在《應用程式通過 Envoy 代理和 Jaeger 進行分散式追蹤(一)》一文中,我們詳細介紹了單個應用程式如何通過 Envoy 和 Jaeger 實現鏈路追蹤的過程。然而,單獨追蹤單個應用程式的鏈路在實際場景中往往顯得不夠有意義。因此,在本文中,我們將進一步擴充套件鏈路追蹤範圍,演示如何將 Nginx Ingress Controller 與之前提到的應用程式一起使用,從而實現更為複雜的分散式鏈路追蹤。
流量走向: 使用者端 ——》Nginx Ingress contorller Pod 中的業務容器 ——》http_request_printer Pod 中的業務容器
1)建立 Nginx Ingress contorller 資源使其能夠管理 tracing 名稱空間下的 Ingress 資源,注意 Nginx Ingress contorller 資源物件不要注入邊車,步驟略
2)建立Ingress資源物件,並代理到 http_request_printer 服務
[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 裡面都是查不到鏈路資訊的。
流量走向: 使用者端 ——》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。
流量走向: 使用者端 ——》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 為服務指定協定 》這篇博文。
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 組態檔如下:
# 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 網格入口》這篇博文。
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。
通過《應用程式通過 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 代理實現分散式鏈路追蹤一定要注意以下幾點:
參考:Jaeger講解