分布式调用链跟踪

本章介绍如何使用ZipkinJaeger收集启用了Istio的应用程序的调用链信息。

完成本章后,你可以理解有关应用程序的所有假设以及如何使其参与跟踪,无论您使用何种语言/框架/平台构建应用程序。

BookInfo示例用来作为此任务的示例应用程序。

环境准备

  • 参照安装指南的说明安装Istio。

    如果您在安装过程中未启动Zipkin或Jaeger插件,则可以运行以下命令启动:

    启动Zipkin:

    1. kubectl apply -f install/kubernetes/addons/zipkin.yaml

    启动Jaeger:

    1. kubectl apply -n istio-system -f https://raw.githubusercontent.com/jaegertracing/jaeger-kubernetes/master/all-in-one/jaeger-all-in-one-template.yml
  • 部署BookInfo示例中的应用程序。

访问仪表盘

Zipkin

通过端口转发设置访问Zipkin dashboard URL:

  1. kubectl port-forward -n istio-system $(kubectl get pod -n istio-system -l app=zipkin -o jsonpath='{.items[0].metadata.name}') 9411:9411 &

然后使用浏览器访问http://localhost:9411

Jaeger

通过端口转发设置访问Jaeger dashboard URL:

  1. kubectl port-forward -n istio-system $(kubectl get pod -n istio-system -l app=jaeger -o jsonpath='{.items[0].metadata.name}') 16686:16686 &

然后使用浏览器访问http://localhost:16686

使用BookInfo示例生成调用链跟踪

BookInfo的应用程序启动和运行后,通过访问http://$GATEWAY_URL/productpage一次或多次生成调用链信息。

如果你查看仪表板,会看到类似以下的内容:

Zipkin Dashboard

Jaeger Dashboard

如果你点击调用链堆栈中(最近的)最近的一条,您应该看到刷新/ productpage后最新的详细信息。页面看起来像这样:

Zipkin Trace View

Jaeger Trace View

像您看到的,调用链由spans组成,其中每个span对应于使用/ productpage去调用BookInfo服务。 因为调用链堆栈是由Istio Sidecar(Envoy代理)包装实际的服务完成的,所以每个服务具有相同的标签istio-proxy。右侧的目的地标签每一行标识该服务的调用耗时。

第一行表示productpage服务被外部调用。192.168.64.3:32000标签是外部请求的主机信息(即$GATEWAY_URL)。 从调用堆栈中可以看到,请求总共耗时大约290毫秒完成。 在执行过程中,productpage调用details服务,耗时约24ms,然后调用review服务。review服务耗时约243毫秒,其中包括一个15毫秒的ratings服务。

理解下发生了什么

尽管Istio代理能够自动发送spans,但他们需要一些标识来将整个调用链关系联系起来。应用程序需要传入合适的HTTP header信息,便于代理发送span信息到Zipkin或Jaeger时,span可以准确地把每次调用关联起来。

为此,应用程序需要从传入的请求中收集如下的header信息并将其传入到每个传出请求:

  • x-request-id
  • x-b3-traceid
  • x-b3-spanid
  • x-b3-parentspanid
  • x-b3-sampled
  • x-b3-flags
  • x-ot-span-context

如果您细看示例的服务,可以看到productpage应用(Python应用)从HTTP请求中提取所需的header信息:

  1. def getForwardHeaders(request):
  2. headers = {}
  3. user_cookie = request.cookies.get("user")
  4. if user_cookie:
  5. headers['Cookie'] = 'user=' + user_cookie
  6. incoming_headers = [ 'x-request-id',
  7. 'x-b3-traceid',
  8. 'x-b3-spanid',
  9. 'x-b3-parentspanid',
  10. 'x-b3-sampled',
  11. 'x-b3-flags',
  12. 'x-ot-span-context'
  13. ]
  14. for ihdr in incoming_headers:
  15. val = request.headers.get(ihdr)
  16. if val is not None:
  17. headers[ihdr] = val
  18. #print "incoming: "+ihdr+":"+val
  19. return headers

示例中reviews应用(Java应用)也做了类似的事情:

  1. @GET
  2. @Path("/reviews")
  3. public Response bookReviews(@CookieParam("user") Cookie user,
  4. @HeaderParam("x-request-id") String xreq,
  5. @HeaderParam("x-b3-traceid") String xtraceid,
  6. @HeaderParam("x-b3-spanid") String xspanid,
  7. @HeaderParam("x-b3-parentspanid") String xparentspanid,
  8. @HeaderParam("x-b3-sampled") String xsampled,
  9. @HeaderParam("x-b3-flags") String xflags,
  10. @HeaderParam("x-ot-span-context") String xotspan) {
  11. String r1 = "";
  12. String r2 = "";
  13. if(ratings_enabled){
  14. JsonObject ratings = getRatings(user, xreq, xtraceid, xspanid, xparentspanid, xsampled, xflags, xotspan);

在应用程序中调用其他服务时,请确保包含这些header信息。

清除

  • 删除调用链跟踪的配置:

    如果使用Zipkin,请运行以下命令进行清理

    1. kubectl delete -f install/kubernetes/addons/zipkin.yaml

    如果使用Jaeger,请运行以下命令进行清理:

    1. kubectl delete -f https://raw.githubusercontent.com/jaegertracing/jaeger-kubernetes/master/all-in-one/jaeger-all-in-one-template.yml
  • 如果您不打算继续后面的章节,请参阅BookInfo cleanup说明关闭应用程序。

进一步阅读