本文介紹Baggage作為鏈路透傳的要求標頭時,如何通過虛擬服務以及ASMHeaderPropagation等流量規則資源的配置實現寬鬆模式的流量泳道和流量降級。
前提條件
已建立ASM企業版或旗艦版執行個體,且執行個體版本為1.21.6.54及以上。具體操作,請參見建立ASM執行個體或升級ASM執行個體。
已添加Kubernetes叢集到ASM執行個體。具體操作,請參見添加叢集到ASM執行個體。
已建立名稱為ingressgateway的ASM網關。具體操作,請參見建立入口網關。
功能介紹
Baggage是OpenTelemetry推出的一種標準化機制,旨在實現分布式系統調用鏈路中跨進程傳遞上下文資訊。它通過在HTTP頭部增加名為“Baggage”的欄位實現,欄位值為索引值對格式,可傳遞租戶ID、追蹤ID、安全憑證等上下文資料,支援鏈路追蹤、日誌關聯等功能而無需修改代碼。例如:
baggage: userId=alice,serverNode=DF%2028,isProduction=false
基於透傳的Baggage上下文資訊,服務網格ASM可以利用ASMHeaderPropagation資源協助您在服務調用鏈路上透傳任意要求標頭、並基於該要求標頭實現寬鬆模式的流量泳道。有關寬鬆模式的流量泳道,請參見流量泳道概述。
步驟一:佈建服務透傳Baggage上下文
本節主要展示如何通過OpenTelemetry Operator自動插裝的方法,為Kubernetes叢集中的服務添加Baggage透傳能力。
部署OpenTelemetry Operator。
通過kubectl串連到ASM執行個體添加的Kubernetes叢集。執行以下命令,建立opentelemetry-operator-system命名空間。
kubectl create namespace opentelemetry-operator-system
執行以下命令,使用Helm在opentelemetry-operator-system命名空間下安裝OpenTelemetry Operator。(關於Helm安裝步驟,請參見安裝Helm)
helm repo add open-telemetry https://open-telemetry.github.io/opentelemetry-helm-charts helm install \ --namespace=opentelemetry-operator-system \ --version=0.46.0 \ --set admissionWebhooks.certManager.enabled=false \ --set admissionWebhooks.certManager.autoGenerateCert=true \ --set manager.image.repository="registry-cn-hangzhou.ack.aliyuncs.com/acs/opentelemetry-operator" \ --set manager.image.tag="0.92.1" \ --set kubeRBACProxy.image.repository="registry-cn-hangzhou.ack.aliyuncs.com/acs/kube-rbac-proxy" \ --set kubeRBACProxy.image.tag="v0.13.1" \ --set manager.collectorImage.repository="registry-cn-hangzhou.ack.aliyuncs.com/acs/opentelemetry-collector" \ --set manager.collectorImage.tag="0.97.0" \ --set manager.opampBridgeImage.repository="registry-cn-hangzhou.ack.aliyuncs.com/acs/operator-opamp-bridge" \ --set manager.opampBridgeImage.tag="0.97.0" \ --set manager.targetAllocatorImage.repository="registry-cn-hangzhou.ack.aliyuncs.com/acs/target-allocator" \ --set manager.targetAllocatorImage.tag="0.97.0" \ --set manager.autoInstrumentationImage.java.repository="registry-cn-hangzhou.ack.aliyuncs.com/acs/autoinstrumentation-java" \ --set manager.autoInstrumentationImage.java.tag="1.32.1" \ --set manager.autoInstrumentationImage.nodejs.repository="registry-cn-hangzhou.ack.aliyuncs.com/acs/autoinstrumentation-nodejs" \ --set manager.autoInstrumentationImage.nodejs.tag="0.49.1" \ --set manager.autoInstrumentationImage.python.repository="registry-cn-hangzhou.ack.aliyuncs.com/acs/autoinstrumentation-python" \ --set manager.autoInstrumentationImage.python.tag="0.44b0" \ --set manager.autoInstrumentationImage.dotnet.repository="registry-cn-hangzhou.ack.aliyuncs.com/acs/autoinstrumentation-dotnet" \ --set manager.autoInstrumentationImage.dotnet.tag="1.2.0" \ --set manager.autoInstrumentationImage.go.repository="registry-cn-hangzhou.ack.aliyuncs.com/acs/opentelemetry-go-instrumentation" \ --set manager.autoInstrumentationImage.go.tag="v0.10.1.alpha-2-aliyun" \ opentelemetry-operator open-telemetry/opentelemetry-operator
執行以下命令,檢查opentelemetry-operator是否正常運行。
kubectl get pod -n opentelemetry-operator-system
預期輸出:
NAME READY STATUS RESTARTS AGE opentelemetry-operator-854fb558b5-pvllj 2/2 Running 0 1m
配置自動插裝(auto-instrumentation)。
使用以下內容,建立instrumentation.yaml檔案。
apiVersion: opentelemetry.io/v1alpha1 kind: Instrumentation metadata: name: demo-instrumentation spec: propagators: - baggage sampler: type: parentbased_traceidratio argument: "1"
執行以下命令,在default命名空間下聲明自動插裝。
kubectl apply -f instrumentation.yaml
說明對於OpenTelemetry架構來說,其最佳實務還包括部署OpenTelemetry Collector以收集可觀測資料。由於本文主要示範OpenTelemetry自動插裝實現的Baggage鏈路透傳,因此沒有包含部署OpenTelemetry Collector的步驟。有關服務網格ASM如何通過OpenTelemetry上報鏈路追蹤資料,請參考見將鏈路追蹤資料擷取到阿里雲可觀測鏈路OpenTelemetry版。
步驟二:部署樣本服務
為default命名空間啟用Sidecar網格代理自動注入。具體操作,請參見管理全域命名空間。
說明關於自動注入的更多資訊,請參見配置Sidecar注入策略。
使用以下內容,建立mock.yaml檔案。
對於每個執行個體服務Pod,都加入了
instrumentation.opentelemetry.io/inject-java: "true"
和instrumentation.opentelemetry.io/container-names: "default"
兩個註解,以聲明該執行個體服務使用Java語言實現,並要求OpenTelemetry Operator對名稱為default
的容器進行自動插裝。執行以下命令,部署執行個體服務。
kubectl apply -f mock.yaml
基於OpenTelemetry自動插裝機制,部署的服務Pod將自動具有在調用鏈路中傳遞Baggage的能力。
步驟三:建立流量規則實現寬鬆模式流量泳道
建立DestinationRule流量規則。
使用以下內容,建立dr-mock.yaml檔案。
此檔案表示將mocka、mockb、mockc三個服務按照Pod的
version
標籤分為v1、v2、v3三個版本子集,其中mocka有v1、v2、v3三個版本,mockb有v1、v3兩個版本,mockc有v1、v2兩個版本。通過kubectl串連ASM執行個體,執行以下命令,建立DestinationRule流量規則。
kuebctl apply -f dr-mock.yaml
建立ASMHeaderPropagation流量規則。服務已經實現了Baggage透傳能力的前提下,可以通過ASMHeaderPropagation流量規則來指定利用Baggage在鏈路上透傳自訂要求標頭。
使用以下內容,建立propagation.yaml檔案。以下檔案表示利用Baggage中的上下文資訊,在調用鏈路中透傳名為
version
的要求標頭。apiVersion: istio.alibabacloud.com/v1beta1 kind: ASMHeaderPropagation metadata: name: version-propagation spec: headers: - version
通過kubectl串連ASM執行個體,執行以下命令, 建立ASMHeaderPropagation流量規則。
kuebctl apply -f propagation.yaml
建立VirtualService流量規則。
使用以下內容,建立vs-mock.yaml檔案。
此檔案為mocka→mockb→mockc的服務調用鏈路建立流量泳道路由規則。具體來說,是通過匹配調用鏈路中透傳的version要求標頭內容、將請求轉寄到對應的版本(例如,帶有
version: v2
要求標頭的請求都發送到v2版本的服務)。同時,該流量規則也指定了流量回退規則:當調用鏈路中的某個服務的對應版本不存在時,統一將請求回退到服務的v1版本。通過kubectl串連ASM執行個體,執行以下命令,建立VirtualService流量規則。
kubectl apply -f vs-mock.yaml
建立網關引流規則。
使用以下內容,建立gw-mock.yaml檔案。
此檔案表示為mocka→mockb→mockc的服務調用鏈路建立網關引流規則。引流方式則為權重引流,對於發送到ASM網關的流量,以4:3:3的比例分別轉寄到mocka服務的v1、v2、v3版本。當網關向mocka服務轉寄請求時,會根據轉寄的目標版本為請求添加version要求標頭,保證調用鏈路中存在泳道對應的版本資訊。
通過kubectl串連ASM執行個體,執行以下命令,建立網關引流規則。
kubectl apply -f gw-mock.yaml
步驟四:驗證流量泳道是否生效
擷取ASM網關的公網IP。具體操作,請參見步驟二:擷取ASM網關地址。
執行以下命令,設定環境變數。xxx.xxx.xxx.xxx為上一步擷取的IP。
export ASM_GATEWAY_IP=xxx.xxx.xxx.xxx
驗證全灰階鏈路功能是否生效。
執行以下命令,查看v1泳道的訪問效果。
for i in {1..100}; do curl http://${ASM_GATEWAY_IP} ; echo ''; sleep 1; done;
預期輸出:
-> mocka(version: v1, ip: 192.168.1.27)-> mockb(version: v1, ip: 192.168.1.30)-> mockc(version: v1, ip: 192.168.1.14) -> mocka(version: v1, ip: 192.168.1.27)-> mockb(version: v1, ip: 192.168.1.30)-> mockc(version: v1, ip: 192.168.1.14) -> mocka(version: v2, ip: 192.168.1.28)-> mockb(version: v1, ip: 192.168.1.30)-> mockc(version: v2, ip: 192.168.1.1) -> mocka(version: v3, ip: 192.168.1.26)-> mockb(version: v3, ip: 192.168.1.29)-> mockc(version: v1, ip: 192.168.1.14) -> mocka(version: v1, ip: 192.168.1.27)-> mockb(version: v1, ip: 192.168.1.30)-> mockc(version: v1, ip: 192.168.1.14) -> mocka(version: v1, ip: 192.168.1.27)-> mockb(version: v1, ip: 192.168.1.30)-> mockc(version: v1, ip: 192.168.1.14) -> mocka(version: v2, ip: 192.168.1.28)-> mockb(version: v1, ip: 192.168.1.30)-> mockc(version: v2, ip: 192.168.1.1) -> mocka(version: v2, ip: 192.168.1.28)-> mockb(version: v1, ip: 192.168.1.30)-> mockc(version: v2, ip: 192.168.1.1) -> mocka(version: v2, ip: 192.168.1.28)-> mockb(version: v1, ip: 192.168.1.30)-> mockc(version: v2, ip: 192.168.1.1) -> mocka(version: v2, ip: 192.168.1.28)-> mockb(version: v1, ip: 192.168.1.30)-> mockc(version: v2, ip: 192.168.1.1) -> mocka(version: v3, ip: 192.168.1.26)-> mockb(version: v3, ip: 192.168.1.29)-> mockc(version: v1, ip: 192.168.1.14) -> mocka(version: v1, ip: 192.168.1.27)-> mockb(version: v1, ip: 192.168.1.30)-> mockc(version: v1, ip: 192.168.1.14) -> mocka(version: v2, ip: 192.168.1.28)-> mockb(version: v1, ip: 192.168.1.30)-> mockc(version: v2, ip: 192.168.1.1) -> mocka(version: v3, ip: 192.168.1.26)-> mockb(version: v3, ip: 192.168.1.29)-> mockc(version: v1, ip: 192.168.1.14) -> mocka(version: v1, ip: 192.168.1.27)-> mockb(version: v1, ip: 192.168.1.30)-> mockc(version: v1, ip: 192.168.1.14) -> mocka(version: v3, ip: 192.168.1.26)-> mockb(version: v3, ip: 192.168.1.29)-> mockc(version: v1, ip: 192.168.1.14) -> mocka(version: v3, ip: 192.168.1.26)-> mockb(version: v3, ip: 192.168.1.29)-> mockc(version: v1, ip: 192.168.1.14) -> mocka(version: v1, ip: 192.168.1.27)-> mockb(version: v1, ip: 192.168.1.30)-> mockc(version: v1, ip: 192.168.1.14) -> mocka(version: v3, ip: 192.168.1.26)-> mockb(version: v3, ip: 192.168.1.29)-> mockc(version: v1, ip: 192.168.1.14) -> mocka(version: v2, ip: 192.168.1.28)-> mockb(version: v1, ip: 192.168.1.30)-> mockc(version: v2, ip: 192.168.1.1) -> mocka(version: v1, ip: 192.168.1.27)-> mockb(version: v1, ip: 192.168.1.30)-> mockc(version: v1, ip: 192.168.1.14) -> mocka(version: v3, ip: 192.168.1.26)-> mockb(version: v3, ip: 192.168.1.29)-> mockc(version: v1, ip: 192.168.1.14) -> mocka(version: v1, ip: 192.168.1.27)-> mockb(version: v1, ip: 192.168.1.30)-> mockc(version: v1, ip: 192.168.1.14) -> mocka(version: v3, ip: 192.168.1.26)-> mockb(version: v3, ip: 192.168.1.29)-> mockc(version: v1, ip: 192.168.1.14)
可以看到,流量將以約4:3:3的比例發送到服務調用鏈路的v1、v2、v3三個版本,並由v1作為基準版本,當調用鏈路中不存在某個服務的特定版本時,將會調用該服務的v1版本。