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技術圈,掌握前沿雲原生技術