当前位置: 首页 > news >正文

基于阿里云服务网格流量泳道的全链路流量管理(三):无侵入式的宽松模式泳道

作者:尹航

在前文《基于阿里云服务网格流量泳道的全链路流量管理(一):严格模式流量泳道》、《基于阿里云服务网格流量泳道的全链路流量管理(二):宽松模式流量泳道》中,我们介绍了流量泳道的概念、使用流量泳道进行全链路灰度管理的方案,以及阿里云服务网格 ASM 提供的严格模式与宽松模式的流量泳道。在本文中,我们将介绍一种基于 OpenTelemetry 自动插装方案实现的宽松模式流量泳道场景,无需任何业务代码改造即可为运行在 Kubernetes 集群中的云原生应用实现灵活的全链路灰度管理能力。

概述

流量泳道是将一个云原生应用中的多个服务根据服务版本(或其他特征)隔离成的多个独立的运行环境。

在严格模式下,每条流量泳道中包含应用的调用链路上的全部服务,对于应用程序则没有任何要求。

而在宽松模式下,您只需要确保创建一条包含调用链路中所有服务的泳道:基线泳道。其它泳道可以不包含调用链路上的全部服务。当一个泳道中的服务进行相互调用时,若目标服务在当前泳道中不存在,则请求将被转发到基线泳道中的相同服务,并在请求目标存在当前泳道中存在时将请求重新转发回当前泳道。宽松模式的流量泳道虽然可以实现灵活的全链路灰度,但要求应用程序必须包含一个能够在整条调用链路中透传的请求头(链路透传请求头)。

阿里云服务网格 ASM 已经提供一种基于 baggage 透传的全新宽松模式流量泳道方案,能够在不改造业务代码的情况下,实现泳道服务的灵活灰度。

背景介绍

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 请求头的能力,接下来创建宽松模式下的三条泳道,并基于流量权重策略对不同的泳道进行引流。

演示:实现服务 baggage 透传与宽松模式流量泳道

前提条件

  • 已创建 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: ingressgatewaynamespace: istio-system
spec:selector:istio: ingressgatewayservers:- port:number: 80name: httpprotocol: HTTPhosts:- '*'

步骤一:配置服务透传 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
  1. 配置自动插装(auto-instrumentation)。

a. 使用以下内容,创建 instrumentation.yaml 文件。

apiVersion: opentelemetry.io/v1alpha1
kind: Instrumentation
metadata:name: demo-instrumentation
spec:propagators:- baggagesampler:type: parentbased_traceidratioargument: "1"

b. 执行以下命令,在 default 命名空间下声明自动插装。

kubectl apply -f instrumentation.yaml

🔔 说明: 对于 OpenTelemetry 框架来说,其最佳实践还包括部署 OpenTelemetry Collector 以收集可观测数据。由于本文主要演示 OpenTelemetry 自动插装实现的 baggage 链路透传,因此没有包含部署 OpenTelemetry Collector 的步骤。

步骤二:部署示例服务

  1. 为 default 命名空间启用 Siedcar 网格代理自动注入。具体操作,请参见开启  Sidecar 自动注入 [ 6]

关于自动注入的更多信息,请参见配置 Sidecar 注入策略。

  1. 使用以下内容,创建 mock.yaml 文件。
apiVersion: v1
kind: Service
metadata:name: mockalabels:app: mockaservice: mocka
spec:ports:- port: 8000name: httpselector:app: mocka
---
apiVersion: apps/v1
kind: Deployment
metadata:name: mocka-v1labels:app: mockaversion: v1
spec:replicas: 1selector:matchLabels:app: mockaversion: v1ASM_TRAFFIC_TAG: v1template:metadata:labels:app: mockaversion: v1ASM_TRAFFIC_TAG: v1annotations:instrumentation.opentelemetry.io/inject-java: "true"instrumentation.opentelemetry.io/container-names: "default"spec:containers:- name: defaultimage: registry-cn-hangzhou.ack.aliyuncs.com/acs/asm-mock:v0.1-javaimagePullPolicy: IfNotPresentenv:- name: versionvalue: v1- name: appvalue: mocka- name: upstream_urlvalue: "http://mockb:8000/"ports:- containerPort: 8000
---
apiVersion: v1
kind: Service
metadata:name: mockblabels:app: mockbservice: mockb
spec:ports:- port: 8000name: httpselector:app: mockb
---
apiVersion: apps/v1
kind: Deployment
metadata:name: mockb-v1labels:app: mockbversion: v1
spec:replicas: 1selector:matchLabels:app: mockbversion: v1ASM_TRAFFIC_TAG: v1template:metadata:labels:app: mockbversion: v1ASM_TRAFFIC_TAG: v1annotations:instrumentation.opentelemetry.io/inject-java: "true"instrumentation.opentelemetry.io/container-names: "default"spec:containers:- name: defaultimage: registry-cn-hangzhou.ack.aliyuncs.com/acs/asm-mock:v0.1-javaimagePullPolicy: IfNotPresentenv:- name: versionvalue: v1- name: appvalue: mockb- name: upstream_urlvalue: "http://mockc:8000/"ports:- containerPort: 8000---
apiVersion: v1
kind: Service
metadata:name: mockclabels:app: mockcservice: mockc
spec:ports:- port: 8000name: httpselector:app: mockc
---
apiVersion: apps/v1
kind: Deployment
metadata:name: mockc-v1labels:app: mockcversion: v1
spec:replicas: 1selector:matchLabels:app: mockcversion: v1ASM_TRAFFIC_TAG: v1template:metadata:labels:app: mockcversion: v1ASM_TRAFFIC_TAG: v1annotations:instrumentation.opentelemetry.io/inject-java: "true"instrumentation.opentelemetry.io/container-names: "default"spec:containers:- name: defaultimage: registry-cn-hangzhou.ack.aliyuncs.com/acs/asm-mock:v0.1-javaimagePullPolicy: IfNotPresentenv:- name: versionvalue: v1- name: appvalue: mockcports:- containerPort: 8000
---
apiVersion: apps/v1
kind: Deployment
metadata:name: mocka-v2labels:app: mockaversion: v2
spec:replicas: 1selector:matchLabels:app: mockaversion: v2ASM_TRAFFIC_TAG: v2template:metadata:labels:app: mockaversion: v2ASM_TRAFFIC_TAG: v2annotations:instrumentation.opentelemetry.io/inject-java: "true"instrumentation.opentelemetry.io/container-names: "default"spec:containers:- name: defaultimage: registry-cn-hangzhou.ack.aliyuncs.com/acs/asm-mock:v0.1-javaimagePullPolicy: IfNotPresentenv:- name: versionvalue: v2- name: appvalue: mocka- name: upstream_urlvalue: "http://mockb:8000/"ports:- containerPort: 8000
---
apiVersion: apps/v1
kind: Deployment
metadata:name: mockb-v2labels:app: mockbversion: v2
spec:replicas: 1selector:matchLabels:app: mockbversion: v2ASM_TRAFFIC_TAG: v2template:metadata:labels:app: mockbversion: v2ASM_TRAFFIC_TAG: v2annotations:instrumentation.opentelemetry.io/inject-java: "true"instrumentation.opentelemetry.io/container-names: "default"spec:containers:- name: defaultimage: registry-cn-hangzhou.ack.aliyuncs.com/acs/asm-mock:v0.1-javaimagePullPolicy: IfNotPresentenv:- name: versionvalue: v2- name: appvalue: mockb- name: upstream_urlvalue: "http://mockc:8000/"ports:- containerPort: 8000---
apiVersion: apps/v1
kind: Deployment
metadata:name: mockc-v2labels:app: mockcversion: v2
spec:replicas: 1selector:matchLabels:app: mockcversion: v2ASM_TRAFFIC_TAG: v2template:metadata:labels:app: mockcversion: v2ASM_TRAFFIC_TAG: v2annotations:instrumentation.opentelemetry.io/inject-java: "true"instrumentation.opentelemetry.io/container-names: "default"spec:containers:- name: defaultimage: registry-cn-hangzhou.ack.aliyuncs.com/acs/asm-mock:v0.1-javaimagePullPolicy: IfNotPresentenv:- name: versionvalue: v2- name: appvalue: mockcports:- containerPort: 8000
---
apiVersion: apps/v1
kind: Deployment
metadata:name: mocka-v3labels:app: mockaversion: v3
spec:replicas: 1selector:matchLabels:app: mockaversion: v3ASM_TRAFFIC_TAG: v3template:metadata:labels:app: mockaversion: v3ASM_TRAFFIC_TAG: v3annotations:instrumentation.opentelemetry.io/inject-java: "true"instrumentation.opentelemetry.io/container-names: "default"spec:containers:- name: defaultimage: registry-cn-hangzhou.ack.aliyuncs.com/acs/asm-mock:v0.1-javaimagePullPolicy: IfNotPresentenv:- name: versionvalue: v3- name: appvalue: mocka- name: upstream_urlvalue: "http://mockb:8000/"ports:- containerPort: 8000
---
apiVersion: apps/v1
kind: Deployment
metadata:name: mockb-v3labels:app: mockbversion: v3
spec:replicas: 1selector:matchLabels:app: mockbversion: v3ASM_TRAFFIC_TAG: v3template:metadata:labels:app: mockbversion: v3ASM_TRAFFIC_TAG: v3annotations:instrumentation.opentelemetry.io/inject-java: "true"instrumentation.opentelemetry.io/container-names: "default"spec:containers:- name: defaultimage: registry-cn-hangzhou.ack.aliyuncs.com/acs/asm-mock:v0.1-javaimagePullPolicy: IfNotPresentenv:- name: versionvalue: v3- name: appvalue: mockb- name: upstream_urlvalue: "http://mockc:8000/"ports:- containerPort: 8000---
apiVersion: apps/v1
kind: Deployment
metadata:name: mockc-v3labels:app: mockcversion: v3
spec:replicas: 1selector:matchLabels:app: mockcversion: v3ASM_TRAFFIC_TAG: v3template:metadata:labels:app: mockcversion: v3ASM_TRAFFIC_TAG: v3annotations:instrumentation.opentelemetry.io/inject-java: "true"instrumentation.opentelemetry.io/container-names: "default"spec:containers:- name: defaultimage: registry-cn-hangzhou.ack.aliyuncs.com/acs/asm-mock:v0.1-javaimagePullPolicy: IfNotPresentenv:- name: versionvalue: v3- name: appvalue: mockcports:- containerPort: 8000

对于每个实例服务 Pod,都加入了 instrumentation.opentelemetry.io/inject-java: "true"和instrumentation.opentelemetry.io/container-names: "default"两个注解,以声明该实例服务使用 java 语言实现,并要求 OpenTelemetry Operator 对名称为 default 的容器进行自动插装。

  1. 执行以下指令,部署实例服务。
kubectl apply -f mock.yaml

基于 OpenTelemetry 自动插装机制,部署的服务 Pod 将自动具有在调用链路中传递 baggage 的能力。

步骤三:创建泳道组和对应泳道

  1. 创建泳道组。

a. 登录 ASM 控制台 [ 7] ,在左侧导航栏,选择服务网格 > 网格管理

b. 在网格管理页面,单击目标实例名称,然后在左侧导航栏,选择流量管理中心 > 流量泳道

c. 在流量泳道页面,单击创建泳道组,在创建泳道组面板,配置相关信息,然后单击确定

image.png

  1. 创建 s1、s2、s3 泳道,并分别绑定 v1、v2、v3 版本。

a. 在流量泳道页面的流量规则定义区域,单击创建泳道

b. 在创建泳道对话框,配置相关信息,然后单击确定

image.png

创建 s1 泳道的示例图如下:

图片

三个泳道创建完成后,示例效果如下。默认情况下,您在泳道组中创建的第一个泳道将被设定为基线泳道。您也可以修改基线泳道,当流量发往其它泳道中不存在的服务时,通过回退机制将请求转发至基线泳道。关于修改基线泳道的具体操作,请参见修改基线泳道 [ 8]

图片

三个泳道创建完成后,针对泳道组中的每个服务都将生成泳道规则对应的目标规则 DestinationRule 和虚拟服务 VirtualService。您可以在控制台左侧导航栏,选择流量管理中心 > 目标规则虚拟服务进行查看。例如,针对 mocka 服务会自动创建如下 DestinationRule 和 VirtualService。

apiVersion: networking.istio.io/v1beta1
kind: DestinationRule
metadata:labels:asm-system: 'true'provider: asmswimlane-group: testname: trafficlabel-dr-test-default-mockanamespace: istio-system
spec:host: mocka.default.svc.cluster.localsubsets:- labels:ASM_TRAFFIC_TAG: v1name: s1- labels:ASM_TRAFFIC_TAG: v2name: s2apiVersion: networking.istio.io/v1beta1
kind: VirtualService
metadata:labels:asm-system: 'true'provider: asmswimlane-group: testname: trafficlabel-vs-test-default-mockanamespace: istio-system
spec:hosts:- mocka.default.svc.cluster.localhttp:- match:- headers:x-asm-prefer-tag:exact: s1route:- destination:host: mocka.default.svc.cluster.localsubset: s1fallback:target:host: mocka.default.svc.cluster.localsubset: s1- match:- headers:x-asm-prefer-tag:exact: s2route:- destination:host: mocka.default.svc.cluster.localsubset: s2fallback:target:host: mocka.default.svc.cluster.localsubset: s1- match:- headers:x-asm-prefer-tag:exact: s3route:- destination:host: mocka.default.svc.cluster.localsubset: s3fallback:target:host: mocka.default.svc.cluster.localsubset: s1
  1. 创建基于权重的统一引流规则。

a. 在流量泳道页面的流量规则定义区域,单击引流策略中的基于权重引流

b. 在设定统一引流规则对话框,配置相关信息,然后单击确定。本文以泳道服务对应入口 API 为 /mock 为例,为三条泳道配置统一的引流规则。

image.png

设定统一引流规则的示例图如下:

图片

  1. 设定三条泳道的引流权重,引流权重确定了流量向每条泳道发送的比例。

a. 在流量泳道页面的流量规则定义区域,在每条泳道的引流权重列,单击数字右侧的图片按钮,在编辑引流权重对话框,配置相关信息,然后单击确定

image.png

编辑流量权重的示例图如下:

图片

步骤四:验证全链路灰度功能是否生效

  1. 获取 ASM 网关的公网 IP。具体操作,请参见获取 ASM 网关地址 [ 9]

  2. 执行以下命令,设置环境变量。xxx.xxx.xxx.xxx 为上一步获取的 IP。

export ASM_GATEWAY_IP=xxx.xxx.xxx.xxx
  1. 验证全链路灰度功能是否生效。

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 泳道中的对应服务。

总结

本文简要讨论了使用流量泳道来实现全链路流量灰度管理的场景与方案,并回顾了阿里云服务网格 ASM 提供的严格与宽松两种模式的流量泳道、以及这两种模式各自的优势与挑战。接下来介绍了一种基于 OpenTelemetry 社区提出的 baggage 透传能力实现的无侵入式的宽松模式泳道,这种类型的流量泳道同时具有对业务代码侵入性低、同时保持宽松模式的灵活特性的特点。同时,我们还介绍了新的基于权重的流量引流策略,这种策略可以基于统一的流量匹配规则,将匹配到的流量以设定好的比例分发到不同的流量泳道。

相关链接:

[1] baggage

https://opentelemetry.io/docs/concepts/signals/baggage/

[2] 创建 ASM 实例

https://help.aliyun.com/document_detail/147793.html#task-2370657

[3] 添加集群到 ASM 实例

https://help.aliyun.com/document_detail/148231.html#task-2372122

[4] 创建入口网关服务

https://help.aliyun.com/document_detail/150510.html#task-2372970

[5] 管理网关规则

https://help.aliyun.com/document_detail/150504.html

[6] 开启 Sidecar 自动注入

https://help.aliyun.com/document_detail/186136.html#task-1962690

[7] ASM 控制台

https://servicemesh.console.aliyun.com/

[8] 修改基线泳道

https://help.aliyun.com/zh/asm/user-guide/scenario-1-the-drainage-request-header-is-not-transmitted-in#0561972057jqm

[9] 获取 ASM 网关地址

https://help.aliyun.com/document_detail/444079.html#section-ida-zt6-md7

相关文章:

基于阿里云服务网格流量泳道的全链路流量管理(三):无侵入式的宽松模式泳道

作者:尹航 在前文《基于阿里云服务网格流量泳道的全链路流量管理(一):严格模式流量泳道》、《基于阿里云服务网格流量泳道的全链路流量管理(二):宽松模式流量泳道》中,我们介绍了流…...

9行超强代码用Python工具快速获取放假日期

9行超强代码用Python工具快速获取放假日期 在很多场景下,我们需要获知国内具体的节假日安排情况,而国内每一年具体的放假安排以及调休情况,都依赖于国务院发布的具体公告,如果不想自己手动整理相关数据的话,我们可以用Python来快速获取最新的放假日期. 可以通过调用公开的 API…...

Elastic Search(ES)Java 入门实操(2)搜索代码

上篇解释了 ES 的基本概念和分词器。Elastic Search (ES)Java 入门实操(1)下载安装、概念-CSDN博客 Elastic Search(ES)Java 入门实操(3)数据同步-CSDN博客 这篇主要演示 Java 整合…...

Hudi Spark Sql Procedures 回滚 Hudi 表数据

前言 因为有 Hudi Rollback 的需求,所以单独总结 Hudi Spark Sql Procedures Rollback。 版本 Hudi 0.13.0(发现有bug)、(然后升级)0.14.1Spark 3.2.3Procedures 官方文档:https://hudi.apache.org/docs/procedures 相关阅读:Hudi Spark SQL Call Procedures学习总结…...

【重学C语言】十九、SDL2 图形化编程的使用

【重学C语言】十九、SDL2 图形化编程的使用 SDL2 的第一个程序渲染器纹理渲染1. 纹理的概念2. 加载纹理3. 渲染纹理4. 纹理设置和查询5. 纹理渲染流程6. 注意事项SDL2_imageSDL2 的第一个程序 #define SDL_MAIN_HANDLED #include <SDL.h>int main(int argc, char* argv[…...

什么是电风扇行情?

“电风扇行情” 是一个金融术语&#xff0c;用于描述证券市场中价格上下波动频繁、幅度较大&#xff0c;但总体趋势不明显的市场状况。   其名称来源于电风扇的扇叶在旋转时&#xff0c;风向不断变化的特征&#xff0c;形象地比喻了市场价格频繁变动但没有明确方向的情景。 …...

pytho入门教程

文章目录 随机数据生成的方式list操作方式数据操作方式处理缺失数据数据框操作方式画图的方式 随机数据生成的方式 # 随机生成数据的方式 # 1. 随机生成10-20之间的浮点数 holdForce random.uniform(10,20) # print(holdForce)# 2.for循环输出50个数据的方式 # for i in rang…...

Elasticsearch:ES|QL 查询 TypeScript 类型(二)

在我之前的文章 “Elasticsearch&#xff1a;ES|QL 查询 TypeScript 类型&#xff08;一&#xff09;”&#xff0c;我们讲述了如何在 Nodejs 里对 ES|QL 进行查询。在今天的文章中&#xff0c;我们来使用一个完整的例子来进行详细描述。更多有关如何使用 Nodejs 来访问 Elasti…...

元音 (音标) 和元音字母的区别

元音 [音标] 和元音字母的区别 1. 音位 (phoneme)1.1. Correspondence between letters and phonemes 2. 元音 (vowel)3. 辅音 (consonant)3.1. Consonant sounds and consonant letters 4. 元音字母 (vowel letter)References 1. 音位 (phoneme) https://en.wikipedia.org/wi…...

SMS - 基于阿里云实现手机短信验证码登录(无需备案,非测试)

目录 SMS 环境调试 从阿里云云市场中购买第三方短信服务 调试短信验证码功能 实战开发 封装组件 对外接口 调用演示 SMS 环境调试 从阿里云云市场中购买第三方短信服务 a&#xff09;进入阿里云首页&#xff0c;然后从云市场中找到 “短信” &#xff08;一定要从 云…...

使用Python编写Ping监测程序

Ping是一种常用的网络诊断工具&#xff0c;它可以测试两台计算机之间的连通性&#xff1b; 如果您需要监测某个IP地址的连通情况&#xff0c;可以使用Python编写一个Ping监测程序&#xff1b; 本文将介绍如何使用Python编写Ping监测程序 首先&#xff0c;需要导入os、sys、t…...

iptables常用命令总结

1.iptables 是什么 在Linux中&#xff0c;iptables就是一款强大而灵活的防火墙工具&#xff0c;它为系统管理员提供了广泛的配置选项&#xff0c;可以有效地控制数据包的流动&#xff0c;实现网络访问的控制及安全性增强。 iptables 使用三个不同的链来允许或阻止流量&#x…...

spring 自定义注解实现

实现自定义注解&#xff0c;通常会结合AOP&#xff08;面向切面编程&#xff09;来创建一个自定义的行为。 下面创建一个名为MyCustomAnnotation的自定义注解&#xff0c;并使用AOP编写一个切面来处理这个注解。 1. 创建自定义注解&#xff1a; import java.lang.annotation…...

10.dockerfile自动构建镜像

dockerfile自动构建镜像 类似ansible剧本&#xff0c;大小几kb 手动做镜像&#xff1a;大小几百M 首先创建一个dockerfile的路径&#xff0c;便于在路径下存在多个路径每个路径下都是dockerfile命名的脚本 注释&#xff1a;文件必须为&#xff1a;dockerfile或者Dockerfile …...

python -- series和 DataFrame增删改数据

学习目标 知道df添加新列的操作 知道insert函数插入列数据 知道drop函数删除df的行或列数据 知道drop_duplicates函数对df或series进行数据去重 知道unique函数对series进行数据去重 知道apply函数的使用方法 1 DataFrame添加列 注意:本文用到的数据集在文章顶部 1.1 直…...

window.clearInterval(timer) 清除定时器

window.clearInterval(timer)是用来清除定时器的方法。在JavaScript中&#xff0c;使用定时器可以在指定的时间间隔执行一段代码。通常&#xff0c;使用setTimeout()方法可以在一定时间后执行一次代码&#xff0c;而使用setInterval()方法可以在每个时间间隔执行一次代码。 使…...

Java项目如何外发告警日志到企业微信

前言 最近领导交代了一个需求,就是有些许客户不单单满足平台告警日志外发到邮箱、短信的形式,还要以消息聊天的形式外发给企业微信。 具体操作 1、注册企业微信。 2、登录企业微信,找到应用管理,创建应用。 3、创建完之后需要记录以下图片中两个值的信息。 4、然后记录下…...

NLP--关键词

在去停用词后的文本中进行词频统计和关键词统计以及词云图显示&#xff0c;来进行文本的关键词提取&#xff0c;让人一目了然。 1.词频统计 统计文本中多次出现的词语&#xff0c;来寻找文章中的关键词&#xff0c;因为多次出现很可能就是关键内容。调用统计数量的Counter库和…...

Qt5学习笔记

一、基础知识 1、基本控件类型 水平弹簧与垂直弹簧的父类都是QSpaceItem。关于PushButton相关的控件类型&#xff1a; QPushButton&#xff1a;最基础的按钮类型。QToolButton&#xff1a;可以控制图片、文字任意组合的显示方式的按钮类型。QRadioButton&#xff1a;就像rad…...

数据结构与算法笔记:基础篇 - 散列表(下):为什么散列表和链表经常会一起使用?

概述 已经学习了这么多章节了&#xff0c;你有没有发现&#xff0c;两种数据结构&#xff0c;散列表和链表&#xff0c;经常会被放在一起使用。你还记得&#xff0c;前面的章节中都有哪些地方讲到散列表和链表的组合使用吗&#xff1f; 在链表那一节&#xff0c;我讲到如何用…...

Chapter03-Authentication vulnerabilities

文章目录 1. 身份验证简介1.1 What is authentication1.2 difference between authentication and authorization1.3 身份验证机制失效的原因1.4 身份验证机制失效的影响 2. 基于登录功能的漏洞2.1 密码爆破2.2 用户名枚举2.3 有缺陷的暴力破解防护2.3.1 如果用户登录尝试失败次…...

Java多线程实现之Callable接口深度解析

Java多线程实现之Callable接口深度解析 一、Callable接口概述1.1 接口定义1.2 与Runnable接口的对比1.3 Future接口与FutureTask类 二、Callable接口的基本使用方法2.1 传统方式实现Callable接口2.2 使用Lambda表达式简化Callable实现2.3 使用FutureTask类执行Callable任务 三、…...

零基础设计模式——行为型模式 - 责任链模式

第四部分&#xff1a;行为型模式 - 责任链模式 (Chain of Responsibility Pattern) 欢迎来到行为型模式的学习&#xff01;行为型模式关注对象之间的职责分配、算法封装和对象间的交互。我们将学习的第一个行为型模式是责任链模式。 核心思想&#xff1a;使多个对象都有机会处…...

实现弹窗随键盘上移居中

实现弹窗随键盘上移的核心思路 在Android中&#xff0c;可以通过监听键盘的显示和隐藏事件&#xff0c;动态调整弹窗的位置。关键点在于获取键盘高度&#xff0c;并计算剩余屏幕空间以重新定位弹窗。 // 在Activity或Fragment中设置键盘监听 val rootView findViewById<V…...

OPENCV形态学基础之二腐蚀

一.腐蚀的原理 (图1) 数学表达式&#xff1a;dst(x,y) erode(src(x,y)) min(x,y)src(xx,yy) 腐蚀也是图像形态学的基本功能之一&#xff0c;腐蚀跟膨胀属于反向操作&#xff0c;膨胀是把图像图像变大&#xff0c;而腐蚀就是把图像变小。腐蚀后的图像变小变暗淡。 腐蚀…...

中医有效性探讨

文章目录 西医是如何发展到以生物化学为药理基础的现代医学&#xff1f;传统医学奠基期&#xff08;远古 - 17 世纪&#xff09;近代医学转型期&#xff08;17 世纪 - 19 世纪末&#xff09;​现代医学成熟期&#xff08;20世纪至今&#xff09; 中医的源远流长和一脉相承远古至…...

【Linux系统】Linux环境变量:系统配置的隐形指挥官

。# Linux系列 文章目录 前言一、环境变量的概念二、常见的环境变量三、环境变量特点及其相关指令3.1 环境变量的全局性3.2、环境变量的生命周期 四、环境变量的组织方式五、C语言对环境变量的操作5.1 设置环境变量&#xff1a;setenv5.2 删除环境变量:unsetenv5.3 遍历所有环境…...

pikachu靶场通关笔记19 SQL注入02-字符型注入(GET)

目录 一、SQL注入 二、字符型SQL注入 三、字符型注入与数字型注入 四、源码分析 五、渗透实战 1、渗透准备 2、SQL注入探测 &#xff08;1&#xff09;输入单引号 &#xff08;2&#xff09;万能注入语句 3、获取回显列orderby 4、获取数据库名database 5、获取表名…...

SpringAI实战:ChatModel智能对话全解

一、引言&#xff1a;Spring AI 与 Chat Model 的核心价值 &#x1f680; 在 Java 生态中集成大模型能力&#xff0c;Spring AI 提供了高效的解决方案 &#x1f916;。其中 Chat Model 作为核心交互组件&#xff0c;通过标准化接口简化了与大语言模型&#xff08;LLM&#xff0…...

Matlab实现任意伪彩色图像可视化显示

Matlab实现任意伪彩色图像可视化显示 1、灰度原始图像2、RGB彩色原始图像 在科研研究中&#xff0c;如何展示好看的实验结果图像非常重要&#xff01;&#xff01;&#xff01; 1、灰度原始图像 灰度图像每个像素点只有一个数值&#xff0c;代表该点的​​亮度&#xff08;或…...