本文介绍Baggage作为链路透传的请求头时,如何通过虚拟服务以及ASMHeaderPropagation等流量规则资源的配置实现宽松模式的流量泳道和流量降级。
前提条件
已添加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版本。