分散式追蹤:瞭解請求流程

分散式追蹤的重要性

隨著我們構建更多的微服務架構,我們建立了一個分散式元件網路,這些元件協同工作以實作業務目標。當請求路徑出現問題時,瞭解發生了什麼至關重要,以便快速診斷和修復。

在單體應用中,我們可以使用熟悉的工具進行除錯,如除錯器、執行時分析器和記憶體分析工具。但在分散式系統中,我們需要新的工具來完成相同的任務。分散式追蹤提供了對分散式系統中服務請求流程的洞察。

分散式追蹤的工作原理

分散式追蹤的核心概念是「Span」和「Trace」:

  • Span:表示服務或元件內部的工作單元,包含操作的開始時間、結束時間、操作名稱以及一組標籤和日誌。
  • Trace:由多個相關的 Span 組成,展示了服務之間的因果關係,包括方向、時間和其他除錯訊息。

分散式追蹤的實作包括 Jaeger、Zipkin、Lightstep 和 Instana 等系統。

Istio 可以處理將 Span 傳送到分散式追蹤引擎的工作,因此你不需要特定語言的函式庫和應用程式特定的設定來實作這一點。當請求透過 Istio 服務代理時,如果沒有正在進行的追蹤,則會啟動一個新的追蹤,並將請求的開始和結束時間作為 Span 的一部分捕捉。

Istio 使用以下 Zipkin 追蹤標頭:

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

為了使 Istio 提供的分散式追蹤功能在整個請求呼叫圖中正常工作,每個應用程式都需要將這些標頭傳播到它發出的任何出站呼叫中。這是因為 Istio 無法知道哪些出站呼叫是哪些入站請求的結果。

安裝分散式追蹤系統

讓我們安裝 Jaeger 的簡化佈署版本:

kubectl apply -f istio-1.13.0/samples/addons/jaeger.yaml

這將建立以下服務:

  • jaeger-collector:收集追蹤資料
  • tracing:提供 UI 介面
  • zipkin:相容 Zipkin 格式的介面

設定 Istio 進行分散式追蹤

Istio 可以在多個層級設定分散式追蹤:全域網格、名稱空間或特定工作負載。以下是一個使用 IstioOperator 資源設定 Zipkin 後端的範例:

apiVersion: install.istio.io/v1alpha1
kind: IstioOperator
metadata:
  namespace: istio-system
spec:
  meshConfig:
    defaultConfig:
      tracing:
        zipkin:
          address: zipkin.istio-system:9411

我們也可以透過修改 istio ConfigMap 中的 MeshConfig 物件來設定追蹤:

kubectl get cm istio -n istio-system -o yaml

對於特定工作負載,我們可以使用 Deployment 資源上的註解來設定追蹤引數:

apiVersion: apps/v1
kind: Deployment
...
spec:
  template:
    metadata:
      annotations:
        proxy.istio.io/config: |
          tracing:
            zipkin:
              address: zipkin.istio-system:9411

檢視分散式追蹤資料

當 Span 被傳送到 Jaeger 時,我們可以使用 Jaeger UI 來查詢和檢視 Trace 及其關聯的 Span:

istioctl dashboard jaeger --browser=false

這將在 http://localhost:16686 上提供 Jaeger UI。在介面中,選擇「Services」下拉選單並選擇「istio-ingressgateway」,然後點選「Find Traces」。如果沒有看到任何追蹤,可以嘗試傳送一些流量:

curl -H "Host: webapp.istioinaction.io" http://localhost/api/catalog

追蹤取樣、強制追蹤和自定義標籤

分散式追蹤和 Span 收集可能會對系統效能產生較大影響,因此你可能希望限制收集分散式追蹤的頻率。我們可以透過設定要收集的追蹤百分比來控制追蹤的取樣:

apiVersion: v1
data:
  mesh: |
    accessLogFile: /dev/stdout
    defaultConfig:
      discoveryAddress: istiod.istio-system.svc:15012
      proxyMetadata: {}
      tracing:
        sampling: 10
        zipkin:
          address: zipkin.istio-system:9411

這將全域追蹤取樣率更改為 10%。我們也可以使用 x-envoy-force-trace 標頭強制對特定請求進行追蹤:

curl -H "x-envoy-force-trace: true" \
     -H "Host: webapp.istioinaction.io" http://localhost/api/catalog

此外,我們可以向 Span 增加自定義標籤,這是一種將額外中繼資料附加到追蹤的方法:

apiVersion: apps/v1
kind: Deployment
...
spec:
  template:
    metadata:
      annotations:
        proxy.istio.io/config: |
          tracing:
            sampling: 100
            customTags:
              custom_tag:
                literal:
                  value: "Test Tag"
            zipkin:
              address: zipkin.istio-system:9411

Kiali:服務網格的視覺化工具

Kiali 是一個強大的視覺化儀錶板,可以幫助理解執行時的服務網格。它從 Prometheus 和底層平台提取大量指標,建立網格元件的執行時圖表,提供服務如何相互通訊的視覺概述。

安裝 Kiali

我們將使用 Kiali Operator 來安裝 Kiali:

kubectl create ns kiali-operator
helm install \
  --set cr.create=true \
  --set cr.namespace=istio-system \
  --namespace kiali-operator \
  --repo https://kiali.org/helm-charts \
  --version 1.40.1 \
  kiali-operator \
  kiali-operator

接下來,我們需要建立一個 Kiali 例項,連線到我們佈署的 Prometheus 和 Jaeger 例項:

apiVersion: kiali.io/v1alpha1
kind: Kiali
metadata:
  namespace: istio-system
  name: kiali
spec:
  istio_namespace: "istio-system"
  istio_component_namespaces:
    prometheus: prometheus
  auth:
    strategy: anonymous
  deployment:
    accessible_namespaces:
      - '**'
  external_services:
    prometheus:
      cache_duration: 10
      cache_enabled: true
      cache_expiration: 300
      url: "http://prom-kube-prometheus-stack-prometheus.prometheus:9090"
    tracing:
      enabled: true
      in_cluster_url: "http://tracing.istio-system:16685/jaeger"
      use_grpc: true

建立 Kiali 例項:

kubectl apply -f ch8/kiali.yaml

然後,我們可以將 Kiali 佈署轉發到本地:

kubectl -n istio-system port-forward deploy/kiali 20001

現在可以從 http://localhost:20001 存取 Kiali 控制枱。

使用 Kiali 探索服務網格

Kiali 儀錶板提供了服務網格的概覽,顯示了不同名稱空間中執行的應用程式數量和它們的健康狀態。點選「Graph」標籤可以檢視顯示服務網格中流量流動的有向圖。

為了在 Kiali 儀錶板中獲得有意義的報告,讓我們向應用程式傳送一些請求:

for i in {1..20}; do curl http://localhost/api/catalog -H "Host: webapp.istioinaction.io"; sleep .5s; done

從圖表中,我們可以觀察到以下關於網格的訊息:

  • 流量的遍歷和流動
  • 位元組數、請求數等
  • 多個版本的多個流量流(如金絲雀發布或加權路由)
  • 每秒請求數;多個版本的總流量百分比
  • 根據網路流量的應用程式健康狀態
  • HTTP/TCP 流量
  • 網路故障,可以快速識別

Kiali 中的遙測資料關聯

Kiali 正在逐步發展成為回答所有服務網格可觀察性問題的儀錶板。其中一個功能是關聯追蹤、指標和日誌,這展示了未來的可能性。

要檢視遙測資料之間的關聯,可以點選「Workloads」選單項,然後從列表中選擇一個工作負載檢視中的選單項顯示以下內容:

  • Overview:服務的 Pod、應用於它的 Istio 設定以及上游和下游的圖表
  • Traffic:入站和出站流量的成功率
  • Logs:應用程式日誌、Envoy 存取日誌和相關的 Span
  • Inbound Metrics 和 Outbound Metrics:與 Span 關聯的指標
  • Traces:Jaeger 報告的追蹤
  • Envoy:應用於工作負載的 Envoy 設定,如叢集、監聽器和路由

當這些遙測資料關聯在一起時,除錯過程變得更加簡單,因為你不必在不同的視窗之間切換,也不必檢查所有圖表的時間是否相同。

與實踐建議

在本文中,我們探討瞭如何使用 Grafana、Jaeger 和 Kiali 等工具來視覺化 Istio 服務網格中的指標、追蹤和網路行為。這些工具提供了不同層面的可觀察性:

  • Grafana 提供了對指標的視覺化,包括控制平面和資料平面的效能和健康狀態。
  • Jaeger 提供了分散式追蹤功能,幫助理解請求如何在多個服務之間流動,以及在哪裡可能出現延遲或錯誤。
  • Kiali 提供了服務網格的網路拓撲檢視,顯示服務之間的連線和流量模式。

這些工具共同為我們提供了一個全面的視角,幫助我們理解和診斷微服務架構中的問題。透過結合使用這些工具,我們可以更有效地監控和管理服務網格,確保系統的可靠性和效能。

在實際應用中,我建議根據具體需求選擇合適的工具組合。對於日常監控,Grafana 儀錶板可能是最有用的;而在調查特定問題時,Jaeger 的追蹤功能和 Kiali 的網路檢視可能更為關鍵。最終,這些工具的價值在於它們如何幫助我們理解和改進我們的服務架構。

理解資料平面問題的本質

在網路通訊中,許多事情可能出錯。Istio 的存在正是為了在網路通訊出現問題時提供可視性,並實作自動修復機制,如超時設定、重試和斷路器等,讓應用程式能自動回應網路問題。

服務代理(Service Proxy)為我們提供了網路通訊的詳細檢視,但當代理本身出現異常行為時,我們該如何處理?這正是本文要探討的核心問題。

最常見的錯誤:資料平面設定錯誤

在 Istio 中,最常見的問題是資料平面設定錯誤。Istio 透過自定義資源定義(CRD)如 VirtualService、DestinationRule 等來設定服務代理。這些資源會被轉換為 Envoy 設定並應用到資料平面。當我們應用新資源後,如果資料平面的行為與預期不符,最常見的原因就是設定錯誤。

讓我們看一個具體案例:我們使用 Gateway 資源允許流量透過 Istio 入口閘道,並使用 VirtualService 資源將 20% 的請求路由到 version-v1 子集,將其餘 80% 的請求路由到 version-v2 子集。

但這裡有個問題:如果沒有 DestinationRule 資源,入口閘道就沒有 version-v1 和 version-v2 子集的叢集定義,因此所有請求都會失敗。這是一個值得深入排查的問題。

識別資料平面問題

在日常操作中,我們最常處理的是資料平面問題。直接深入除錯資料平面可能成為習慣,但首先排除控制平面問題至關重要。考慮到控制平面的主要功能是將資料平面同步到最新設定,第一步是驗證控制平面和資料平面是否同步。

如何驗證資料平面是否最新

資料平面設定在設計上是最終一致的。這意味著環境變化(服務、端點、健康狀態)或設定變更不會立即反映在資料平面,直到與控制平面進行適當的同步。

我們可以使用 istioctl proxy-status 命令檢查資料平面是否與最新設定同步:

$ istioctl proxy-status
NAME                                      CDS        LDS        RDS        
catalog.<...>.istioinaction               SYNCED     SYNCED     SYNCED     
catalog.<...>.istioinaction               SYNCED     SYNCED     SYNCED     
catalog.<...>.istioinaction               SYNCED     SYNCED     SYNCED     
istio-egressgateway.<...>.istio-system    SYNCED     SYNCED     NOT SENT   
istio-ingressgateway.<...>.istio-system   SYNCED     SYNCED     SYNCED     

輸出列出了所有工作負載及其每個 xDS API 的同步狀態:

  • SYNCED:Envoy 已確認控制平面傳送的最新設定
  • NOT SENT:控制平面沒有向 Envoy 傳送任何內容(通常是因為控制平面沒有需要傳送的內容)
  • STALE:istiod 控制平面已傳送更新,但未收到確認

使用 Kiali 發現設定錯誤

Kiali 可以發現設定錯誤的服務。開啟 Kiali 儀錶板後,我們可以看到 istioinaction 名稱空間中的警告。點選警告會顯示 catalog-v1-v2 VirtualService 資源的問題,並標記出設定錯誤的部分。

警告訊息 “KIA1107 Subset not found” 表明子集未找到。解決方案是修復指向不存在子集的路由,可能是修正子集名稱的拼寫錯誤,或在 DestinationRule 中定義缺失的子集。

使用 istioctl 發現設定錯誤

istioctl analyzeistioctl describe 是自動排查設定錯誤的有用命令。

使用 istioctl analyze 分析 Istio 設定

$ istioctl analyze -n istioinaction
Error [IST0101] (VirtualService catalog-v1-v2.istioinaction) Referenced host+subset in destinationrule not found: "catalog.istioinaction.svc.cluster.local+version-v1"
Error [IST0101] (VirtualService catalog-v1-v2.istioinaction) Referenced host+subset in destinationrule not found: "catalog.istioinaction.svc.cluster.local+version-v2"

輸出顯示找不到子集,並提供了錯誤程式碼 IST0101,可在 Istio 檔案中查詢更多詳細訊息。

檢測特定工作負載的設定錯誤

describe 子命令用於描述特定工作負載的設定:

$ istioctl x describe pod catalog-68666d4988-vqhmb
Pod: catalog-68666d4988-q6w42
Pod Ports: 3000 (catalog), 15090 (istio-proxy)
--------------------
Service: catalog
   Port: http 80/HTTP targets pod port 3000
Exposed on Ingress Gateway http://13.91.21.16
VirtualService: catalog-v1-v2
   WARNING: No destinations match pod subsets (checked 1 HTTP routes)
   Warning: Route to subset version-v1 but NO DESTINATION RULE defining subsets!
   Warning: Route to subset version-v2 but NO DESTINATION RULE defining subsets!

輸出顯示警告訊息 “Route to subset version-v1 but NO DESTINATION RULE defining subsets”,表示路由設定了不存在的子集。

手動從 Envoy 設定中發現設定錯誤

當自動分析器不足時,我們需要手動檢查整個 Envoy 設定。我們可以使用 Envoy 管理介面或 istioctl 檢索應用於工作負載的 Envoy 設定。

Envoy 管理介面

Envoy 管理介面在每個服務代理的 15000 連線埠上公開 Envoy 設定和其他功能。使用 istioctl,我們可以將其轉發到本地主機:

$ istioctl dashboard envoy deploy/catalog -n istioinaction

使用 istioctl 查詢代理設定

istioctl proxy-config 命令使我們能夠根據 Envoy xDS API 檢索和過濾代理設定:

$ istioctl proxy-config listeners deploy/istio-ingressgateway -n istio-system
ADDRESS     PORT  MATCH  DESTINATION
0.0.0.0     8080  ALL    Route: http.8080
0.0.0.0     15021 ALL    Inline Route: /healthz/ready*
0.0.0.0     15090 ALL    Inline Route: /stats/prometheus*

我們可以看到連線埠 8080 上設定了一個監聽器,流量根據名為 http.8080 的路由進行路由。

查詢 Envoy 路由設定:

$ istioctl pc routes deploy/istio-ingressgateway -n istio-system --name http.8080

輸出顯示主機 catalog.istioinaction.io 的流量被路由到 catalog VirtualService。

查詢 Envoy 叢集設定:

$ istioctl proxy-config clusters deploy/istio-ingressgateway.istio-system \
  --fqdn catalog.istioinaction.svc.cluster.local \
  --port 80 \
  --subset version-v1

結果顯示沒有 version-v1 或 version-v2 子集的叢集。這就是請求失敗的原因:虛擬服務正在路由到不存在的叢集。

我們可以透過建立定義這些子集的目標規則來修復它:

$ kubectl apply -f ch10/catalog-destinationrule-v1-v2.yaml
destinationrule.networking.istio.io/catalog created

再次查詢叢集,我們應該看到新定義的 version-v1 和 version-v2 子集。

排查應用程式問題

服務代理生成的日誌和指標有助於排查許多問題,如發現導致效能瓶頸的服務、識別經常失敗的端點、檢測效能下降等。

設定間歇性慢速工作負載超時

我們可以設定 catalog 工作負載間歇性回傳慢速回應,並設定 catalog-v1-v2 虛擬服務在請求超過半秒時超時。

理解 Envoy 存取日誌

Envoy 存取日誌記錄了 Envoy 代理處理的所有請求,這有助於除錯和排查問題。我們可以設定服務代理使用 JSON 格式,這更易於閲讀。

在 JSON 格式的存取日誌中,我們可以看到:

  • response_flags 值為 UT,表示「上游請求超時」
  • upstream_host 值代表處理請求的工作負載的實際 IP 地址

增加入口閘道的日誌級別

istioctl 提供了讀取和更改 Envoy 代理日誌級別的工具:

$ istioctl proxy-config log deploy/istio-ingressgateway -n istio-system --level http:debug,router:debug,connection:debug,pool:debug

增加日誌級別後,我們可以看到更多關於代理行為的詳細訊息。

使用 ksniff 檢查網路流量

我們可以使用 ksniff(一個 kubectl 外掛)和 Wireshark 檢查受影響 Pod 的網路流量,這提供了平滑的除錯體驗。

使用 Envoy 遙測瞭解應用程式

在 Grafana 中,我們可以檢視失敗請求的比率。在 Istio Service Dashboard 中,我們可以看到客戶端成功率(非 5xx 回應)圖表顯示約 30% 的請求失敗。

當 Grafana 儀錶板不提供足夠的詳細訊息時,我們可以直接查詢 Prometheus。例如,我們可以查詢每個 Pod 的失敗率,這有助於隔離不健康的應用程式。

控制平面效能最佳化

控制平面的主要目標

控制平面的主要功能是將資料平面同步到最新設定。這是一個持續的過程,以維持網格的正確行為,重要的是這個狀態協調過程能及時發生。當控制平面未能做到這一點時,會導致意外後果,因為工作負載設定的狀態已經改變。

一個常見的現象是「幻影工作負載」:服務被設定為將流量路由到已經消失的端點,因此請求失敗。

理解資料平面同步的步驟

將資料平面同步到所需狀態是一個多步驟過程:

  1. 控制平面從 Kubernetes 接收事件
  2. 事件被轉換為 Envoy 設定
  3. 設定被推播到資料平面的服務代理

istiod 透過使用去抖動和限流來保護自己不被過載,這些可以設定以提高效能。

決定效能的因素

影響控制平面效能的因素包括:

  • 變更率 - 更高的變更率需要更多處理來保持資料平面同步
  • 分配的資源 - 如果需求超過分配給 istiod 的資源,工作必須排隊
  • 要更新的工作負載數量 - 更多工作負載需要更多處理能力和網路頻寬
  • 設定大小 - 更大的 Envoy 設定需要更多處理能力和網路頻寬

監控制平面

istiod 公開了測量關鍵效能指標持續時間和頻率的指標,如資源利用率、傳入或傳出流量負載、錯誤率等。

控制平面的四個黃金訊號

  1. 延遲:更新資料平面所需的時間

    • pilot_proxy_convergence_time:測量從代理推播請求進入佇列到分發到工作負載的整個過程持續時間
    • pilot_proxy_queue_time:測量推播請求在佇列中等待的時間
    • pilot_xds_push_time:測量將 Envoy 設定推播到工作負載所需的時間
  2. 飽和度:控制平面有多滿?

    • container_cpu_usage_seconds_total:CPU 利用率
    • process_cpu_seconds_total:CPU 利用率
  3. 流量:控制平面的負載是什麼?

    • 傳入流量指標:pilot_inbound_updatespilot_push_triggerspilot_services
    • 傳出流量指標:pilot_xds_pushespilot_xdsenvoy_cluster_upstream_cx_tx_bytes_total
  4. 錯誤:控制平面的失敗率是多少?

    • pilot_total_xds_rejects:拒絕的設定推播計數
    • pilot_xds_write_timeout:啟動推播時的錯誤和超時總和
    • pilot_xds_push_context_errors:生成 Envoy 設定時的 Istio Pilot 錯誤計數

調整效能

控制平面效能的調整旋鈕包括:

  • 忽略與服務網格無關的事件
  • 批處理事件以減少更新資料平面所需的推播次數
  • 分配額外資源
  • 只向工作負載推播相關更新

使用 Sidecar 減少設定大小和推播次數

預設情況下,Istio 無法確定每個服務需要的存取許可權,因此預設設定每個服務代理瞭解網格中的每個其他工作負載。這會不必要地膨脹代理的設定。

我們可以使用 Sidecar 資源微調 sidecar 代理的入站和出站流量設定:

apiVersion: networking.istio.io/v1beta1
kind: Sidecar
metadata:
  name: default
  namespace: istioinaction
spec:
  workloadSelector:
    labels:
      app: foo
  egress:
    hosts:
      - "./bar.istioinaction.svc.cluster.local"
      - "istio-system/*"

使用網格範圍的 Sidecar 設定義更好的預設值

最簡單的方法是定義一個網格範圍的 sidecar 設定,只允許到 istio-system 名稱空間中服務的出口流量:

apiVersion: networking.istio.io/v1beta1
kind: Sidecar
metadata:
  name: default
  namespace: istio-system
spec:
  egress:
    hosts:
      - "istio-system/*"
      - "prometheus/*"
  outboundTrafficPolicy:
    mode: REGISTRY_ONLY

使用發現選擇器忽略事件

預設情況下,Istio 控制平面監視所有名稱空間中 Pod、服務和其他資源的建立事件!在較大的叢集中,這可能導致控制平面壓力。

我們可以使用名稱空間發現選擇器來精確調整控制平面關心的入站事件:

apiVersion: install.istio.io/v1alpha1
kind: IstioOperator
metadata:
  namespace: istio-system
spec:
  meshConfig:
    discoverySelectors:
      - matchLabels:
          istio-discovery: enabled

事件批處理和推播限流屬性

執行時環境中導致資料平面設定變更的事件通常超出操作員的控制範圍。但我們可以控制延遲更新和批處理這些事件的時間。

定義批處理期的環境變數:

  • PILOT_DEBOUNCE_AFTER:指定去抖動時間,預設為 100 毫秒
  • PILOT_DEBOUNCE_MAX:指定允許事件去抖動的最大時間,預設為 10 秒
  • PILOT_ENABLE_EDS_DEBOUNCE:指定端點更新是否遵循去抖動規則,預設為 true
  • PILOT_PUSH_THROTTLE:指定 istiod 同時處理的推播請求數,預設為 100

為控制平面分配額外資源

在定義 Sidecar 資源、使用發現選擇器和設定批處理後,提高效能的唯一其他選擇是為控制平面分配額外資源。我們可以透過增加更多 istiod 例項進行橫向擴充套件,或透過為每個例項提供更多資源進行縱向擴充套件。

$ istioctl install --set profile=demo \
  --set values.pilot.resources.requests.cpu=2 \
  --set values.pilot.resources.requests.memory=4Gi \
  --set values.pilot.replicaCount=3

效能調整

在調整效能之前,請記住 Istio 的效能非常好。Istio 團隊測試每個新版本的引數:

  • 1,000 個 Kubernetes 服務
  • 2,000 個需要同步的工作負載
  • 整個服務網格中每秒 70,000 個請求

這種負載僅消耗單個 Istio Pilot 例項的一個虛擬核心和 1.5 GB 記憶體。

控制平面效能調整的一些指導原則:

  • 確保這是效能問題
  • 識別效能瓶頸
  • 進行漸進式變更
  • 偏向安全側
  • 考慮使用可突發 VM

服務網格的資料平面疑難排解與控制平面效能最佳化是確保 Istio 服務網格穩定執行的關鍵。透過理解常見問題、使用適當的工具進行診斷,以及實施有效的效能最佳化策略,我們可以維持高效與可靠的服務網格環境。