• Nginx Ingress Contoller 通过 Envoy 代理和 Jaeger 进行分布式追踪(二)


    1、概述

      在《应用程序通过 Envoy 代理和 Jaeger 进行分布式追踪(一)》一文中,我们详细介绍了单个应用程序如何通过 Envoy 和 Jaeger 实现链路追踪的过程。然而,单独追踪单个应用程序的链路在实际场景中往往显得不够有意义。因此,在本文中,我们将进一步扩展链路追踪范围,演示如何将 Nginx Ingress Controller 与之前提到的应用程序一起使用,从而实现更为复杂的分布式链路追踪。

    2、 通过 Nginx Ingress Controller 代理访问 http_request_printer 服务 

    2.1 Nginx Ingress Controller 组件不注入边车,http_request_printer 服务不注入边车

    流量走向: 客户端 ——》Nginx Ingress contorller Pod 中的业务容器 ——》http_request_printer Pod 中的业务容器

    1)创建 Nginx Ingress contorller 资源使其能够管理 tracing 命名空间下的 Ingress 资源,注意 Nginx Ingress contorller 资源对象不要注入边车,步骤略

    2)创建Ingress资源对象,并代理到 http_request_printer 服务

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    [root@master1 ~]# kubectl get ingress -n=tracing http-request-printer -o yaml
    apiVersion: networking.k8s.io/v1
    kind: Ingress
      ......
      name: http-request-printer
      namespace: tracing
    spec:
      rules:
      - host: http-request-printer.xx.xx.xx.xx.nip.io
        http:
          paths:
          - backend:
              service:
                name: http-request-printer
                port:
                  number: 80
            path: /
            pathType: ImplementationSpecific

    3)通过 Ingress 访问 http_request_printer 服务

     

    4)通过容器日志查看请求信息

    可以看到除了自定义 Header 和谷歌浏览器里面的 Postman 插件加了一些 Header 信息外 Nginx Ingress Controller 组件还帮我们加了一些 Header 信息,尤其注意  Nginx Ingress Controller 组件默认会给一个请求生成 X-Request-Id Header 头信息,并且它加的这个 X-Request-Id Header 头信息和  Envoy 产生的 X-Request-Id Header 头信息(xxx-xxx-xxx)格式不一致。

    注意,由于 Nginx Ingress Controller 和 http_request_printer 都没接入 Jaeger,所以此时不管怎么访问在 Jaeger 里面都是查不到链路信息的。

    2.2 Nginx Ingress Controller 组件不注入边车,http_request_printer 服务注入边车

    流量走向: 客户端 ——》Nginx Ingress contorller Pod 中的业务容器 ——》http_request_printer Pod 中的 Envoy 边车容器入站流量劫持 ——》http_request_printer Pod 中的业务容器

    1)重复执行2.1中的步骤1)-3),唯一不同的是 http_request_printer 注入边车

    2)通过容器日志查看请求信息

    查看 http_request_printer 服务对应容器日志,和2.1示例日志不一样在于除了自定义 Header、谷歌浏览器里面的 Postman 插件加了一些 Header 信息、以及 Nginx Ingress Controller 组件加的一些 Header 信息外 http_request_printer 服务注入的边车 Envoy 加了和链路追踪相关的 Header 头信息 。

    查看 Nginx Ingress Controller 组件对应容器日志,可以看到 http_request_printer 服务使用的 X-Request-Id Header 头信息是  Nginx Ingress Controller 组件生成并传递给 http_request_printer 这个上游服务的,注意  Nginx Ingress Controller 组件它加的这个 X-Request-Id Header 头信息和  Envoy 产生的 X-Request-Id Header 头信息(xxx-xxx-xxx)格式不一致。

    3) 查看 http_request_printer 服务上报的链路信息

    整个请求访问链由于只有 http_request_printer 组件注入了边车会向 jaeger-collector 组件上报链路信息,所以整个请求链只有1个 span。

    2.3 Nginx Ingress Controller 组件注入边车,http_request_printer 服务注入边车

    流量走向: 客户端 ——》Nginx Ingress contorller Pod 中的 Envoy 边车容器入站流量劫持 ——》Nginx Ingress contorller Pod 中的业务容器 ——》Nginx Ingress contorller Pod 中的 Envoy 边车容器出站流量劫持 ——》http_request_printer Pod 中的 Envoy 边车容器入站流量劫持 ——》http_request_printer Pod 中的业务容器

    1)重复执行2.1中的步骤1)-3),唯一不同的是 Nginx Ingress Controller 组件和 http_request_printer 服务都会注入边车。

    注意 1, 定义 Nginx Ingress Contorller  svc 资源对象时需要指定 Nginx Ingress Controller 服务应用层协议,因为 istio 需要知道服务提供什么七层协议,从而来为其配置相应协议的 filter chain,千万不要写成 tcp 协议,否则不会上报链路信息,详情参见《 Istio 为服务指定协议 》这篇博文。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    apiVersion: v1
    kind: Service
    metadata:
      name: nginx-ingress-controller
    spec:
      ports:
      - name: http-80  #需要指定服务应用层协议,千万不要写成tcp协议,否则不会上报链路信息
        port: 80
        protocol: TCP
        targetPort: 80
      - name: http-443
        port: 443
        protocol: TCP
        targetPort: 443
      - name: http-10254
        port: 10254
        protocol: TCP
        targetPort: 10254
      selector:
        app: nginx-ingress-controller
      sessionAffinity: None
      type: ClusterIP

    2)通过容器日志查看请求信息

    查看 http_request_printer 服务对应容器日志,和2.2示例日志不一样在于多了 X-B3-Parentspanid(父跨度ID)字段 。

    查看 Nginx Ingress Controller 组件对应容器日志,可以看到 Nginx Ingress Controller 组件的 X-Request-Id Header头信息是 Nginx Ingress Controller 组件边车生成的,然后 Nginx Ingress Controller 组件将 X-Request-Id Header 头信息传递给 http_request_printer 这个上游服务的。

    3) 查看 http_request_printer 服务上报的链路信息

    整个请求Nginx Ingress contorller Pod 中的 Envoy 边车容器入站流量劫持、Nginx Ingress contorller Pod 中的 Envoy 边车容器出站流量劫持、http_request_printer Pod 中的 Envoy 边车容器入站流量劫持都会向 jaeger-collector 组件上报链路信息,所以整个请求链有3个 span。

    下面简单分析下这三个span。

    Nginx Ingress contorller Pod 中的 Envoy 边车容器入站流量劫持 span:

    Nginx Ingress contorller Pod 中的 Envoy 边车容器出站流量劫持 span:

    http_request_printer Pod 中的 Envoy 边车容器入站流量劫持 span: 

    注意 1:通过2.2和2.3章节我们知道,即使 Nginx Ingress Controller 组件不注入边车,默认也会给一个请求生成 X-Request-Id Header 头信息,并且它加的这个 X-Request-Id Header 头信息是非标准形式形式的。而 Nginx Ingress Controller 组件注入边车的话,流量先经过 Istio-Proxy 容器,自动加上标准形式的 X-Request-Id Header 头信息,再经过 Nginx 容器时,因为请求头中已经有了 X-Request-Id Header 头信息,因此就不会使用 Nginx 生成的非标准 requestID。

    注意 2:为什么 Ingress 注入 Sidecar 跟不注入 Sidecar,X-Request-Id 形式不一样呢?

    当请求访问 Ingress 时,会先经过一个 Sidecar Istio-Proxy Container 后,产生一个标准 ID(xxx-xxx-xxx)。请求转发至 Ingress Controller 的 Container,此时请求已经有了 RequestID,因此不会被 Nginx 覆盖。Nginx 配置文件如下:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    # Reverse proxies can detect if a client provides a X-Request-ID header, and pass it on to the backend server.
    # If no such header is provided, it can provide a random value.
    map $http_x_request_id $req_id {  # http_x_request_id 是请求进来的requestId
      default   $http_x_request_id; # 默认使用请求头中的requestID,即将http_x_request_id的值赋给req_id
     
      ""        $request_id; # http_x_request_id是空,那么使用 request_id,request_id是Nginx默认的自带的变量,一个16位比特的随机数,用32位的16进制数表示。
     
    ...
    }
    proxy_set_header X-Request-ID           $req_id;

    注意 3:要使用 Nginx Ingress Controller 作为 Istio 网格入口的话需要在 ingress 资源上添加如下注解,详情参见《利用Nginx Ingress Controller作为 Istio 网格入口》这篇博文。

    1
    2
    nginx.ingress.kubernetes.io/service-upstream: "true"
    nginx.ingress.kubernetes.io/upstream-vhost: http-request-printer.tracing.svc.cluster.local

    不加这两个注解的话,流量从 nginx ingress controller envoy 出站的时候直接通过 PodIp:Pod端口 访问上游服务,说明没有匹配 envoy 配置规则,这时候在链路追踪UI页面查询upstream_cluster 和 upstream_cluster.name 值的话都是PassthroughCluster。

     3、总结

    通过《应用程序通过 Envoy 代理和 Jaeger 进行分布式追踪(一)》这篇博文我们知道,Envoy 原生支持 Jaeger,追踪所需 x-b3 开头的 Header 和 x-request-id 在不同的服务之间由业务逻辑进行传递,并由 Envoy 上报给 Jaeger,最终 Jaeger 生成完整的追踪信息。为了将各种追踪 span 整合在一起以获得完整的追踪图,应用程序必须在传入和传出请求之间传播追踪上下文信息。特别是,Istio 依赖于应用程序传播 b3 追踪 Header 以及由 Envoy 生成的请求 ID,即应用程序服务请求时需携带这些 Header。如果请求中没有 B3 HTTP Header,Istio Sidecar 代理(Envoy) 会自动生成初始化的 Headers。

    在本文我们进一步扩展链路追踪范围,演示了如何将 Nginx Ingress Controller 与之前提到的应用程序一起使用,从而实现更为复杂的分布式链路追踪。应用程序通过 Envoy 代理实现分布式链路追踪一定要注意以下几点:

    1. Envoy 调用时会帮助应用程序生成 trace 信息,而调用链中关键的就是 trace_id 和 span,如果应用不传递的话,每次 Envoy 都会生成新的 trace_id,导致查看到的结果就是一个调用链中只有一个 span,所以应用程序必须在传入和传出请求之间传播追踪上下文信息,否则整个链路串不起来。
    2. 定义 svc 资源对象时需要指定服务应用层协议,因为 istio 需要知道服务提供什么七层协议,从而来为其配置相应协议的 filter chain,千万不要写成 tcp 协议,否则不会上报链路信息。( 服务指定协议配置详情请参见《 Istio 为服务指定协议 》这篇博文)
    3. 要使用 Nginx Ingress Controller 作为 Istio 网格入口的话需要在 ingress 资源上添加配置nginx.ingress.kubernetes.io/service-upstream、nginx.ingress.kubernetes.io/upstream-vhost注解。 (Nginx Ingress Controller 作为 Istio 网格入口配置详情参见《利用Nginx Ingress Controller作为 Istio 网格入口》这篇博文)

    参考:Jaeger讲解

  • 相关阅读:
    Linux - 进程的优先级 和 如何使用优先级调度进程
    .net core 大文件上传
    高效数据管理:Java助力实现Excel数据验证
    alsa pcm接口之阻塞和非阻塞打开和异步通知模式
    Java架构师系统架构设计资源估算
    spring 用户通过交互界面登录成功事件源码分析
    从零开始学习 Java:简单易懂的入门指南之线程池(三十六)
    R语言使用oneway.test函数执行单因素方差分析(One-Way ANOVA)、使用数据集的子集数据进行单因素方差分析(subset函数筛选数据子集)
    微信小程序-组件
    历届蓝桥杯青少年编程比赛 计算思维题真题解析【已更新5套 持续更新中】
  • 原文地址:https://www.cnblogs.com/zhangmingcheng/p/17645986.html