Dapr 可觀測性之分佈式追蹤

語言: CN / TW / HK

在構建應用程序時,瞭解系統的行為方式是運維它的重要部分——這包括能夠觀察應用程序的內部調用、衡量其性能並在問題發生時能夠立即找到問題。這對任何系統來説都是具有挑戰性的,對於由多個微服務組成的分佈式系統更是如此,其中由多個調用組成的流可能在一個微服務中開始,但在另一個微服務中繼續調用。可觀測性在生產環境中至關重要,在開發過程中對於瞭解瓶頸、提高性能和跨微服務執行基本調試也很有用。

雖然可以從底層基礎架構中收集有關應用程序的一些數據(例如內存消耗、CPU 使用情況),但必須從 應用程序感知 層收集其他有意義的信息——該層可以顯示如何執行一系列重要的調用跨微服務。這通常意味着開發人員必須為此添加一些代碼來檢測應用程序。通常,檢測代碼只是將收集到的數據(例如追蹤和指標)發送到外部監控工具或服務,以幫助存儲、可視化和分析這些信息。

由於這部分代碼並不是應用程序的核心邏輯,所以這自然成為了開發人員的另一個負擔,有時需要了解監控工具的 API,使用額外的 SDK 等。這種工具也可能會增加應用程序的可移植性挑戰。應用程序可能需要不同的工具,具體取決於應用程序的部署環境。例如,不同的雲提供商提供不同的監控解決方案,本地部署可能需要本地解決方案。

用於獲得可觀測性的系統信息被稱為 telemetry(遙測) ,它可以分為四大類。

  1. Distributed tracing(分佈式追蹤)提供了對參與分佈式業務通信的服務之間流量的洞察力。

  2. Metrics(指標)提供了對服務性能及其資源消耗的洞察力。

  3. Logging(日誌)提供了對代碼如何執行以及是否發生錯誤的洞察力。

  4. Health(健康)端點提供了對服務可用性的洞察力。

Dapr 可觀測性構件將可觀測性與應用解耦,它自動捕捉由構成 Dapr 控制平面的 Dapr sidecar 和 Dapr 系統服務產生的流量。該模塊將跨越多個服務的單個操作的流量進行關聯。它還暴露了性能指標、資源利用率和系統的健康狀況。遙測數據以開放標準的格式發佈,使信息能夠被輸入你選擇的監控後端。在那裏,這些信息可以被可視化、查詢和分析。

由於 Dapr 進行了抽象,所以應用程序不知道可觀測性是如何實現的。不需要開發者關心如何去實現這部分與核心業務邏輯無關的代碼,Dapr 允許開發者專注於構建業務邏輯,而不是觀察能力的建設。觀察力是在 Dapr 系統層面上配置的,並且在不同的服務中是一致的,即使是由不同的團隊創建,並使用不同的技術棧構建。

如何工作

Dapr 的 sidecar 架構實現了內置的可觀測性功能,當服務進行通信時,Dapr sidecars 攔截流量並提取追蹤、指標和日誌信息,遙測數據以開放標準格式進行發佈,默認 Dapr 支持 OpenTelemetry 和 Zipkin。

Dapr 提供 collectors 收集器,可以將遙測數據發佈到不同的後端監控工具,這些工具將 Dapr 遙測數據呈現出來,用於分析和查詢。圖 10-1 顯示了 Dapr 的可觀察性架構。

dapr observability 架構
  • 服務 A 調用服務 B 的一個操作,該調用從服務 A 的 Dapr sidecar 被路由到服務 B 的 sidecar。

  • 當服務 B 完成操作時,響應會通過 Dapr sidecar 被送回服務 A。它們收集併發布每個請求和響應的所有可用遙測數據。

  • 配置的收集器攝取遙測數據並將其發送到監控後端。

不過需要注意的是添加可觀測性的支持不同於配置其他 Dapr 構建塊,比如前面我們介紹的發佈訂閲或者狀態管理這些組件,我們不需要引用構建塊了,而是添加收集器和監控後端,上圖顯示我們可以配置多個與不同監控後端集成的收集器。

下面我們來分別對可觀測性的幾個遙測類型進行説明。

分佈式追蹤

分佈式追蹤提供了對分佈式應用中跨服務流動流量的洞察力。交換的請求和響應信息的日誌是排除問題的重要信息來源,比較困難的是把屬於同一業務事務的消息整合起來。

Dapr 使用 W3C Trace Context 這個統一的標準來關聯相關信息,它將相同的上下文信息注入到一次完整的請求和響應中。

W3C Trace Context 示例

上圖顯示了一個 W3C Trace Context 標準的示例:

  • 服務 A 調用服務 B 上的操作。當服務 A 開始調用時,Dapr 創建一個唯一的 trace context 並將其注入到請求中。
  • 服務 B 接收請求並調用服務 C 上的操作。Dapr 檢測到傳入請求包含 trace context 並通過將其注入到服務 C 的傳出請求中來傳播它。
  • 服務 C 接收請求並處理它。Dapr 檢測到傳入的請求包含 trace context ,並通過將其注入到傳出響應中返回給服務 B 來傳播它。
  • 服務 B 接收響應並處理它。然後它創建一個新的響應並通過將其注入到傳出響應中來傳播 trace context 並返回到服務 A。

一組屬於一起的請求和響應就稱為 trace(追蹤) ,如下圖所示:

Traces 和 spans

注意查看上圖 trace 是如何代表一個發生在許多服務中的獨特應用事務的。一個 trace 是一系列 spans 集合組成的,每個 span 代表一個單一的操作或在 trace 中完成的工作單位。 Spans 是在實現單一事務的服務之間發送的請求和響應。

接下來我們來討論如何通過將遙測數據發佈到對應的監控後端。

使用 Zipkin

Zipkin 是一個開源的分佈式追蹤系統,它可以攝取和可視化遙測數據。Dapr 為 Zipkin 提供了默認支持。

當 Dapr 在自託管模式下初始化 (dapr init) 時,多個容器會部署到本地 Docker,可以運行 docker ps 命令查看本地運行的所有容器,確保 Zipkin 容器已啟動並正在運行,並記下它正在運行的端口(默認為 9411)。

zipkin 容器

如果沒有 Zipkin 容器服務運行,可以使用下面的命令來進行啟動:

➜  docker run --name dapr_zipkin -d -p 9411:9411 openzipkin/zipkin

此時其實我們即可在瀏覽器中通過 http://localhost:9411 訪問到 Zipkin 的 Web 頁面,在 Dashboard 中我們可以搜索查看已通過 Dapr 可觀測性構建塊記錄的遙測數據。

Zipkin Dashboard

在搜索結果中點擊 SHOW 按鈕即可查看詳細的遙測數據。

Zipkin Show

我們可以發現在本地自拓管模式下面並沒有做任何的關於 Zipkin 的配置,當有服務請求經過了 Dapr sidecar 過後,Zipkin 中就有了對應的遙測數據了,這是因為自拓管模式下面默認就啟用了 Zipkin 來收集遙測數據。相關的配置位於 $HOME/.dapr/config.yaml ,內容如下所示:

apiVersion: dapr.io/v1alpha1
kind: Configuration
metadata:
name: daprConfig
spec:
tracing:
samplingRate: "1"
zipkin:
endpointAddress: http://localhost:9411/api/v2/spans

所以如果是在 Kubernetes 模式下面要啟用 Zipkin 作為 tracing 後端,則需要單獨創建 Configuration 對象才行。

首先,必須使用 Dapr 配置文件為 Dapr 運行時啟用 tracing。下面是一個名為 dapr-config.yaml 的配置文件示例,它啟用了 tracing:

# dapr-config.yaml
apiVersion: dapr.io/v1alpha1
kind: Configuration
metadata:
name: appconfig
spec:
tracing:
samplingRate: "1"
zipkin:
endpointAddress: "http://zipkin.default.svc.cluster.local:9411/api/v2/spans"

可以看到該配置文件和本地的配置幾乎一致,唯一不同的就是 zipkin.endpointAddress 的地址不同。其中的 samplingRate 屬性指定了用於發佈追蹤的間隔時間,這個值必須在 0(禁止追蹤)和 1(每條追蹤都被髮布)之間。例如,值為 0.5 時,則表示每隔一段時間就發佈一次 trace,這樣就大大減少了發佈流量。我們這裏的 zipkin.endpointAddress 指向 Kubernetes 集羣中運行的 Zipkin 服務器,Zipkin 的默認端口是 9411。直接應用該資源對象即可:

➜  kubectl apply -f dapr-config.yaml

當然還需要手動部署 Zipkin 服務,對應的資源清單文件如下所示:

# zipkin.yaml
kind: Deployment
apiVersion: apps/v1
metadata:
name: zipkin
namespace: default
labels:
service: zipkin
spec:
selector:
matchLabels:
service: zipkin
template:
metadata:
labels:
service: zipkin
spec:
containers:
- name: zipkin
image: openzipkin/zipkin-slim
imagePullPolicy: IfNotPresent
ports:
- name: http
containerPort: 9411
protocol: TCP
---
kind: Service
apiVersion: v1
metadata:
name: zipkin
namespace: default
labels:
service: zipkin
spec:
type: NodePort
ports:
- port: 9411
targetPort: 9411
nodePort: 32411
protocol: TCP
name: zipkin
selector:
service: zipkin

這裏我們使用的 openzipkin/zipkin-slim 容器鏡像,Zipkin Service 暴露了 Zipkin Web 前端,可以通過 32411 端口來進行訪問。同樣直接應用上面的資源清單:

➜  kubectl apply -f zipkin.yaml

部署完成後可以查看 Pod 狀態瞭解應用是否部署成功:

➜  kubectl get pods -l service=zipkin
NAME READY STATUS RESTARTS AGE
zipkin-f5c696fb7-94mqz 1/1 Running 0 3m9s
➜ kubectl get svc -l service=zipkin
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
zipkin NodePort 10.102.75.84 <none> 9411:32411/TCP 30s

部署成功後可以通過 http:<node-ip>:32411 來訪問 Zipkin Web 頁面。

Zipkin Web

接下來我們就可以發佈遙測數據了,需要注意的是我們需要在每個 Dapr sidecar 在啟動時發出遙測數據,為此需要為應用添加一個 dapr.io/config 註解。

同樣這裏我們還是以 quickstarts 示例進行説明,定位到 tutorials/distributed-calculator 目錄下面:

➜  git clone [-b <dapr_version_tag>] http://github.com/dapr/quickstarts.git
cd tutorials/distributed-calculator

該示例是一個分佈式計算器,展示了 Dapr 的方法調用和狀態持久化功能,其中每個操作都由用不同語言/框架編寫的服務提供支持:

  • Addition: Go mux application

  • Multiplication: Python flask application

  • Division: Node Express application

  • Subtraction: .NET Core application

前端應用由一個服務端和一個用 React 編寫的客户端組成,源碼地址:React calculator 。

分佈式計算器

上圖為該示例應用各個組件的組成和服務架構。

我們可以隨便查看一個微服務的部署清單,位於 deploy/ 目錄下面,比如 go-adder.yaml

# go-adder.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: addapp
labels:
app: add
spec:
replicas: 1
selector:
matchLabels:
app: add
template:
metadata:
labels:
app: add
annotations:
dapr.io/enabled: "true"
dapr.io/app-id: "addapp"
dapr.io/app-port: "6000"
dapr.io/config: "appconfig"
spec:
containers:
- name: add
image: ghcr.io/dapr/samples/distributed-calculator-go:latest
env:
- name: APP_PORT
value: "6000"
ports:
- containerPort: 6000
imagePullPolicy: Always

上面的資源清單中我們通過 dapr.io/config 註解指定了使用 appconfig 這個配置文件,該配置文件中使用了 Zipkin 服務來獲取遙測數據,其他微服務中也使用了該註解,所以當應用部署完成後,Zipkin 就能獲取到相應的遙測數據。

需要注意 dapr.io/config 後面指定的 Configuration 對象需要和當前應用位於同一個命名空間之下。

直接部署該示例應用:

➜  kubectl apply -f deploy/

部署完成後我們可以通過 dapr configurations 命令查看當前集羣中的所有配置信息:

➜  dapr configurations -k -A
NAMESPACE NAME TRACING-ENABLED METRICS-ENABLED AGE CREATED
default appconfig true true 1m 2022-09-20 17:01.21

同樣在 Dashboard 中也可以看到該配置信息:

dapr configuration

應用部署完成後查看 Pod 的狀態:

➜  kubectl get pods
NAME READY STATUS RESTARTS AGE
addapp-84c9764fdb-72mxf 2/2 Running 0 74m
calculator-front-end-59cbb6658c-rbctf 2/2 Running 0 74m
divideapp-8476b7fbb6-kr8dr 2/2 Running 0 74m
multiplyapp-7c45fbbf99-hrmff 2/2 Running 0 74m
subtractapp-58645db87-25tg9 2/2 Running 0 62m
➜ kubectl get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
addapp-dapr ClusterIP None <none> 80/TCP,50001/TCP,50002/TCP,9090/TCP 8m29s
calculator-front-end LoadBalancer 10.110.177.32 192.168.0.54 80:31701/TCP 8m29s
calculator-front-end-dapr ClusterIP None <none> 80/TCP,50001/TCP,50002/TCP,9090/TCP 8m29s
divideapp-dapr ClusterIP None <none> 80/TCP,50001/TCP,50002/TCP,9090/TCP 8m29s
multiplyapp-dapr ClusterIP None <none> 80/TCP,50001/TCP,50002/TCP,9090/TCP 8m29s
subtractapp-dapr ClusterIP None <none> 80/TCP,50001/TCP,50002/TCP,9090/TCP 8m29s
zipkin NodePort 10.108.46.223 <none> 9411:32411/TCP 16m

部署完成後我們可以通過 calculator-front-end 這個 LoadBalancer 類型的 Service 去訪問計算器的前端應用,我們這裏分配的 EXTERNAL-IP 地址為 192.168.0.54

計算器

打開瀏覽器的控制枱窗口(使用 F12 鍵) ,查看在使用計算器時生成的日誌。請注意,每次單擊按鈕時,都會看到表示狀態持久性的日誌:

Rehydrating State:
{total: "21", next: "2", operation: "x"}

還要注意,每次輸入一個完整的方程式(例如 126 ÷ 3 = ) ,日誌都會指示對服務的調用:

Calling divide service

客户端代碼調用 Express 服務器,後者將調用通過 Dapr 路由到後端服務。在這種情況下,在 nodejs 應用程序上調用 divide 端點。

當我們操作應用的時候,後面就有網絡請求產生,也就有了微服務之間的調用,所以此時就會參數對應的 trace 遙測數據,我們可以前往 Zipkin 查詢下數據。

Zipkin Dashboard

點擊 SHOW 就可以看到詳細的遙測數據。

Zipkin SHOW

同樣的除了 Zipkin,其他監視後端軟件也可引入 Zipkin 格式的遙測,比如 Jaeger,Jaeger 是由 Uber 創建的開源追蹤系統。它用於跟蹤分佈式服務之間的事務,並對複雜的微服務環境進行故障排除,又比如 New Relic 是一個全堆棧可觀測性平台,它可以鏈接來自分散應用程序的相關數據,以提供系統的完整圖片 要試用它們,只需要在 Dapr 配置文件中指定一個指向 Jaeger 或 New Relic 服務器的 endpointAddress 即可。下面是配置 Dapr 以將遙測發送到 Jaeger 服務器的配置文件示例。Jaeger 的 URL 與 Zipkin 的 URL 相同。唯一的區別是服務器運行的端口號:

apiVersion: dapr.io/v1alpha1
kind: Configuration
metadata:
name: dapr-config
namespace: default
spec:
tracing:
samplingRate: "1"
zipkin:
endpointAddress: "http://localhost:9415/api/v2/spans"

同樣如果要使用 New Relic,則需要將 endpointAddress 指定為 New Relic API 的地址。

  點擊上方卡片關注K8s技術圈,掌握前沿雲原生技術