在前文《基于阿里云服务网格流量泳道的全链路流量管理(一):严格模式流量泳道》、《基于阿里云服务网格流量泳道的全链路流量管理(二):宽松模式流量泳道》中,我们介绍了流量泳道的概念、使用流量泳道进行全链路灰度管理的方案,以及阿里云服务网格 ASM 提供的严格模式与宽松模式的流量泳道。在本文中,我们将介绍一种基于 OpenTelemetry 自动插装方案实现的宽松模式流量泳道场景,无需任何业务代码改造即可为运行在 Kubernetes 集群中的云原生应用实现灵活的全链路灰度管理能力。
1. 概述
流量泳道是将一个云原生应用中的多个服务根据服务版本(或其他特征)隔离成的多个独立的运行环境。
在严格模式下,每条流量泳道中包含应用的调用链路上的全部服务,对于应用程序则没有任何要求。
而在宽松模式下,您只需要确保创建一条包含调用链路中所有服务的泳道:基线泳道。其它泳道可以不包含调用链路上的全部服务。当一个泳道中的服务进行相互调用时,若目标服务在当前泳道中不存在,则请求将被转发到基线泳道中的相同服务,并在请求目标存在当前泳道中存在时将请求重新转发回当前泳道。宽松模式的流量泳道虽然可以实现灵活的全链路灰度,但要求应用程序必须包含一个能够在整条调用链路中透传的请求头(链路透传请求头)。
阿里云服务网格 ASM 已经提供一种基于 baggage 透传的全新宽松模式流量泳道方案,能够在不改造业务代码的情况下,实现泳道服务的灵活灰度。
2. 背景介绍
OpenTelemetry 是云原生计算基金会(Cloud Native Computing Foundation, CNCF)的一个项目,与其他 CNCF 项目如 Kubernetes、Prometheus 和 Envoy 等紧密相连,共同构建了云原生技术生态系统的基础。由于其提供的丰富功能和广泛适用性,OpenTelemetry 在业内已经确立了其领导地位,越来越多的企业和开发者采用它作为构建和维护可观测性平台的核心组件。
baggage 是由 OpenTelemetry 制定并推动的、在分布式系统调用链路中传递链路上下文信息的标准。它具体表现为一个名为 baggage 的请求头,其内部包含了以键值对形式存储的链路上下文信息,例如:
baggage: userId=alice,serverNode=DF%2028,isProduction=false
您可以使用 OpenTelemetry 提供的 SDK,在服务调用链路中传递 baggage 请求头、进而在整条服务链路上传递自定义的上下文信息。当服务部署在 Kubernentes 集群中时,则可以通过 OpenTelemetry Operator 的自动插装来为服务提供 baggage 透传能力、而无需修改业务代码。有关 baggage 的详细信息,可以参考 baggage[1]。
基于 baggage 的透传,阿里云服务网格 ASM 提供了一种无需修改代码即可实现的宽松模式流量泳道,并针对流量泳道的引流策略进行了增强,支持基于流量权重策略对不同的泳道进行引流。本文将首先使用 OpenTelemetry 自动插装来为服务引用透传 baggage 请求头的能力,接下来创建宽松模式下的三条泳道,并基于流量权重策略对不同的泳道进行引流。
3. 演示:实现服务 baggage 透传与宽松模式流量泳道
3.1 前提条件
- 已创建 ASM 企业版或旗舰版实例,且版本为 1.21.6.54 及以上。具体操作,请参见创建 ASM 实例[2]。
- 已添加集群到 ASM 实例。具体操作,请参见添加集群到 ASM 实例[3]。
- 已创建名称为 ingressgateway 的 ASM 网关。具体操作,请参见创建入口网关服务[4]。
- 已创建名称为 ingressgateway 且命名空间为 istio-system 的网关规则。具体操作,请参见管理网关规则[5]。
apiVersion: networking.istio.io/v1beta1 kind: Gateway metadata: name: ingressgateway namespace: istio-system spec: selector: istio: ingressgateway servers: - port: number: 80 name: http protocol: HTTP hosts: - '*'
3.2 步骤一:配置服务透传 baggage 上下文
本节主要展示如何通过 OpenTelemetry Operator 自动插装的方法,为 Kubernetes 集群中的服务添加 baggage 透传能力。
1. 部署 OpenTelemetry Operator。
a. 通过 kubectl 连接到 ASM 实例添加的 Kubernetes 集群。执行以下命令,创建 opentelemetry-operator-system 命名空间。
kubectl create namespace opentelemetry-operator-system
b. 执行以下命令,使用 Helm 在 opentelemetry-operator-system 命名空间下安装 OpenTelemetry Operator。(如果尚未安装 Helm,可以参考安装 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
c. 执行以下命令,检查 opentelemetry-operator 是否正常运行。
kubectl get pod -n opentelemetry-operator-system
预期输出:
NAME READY STATUS RESTARTS AGE opentelemetry-operator-854fb558b5-pvllj 2/2 Running 0 1m
2. 配置自动插装(auto-instrumentation)。
a. 使用以下内容,创建 instrumentation.yaml 文件。
apiVersion: opentelemetry.io/v1alpha1 kind: Instrumentation metadata: name: demo-instrumentation spec: propagators: - baggage sampler: type: parentbased_traceidratio argument: "1"
b. 执行以下命令,在 default 命名空间下声明自动插装。
kubectl apply -f instrumentation.yaml
🔔 说明:对于 OpenTelemetry 框架来说,其最佳实践还包括部署 OpenTelemetry Collector 以收集可观测数据。由于本文主要演示 OpenTelemetry 自动插装实现的 baggage 链路透传,因此没有包含部署 OpenTelemetry Collector 的步骤。
3.3 步骤二:部署示例服务
1. 为 default 命名空间启用 Siedcar 网格代理自动注入。具体操作,请参见开启 Sidecar 自动注入[6]。
关于自动注入的更多信息,请参见配置 Sidecar 注入策略。
2. 使用以下内容,创建 mock.yaml 文件。
apiVersion: v1 kind: Service metadata: name: mocka labels: app: mocka service: mocka spec: ports: - port: 8000 name: http selector: app: mocka --- apiVersion: apps/v1 kind: Deployment metadata: name: mocka-v1 labels: app: mocka version: v1 spec: replicas: 1 selector: matchLabels: app: mocka version: v1 ASM_TRAFFIC_TAG: v1 template: metadata: labels: app: mocka version: v1 ASM_TRAFFIC_TAG: v1 annotations: instrumentation.opentelemetry.io/inject-java: "true" instrumentation.opentelemetry.io/container-names: "default" spec: containers: - name: default image: registry-cn-hangzhou.ack.aliyuncs.com/acs/asm-mock:v0.1-java imagePullPolicy: IfNotPresent env: - name: version value: v1 - name: app value: mocka - name: upstream_url value: "http://mockb:8000/" ports: - containerPort: 8000 --- apiVersion: v1 kind: Service metadata: name: mockb labels: app: mockb service: mockb spec: ports: - port: 8000 name: http selector: app: mockb --- apiVersion: apps/v1 kind: Deployment metadata: name: mockb-v1 labels: app: mockb version: v1 spec: replicas: 1 selector: matchLabels: app: mockb version: v1 ASM_TRAFFIC_TAG: v1 template: metadata: labels: app: mockb version: v1 ASM_TRAFFIC_TAG: v1 annotations: instrumentation.opentelemetry.io/inject-java: "true" instrumentation.opentelemetry.io/container-names: "default" spec: containers: - name: default image: registry-cn-hangzhou.ack.aliyuncs.com/acs/asm-mock:v0.1-java imagePullPolicy: IfNotPresent env: - name: version value: v1 - name: app value: mockb - name: upstream_url value: "http://mockc:8000/" ports: - containerPort: 8000 --- apiVersion: v1 kind: Service metadata: name: mockc labels: app: mockc service: mockc spec: ports: - port: 8000 name: http selector: app: mockc --- apiVersion: apps/v1 kind: Deployment metadata: name: mockc-v1 labels: app: mockc version: v1 spec: replicas: 1 selector: matchLabels: app: mockc version: v1 ASM_TRAFFIC_TAG: v1 template: metadata: labels: app: mockc version: v1 ASM_TRAFFIC_TAG: v1 annotations: instrumentation.opentelemetry.io/inject-java: "true" instrumentation.opentelemetry.io/container-names: "default" spec: containers: - name: default image: registry-cn-hangzhou.ack.aliyuncs.com/acs/asm-mock:v0.1-java imagePullPolicy: IfNotPresent env: - name: version value: v1 - name: app value: mockc ports: - containerPort: 8000 --- apiVersion: apps/v1 kind: Deployment metadata: name: mocka-v2 labels: app: mocka version: v2 spec: replicas: 1 selector: matchLabels: app: mocka version: v2 ASM_TRAFFIC_TAG: v2 template: metadata: labels: app: mocka version: v2 ASM_TRAFFIC_TAG: v2 annotations: instrumentation.opentelemetry.io/inject-java: "true" instrumentation.opentelemetry.io/container-names: "default" spec: containers: - name: default image: registry-cn-hangzhou.ack.aliyuncs.com/acs/asm-mock:v0.1-java imagePullPolicy: IfNotPresent env: - name: version value: v2 - name: app value: mocka - name: upstream_url value: "http://mockb:8000/" ports: - containerPort: 8000 --- apiVersion: apps/v1 kind: Deployment metadata: name: mockb-v2 labels: app: mockb version: v2 spec: replicas: 1 selector: matchLabels: app: mockb version: v2 ASM_TRAFFIC_TAG: v2 template: metadata: labels: app: mockb version: v2 ASM_TRAFFIC_TAG: v2 annotations: instrumentation.opentelemetry.io/inject-java: "true" instrumentation.opentelemetry.io/container-names: "default" spec: containers: - name: default image: registry-cn-hangzhou.ack.aliyuncs.com/acs/asm-mock:v0.1-java imagePullPolicy: IfNotPresent env: - name: version value: v2 - name: app value: mockb - name: upstream_url value: "http://mockc:8000/" ports: - containerPort: 8000 --- apiVersion: apps/v1 kind: Deployment metadata: name: mockc-v2 labels: app: mockc version: v2 spec: replicas: 1 selector: matchLabels: app: mockc version: v2 ASM_TRAFFIC_TAG: v2 template: metadata: labels: app: mockc version: v2 ASM_TRAFFIC_TAG: v2 annotations: instrumentation.opentelemetry.io/inject-java: "true" instrumentation.opentelemetry.io/container-names: "default" spec: containers: - name: default image: registry-cn-hangzhou.ack.aliyuncs.com/acs/asm-mock:v0.1-java imagePullPolicy: IfNotPresent env: - name: version value: v2 - name: app value: mockc ports: - containerPort: 8000 --- apiVersion: apps/v1 kind: Deployment metadata: name: mocka-v3 labels: app: mocka version: v3 spec: replicas: 1 selector: matchLabels: app: mocka version: v3 ASM_TRAFFIC_TAG: v3 template: metadata: labels: app: mocka version: v3 ASM_TRAFFIC_TAG: v3 annotations: instrumentation.opentelemetry.io/inject-java: "true" instrumentation.opentelemetry.io/container-names: "default" spec: containers: - name: default image: registry-cn-hangzhou.ack.aliyuncs.com/acs/asm-mock:v0.1-java imagePullPolicy: IfNotPresent env: - name: version value: v3 - name: app value: mocka - name: upstream_url value: "http://mockb:8000/" ports: - containerPort: 8000 --- apiVersion: apps/v1 kind: Deployment metadata: name: mockb-v3 labels: app: mockb version: v3 spec: replicas: 1 selector: matchLabels: app: mockb version: v3 ASM_TRAFFIC_TAG: v3 template: metadata: labels: app: mockb version: v3 ASM_TRAFFIC_TAG: v3 annotations: instrumentation.opentelemetry.io/inject-java: "true" instrumentation.opentelemetry.io/container-names: "default" spec: containers: - name: default image: registry-cn-hangzhou.ack.aliyuncs.com/acs/asm-mock:v0.1-java imagePullPolicy: IfNotPresent env: - name: version value: v3 - name: app value: mockb - name: upstream_url value: "http://mockc:8000/" ports: - containerPort: 8000 --- apiVersion: apps/v1 kind: Deployment metadata: name: mockc-v3 labels: app: mockc version: v3 spec: replicas: 1 selector: matchLabels: app: mockc version: v3 ASM_TRAFFIC_TAG: v3 template: metadata: labels: app: mockc version: v3 ASM_TRAFFIC_TAG: v3 annotations: instrumentation.opentelemetry.io/inject-java: "true" instrumentation.opentelemetry.io/container-names: "default" spec: containers: - name: default image: registry-cn-hangzhou.ack.aliyuncs.com/acs/asm-mock:v0.1-java imagePullPolicy: IfNotPresent env: - name: version value: v3 - name: app value: mockc ports: - containerPort: 8000
对于每个实例服务 Pod,都加入了 instrumentation.opentelemetry.io/inject-java: "true"和instrumentation.opentelemetry.io/container-names: "default"两个注解,以声明该实例服务使用 java 语言实现,并要求 OpenTelemetry Operator 对名称为 default 的容器进行自动插装。
3. 执行以下指令,部署实例服务。
kubectl apply -f mock.yaml
基于 OpenTelemetry 自动插装机制,部署的服务 Pod 将自动具有在调用链路中传递 baggage 的能力。
3.4 步骤三:创建泳道组和对应泳道
1. 创建泳道组。
a. 登录 ASM 控制台[7],在左侧导航栏,选择服务网格 > 网格管理。
b. 在网格管理页面,单击目标实例名称,然后在左侧导航栏,选择流量管理中心 > 流量泳道。
c. 在流量泳道页面,单击创建泳道组,在创建泳道组面板,配置相关信息,然后单击确定。
配置项 | 说明 |
泳道组名称 | 本示例配置为test。 |
入口网关 | 选择ingressgateway。 |
泳道模式 | 选择宽松模式。 |
调用链路上下文透传方式 | 选择透传baggage,并在下方引流请求头中填写x-asm-prefer-tag |
泳道服务 | 选择目标Kubernetes集群和default命名空间,在下方列表中选中mocka、mockb和mockc服务,单击图标,添加目标服务到已选择区域。 |
2. 创建 s1、s2、s3 泳道,并分别绑定 v1、v2、v3 版本。
a. 在流量泳道页面的流量规则定义区域,单击创建泳道。
b. 在创建泳道对话框,配置相关信息,然后单击确定。
配置项 | 说明 |
泳道名称 | 三条泳道分别配置为s1、s2、s3。 |
配置服务标签 | 标签名称:配置为ASM_TRAFFIC_TAG标签值:三条泳道分别配置为v1、v2和v3。 |
添加服务 | s1泳道:选择mocka(default)、mockb(default)、mockc(default)。s2泳道:选择mocka(default)、mockc(default)。s3泳道:选择mockb(default)。 |
创建 s1 泳道的示例图如下:
三个泳道创建完成后,示例效果如下。默认情况下,您在泳道组中创建的第一个泳道将被设定为基线泳道。您也可以修改基线泳道,当流量发往其它泳道中不存在的服务时,通过回退机制将请求转发至基线泳道。关于修改基线泳道的具体操作,请参见修改基线泳道[8]。
三个泳道创建完成后,针对泳道组中的每个服务都将生成泳道规则对应的目标规则 DestinationRule 和虚拟服务 VirtualService。您可以在控制台左侧导航栏,选择流量管理中心 > 目标规则或虚拟服务进行查看。例如,针对 mocka 服务会自动创建如下 DestinationRule 和 VirtualService。
apiVersion: networking.istio.io/v1beta1 kind: DestinationRule metadata: labels: asm-system: 'true' provider: asm swimlane-group: test name: trafficlabel-dr-test-default-mocka namespace: istio-system spec: host: mocka.default.svc.cluster.local subsets: - labels: ASM_TRAFFIC_TAG: v1 name: s1 - labels: ASM_TRAFFIC_TAG: v2 name: s2 apiVersion: networking.istio.io/v1beta1 kind: VirtualService metadata: labels: asm-system: 'true' provider: asm swimlane-group: test name: trafficlabel-vs-test-default-mocka namespace: istio-system spec: hosts: - mocka.default.svc.cluster.local http: - match: - headers: x-asm-prefer-tag: exact: s1 route: - destination: host: mocka.default.svc.cluster.local subset: s1 fallback: target: host: mocka.default.svc.cluster.local subset: s1 - match: - headers: x-asm-prefer-tag: exact: s2 route: - destination: host: mocka.default.svc.cluster.local subset: s2 fallback: target: host: mocka.default.svc.cluster.local subset: s1 - match: - headers: x-asm-prefer-tag: exact: s3 route: - destination: host: mocka.default.svc.cluster.local subset: s3 fallback: target: host: mocka.default.svc.cluster.local subset: s1
3. 创建基于权重的统一引流规则。
a. 在流量泳道页面的流量规则定义区域,单击引流策略中的基于权重引流。
b. 在设定统一引流规则对话框,配置相关信息,然后单击确定。本文以泳道服务对应入口 API 为 /mock 为例,为三条泳道配置统一的引流规则。
配置项 | 说明 |
域名 | 配置为*。 |
匹配请求的URI | 配置匹配方式为前缀,匹配内容为/。 |
设定统一引流规则的示例图如下:
4. 设定三条泳道的引流权重,引流权重确定了流量向每条泳道发送的比例。
a. 在流量泳道页面的流量规则定义区域,在每条泳道的引流权重列,单击数字右侧的 按钮,在编辑引流权重对话框,配置相关信息,然后单击确定。
配置项 | 说明 |
入口服务 | 三条泳道都配置为mocka.default.svc.cluster.local |
权重数值 |
|
编辑流量权重的示例图如下:
3.5 步骤四:验证全链路灰度功能是否生效
1. 获取 ASM 网关的公网 IP。具体操作,请参见获取 ASM 网关地址[9]。
2. 执行以下命令,设置环境变量。xxx.xxx.xxx.xxx 为上一步获取的 IP。
export ASM_GATEWAY_IP=xxx.xxx.xxx.xxx
3. 验证全链路灰度功能是否生效。
a. 执行以下命令,查看三条泳道的访问效果。
for i in {1..100}; do curl http://${ASM_GATEWAY_IP}/mock ; echo ''; sleep 1; done;
预期输出:
-> mocka(version: v1, ip: 192.168.0.193)-> mockb(version: v1, ip: 192.168.0.1)-> mockc(version: v1, ip: 192.168.0.190) -> mocka(version: v1, ip: 192.168.0.193)-> mockb(version: v3, ip: 192.168.0.2)-> mockc(version: v1, ip: 192.168.0.190) -> mocka(version: v1, ip: 192.168.0.193)-> mockb(version: v1, ip: 192.168.0.1)-> mockc(version: v1, ip: 192.168.0.190) -> mocka(version: v1, ip: 192.168.0.193)-> mockb(version: v1, ip: 192.168.0.1)-> mockc(version: v1, ip: 192.168.0.190) -> mocka(version: v2, ip: 192.168.0.184)-> mockb(version: v1, ip: 192.168.0.1)-> mockc(version: v2, ip: 192.168.0.189) -> mocka(version: v1, ip: 192.168.0.193)-> mockb(version: v1, ip: 192.168.0.1)-> mockc(version: v1, ip: 192.168.0.190) -> mocka(version: v2, ip: 192.168.0.184)-> mockb(version: v1, ip: 192.168.0.1)-> mockc(version: v2, ip: 192.168.0.189) -> mocka(version: v1, ip: 192.168.0.193)-> mockb(version: v1, ip: 192.168.0.1)-> mockc(version: v1, ip: 192.168.0.190) -> mocka(version: v1, ip: 192.168.0.193)-> mockb(version: v1, ip: 192.168.0.1)-> mockc(version: v1, ip: 192.168.0.190) -> mocka(version: v1, ip: 192.168.0.193)-> mockb(version: v3, ip: 192.168.0.2)-> mockc(version: v1, ip: 192.168.0.190) -> mocka(version: v1, ip: 192.168.0.193)-> mockb(version: v1, ip: 192.168.0.1)-> mockc(version: v1, ip: 192.168.0.190) -> mocka(version: v1, ip: 192.168.0.193)-> mockb(version: v1, ip: 192.168.0.1)-> mockc(version: v1, ip: 192.168.0.190) -> mocka(version: v2, ip: 192.168.0.184)-> mockb(version: v1, ip: 192.168.0.1)-> mockc(version: v2, ip: 192.168.0.189) -> mocka(version: v1, ip: 192.168.0.193)-> mockb(version: v1, ip: 192.168.0.1)-> mockc(version: v1, ip: 192.168.0.190) -> mocka(version: v1, ip: 192.168.0.193)-> mockb(version: v1, ip: 192.168.0.1)-> mockc(version: v1, ip: 192.168.0.190) -> mocka(version: v2, ip: 192.168.0.184)-> mockb(version: v1, ip: 192.168.0.1)-> mockc(version: v2, ip: 192.168.0.189) -> mocka(version: v1, ip: 192.168.0.193)-> mockb(version: v3, ip: 192.168.0.2)-> mockc(version: v1, ip: 192.168.0.190) -> mocka(version: v1, ip: 192.168.0.193)-> mockb(version: v1, ip: 192.168.0.1)-> mockc(version: v1, ip: 192.168.0.190) -> mocka(version: v2, ip: 192.168.0.184)-> mockb(version: v1, ip: 192.168.0.1)-> mockc(version: v2, ip: 192.168.0.189) -> mocka(version: v2, ip: 192.168.0.184)-> mockb(version: v1, ip: 192.168.0.1)-> mockc(version: v2, ip: 192.168.0.189) -> mocka(version: v1, ip: 192.168.0.193)-> mockb(version: v1, ip: 192.168.0.1)-> mockc(version: v1, ip: 192.168.0.190) -> mocka(version: v1, ip: 192.168.0.193)-> mockb(version: v1, ip: 192.168.0.1)-> mockc(version: v1, ip: 192.168.0.190) -> mocka(version: v1, ip: 192.168.0.193)-> mockb(version: v1, ip: 192.168.0.1)-> mockc(version: v1, ip: 192.168.0.190) -> mocka(version: v1, ip: 192.168.0.193)-> mockb(version: v3, ip: 192.168.0.2)-> mockc(version: v1, ip: 192.168.0.190) -> mocka(version: v1, ip: 192.168.0.193)-> mockb(version: v1, ip: 192.168.0.1)-> mockc(version: v1, ip: 192.168.0.190) -> mocka(version: v1, ip: 192.168.0.193)-> mockb(version: v1, ip: 192.168.0.1)-> mockc(version: v1, ip: 192.168.0.190) -> mocka(version: v1, ip: 192.168.0.193)-> mockb(version: v3, ip: 192.168.0.2)-> mockc(version: v1, ip: 192.168.0.190) -> mocka(version: v2, ip: 192.168.0.184)-> mockb(version: v1, ip: 192.168.0.1)-> mockc(version: v2, ip: 192.168.0.189) -> mocka(version: v1, ip: 192.168.0.193)-> mockb(version: v3, ip: 192.168.0.2)-> mockc(version: v1, ip: 192.168.0.190) -> mocka(version: v1, ip: 192.168.0.193)-> mockb(version: v1, ip: 192.168.0.1)-> mockc(version: v1, ip: 192.168.0.190) -> mocka(version: v1, ip: 192.168.0.193)-> mockb(version: v3, ip: 192.168.0.2)-> mockc(version: v1, ip: 192.168.0.190) -> mocka(version: v2, ip: 192.168.0.184)-> mockb(version: v1, ip: 192.168.0.1)-> mockc(version: v2, ip: 192.168.0.189) -> mocka(version: v1, ip: 192.168.0.193)-> mockb(version: v1, ip: 192.168.0.1)-> mockc(version: v1, ip: 192.168.0.190) -> mocka(version: v1, ip: 192.168.0.193)-> mockb(version: v1, ip: 192.168.0.1)-> mockc(version: v1, ip: 192.168.0.190) -> mocka(version: v1, ip: 192.168.0.193)-> mockb(version: v3, ip: 192.168.0.2)-> mockc(version: v1, ip: 192.168.0.190) -> mocka(version: v2, ip: 192.168.0.184)-> mockb(version: v1, ip: 192.168.0.1)-> mockc(version: v2, ip: 192.168.0.189) -> mocka(version: v1, ip: 192.168.0.193)-> mockb(version: v1, ip: 192.168.0.1)-> mockc(version: v1, ip: 192.168.0.190) -> mocka(version: v1, ip: 192.168.0.193)-> mockb(version: v1, ip: 192.168.0.1)-> mockc(version: v1, ip: 192.168.0.190) -> mocka(version: v1, ip: 192.168.0.193)-> mockb(version: v1, ip: 192.168.0.1)-> mockc(version: v1, ip: 192.168.0.190) -> mocka(version: v1, ip: 192.168.0.193)-> mockb(version: v1, ip: 192.168.0.1)-> mockc(version: v1, ip: 192.168.0.190) -> mocka(version: v1, ip: 192.168.0.193)-> mockb(version: v1, ip: 192.168.0.1)-> mockc(version: v1, ip: 192.168.0.190) -> mocka(version: v1, ip: 192.168.0.193)-> mockb(version: v1, ip: 192.168.0.1)-> mockc(version: v1, ip: 192.168.0.190)
由预期输出得到,流量将以约 6:2:2 的比例发送到 s1、s2、s3 泳道,并由 s1 作为基线泳道,当调用链路中不存在某个服务的特定版本时,将会调用 s1 泳道中的对应服务。
4. 总结
本文简要讨论了使用流量泳道来实现全链路流量灰度管理的场景与方案,并回顾了阿里云服务网格 ASM 提供的严格与宽松两种模式的流量泳道、以及这两种模式各自的优势与挑战。接下来介绍了一种基于 OpenTelemetry 社区提出的 baggage 透传能力实现的无侵入式的宽松模式泳道,这种类型的流量泳道同时具有对业务代码侵入性低、同时保持宽松模式的灵活特性的特点。同时,我们还介绍了新的基于权重的流量引流策略,这种策略可以基于统一的流量匹配规则,将匹配到的流量以设定好的比例分发到不同的流量泳道。
相关链接:
[1] baggage
[2] 创建 ASM 实例
[3] 添加集群到 ASM 实例
[4] 创建入口网关服务
[5] 管理网关规则
[6] 开启 Sidecar 自动注入
[7] ASM 控制台
[8] 修改基线泳道
[9] 获取 ASM 网关地址
作者:尹航