当前位置: 首页 > 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;我讲到如何用…...

读AI未来进行式笔记06自动驾驶技术

1. 跃层冲击 1.1. 每个社会其实都处于不同的楼层&#xff0c;往往处于更低楼层的社会&#xff0c;要承受来自更高楼层的社会发展带来的更大冲击 2. 驾驶 2.1. 开车时最关键的不是车&#xff0c;而是路 2.2. 人是比机器更脆弱的生命&am…...

SpringAOP 常见应用场景

文章目录 SpringAOP1 概念2 常见应用场景3 AOP的几种通知类型分别有什么常见的应用场景4 AOP实现 性能监控4.1 首先&#xff0c;定义一个切面类&#xff0c;用于实现性能监控逻辑&#xff1a;4.2 定义自定义注解4.3 注解修饰监控的方法 5 AOP实现 API调用统计5.1 定义切面类&am…...

html+css示例

HTML HTML&#xff08;超文本标记语言&#xff09;和CSS&#xff08;层叠样式表&#xff09;是构建和设计网页的两种主要技术。HTML用于创建网页的结构和内容&#xff0c;而CSS用于控制其外观和布局。 HTML基础 HTML使用标签来标记网页中的不同部分。每个标签通常有一个开始…...

Day51 动态规划part10+Day52 动态规划part11

LC121买卖股票的最佳时机&#xff08;未掌握&#xff09; 暴力&#xff1a;双层循环寻找最优间距&#xff0c;每一次都确定一个起点&#xff0c;遍历剩余节点当作终点 贪心&#xff1a;取最左最小值&#xff0c;不断遍历那么得到的差值最最大值就是最大利润。 动态规划 dp数组…...

Wireshark自定义Lua插件

背景&#xff1a; 常见的抓包工具有tcpdump和wireshark&#xff0c;二者可基于网卡进行抓包&#xff1a;tcpdump用于Linux环境抓包&#xff0c;而wireshark用于windows环境。抓包后需借助包分析工具对数据进行解析&#xff0c;将不可读的二进制数转换为可读的数据结构。 wires…...

商城项目【尚品汇】07分布式锁-2 Redisson篇

文章目录 1 Redisson功能介绍2 Redisson在Springboot中快速入门&#xff08;代码&#xff09;2.1 导入依赖2.2 Redisson配置2.3 将自定义锁setnx换成Redisson实现&#xff08;可重入锁&#xff09; 3 可重入锁原理3.1 自定义分布式锁setnx为什么不可以重入3.2 redisson为什么可…...

Adobe Illustrator 矢量图设计软件下载安装,Illustrator 轻松创建各种矢量图形

Adobe Illustrator&#xff0c;它不仅仅是一个简单的图形编辑工具&#xff0c;更是一个拥有丰富功能和强大性能的设计利器。 在这款软件中&#xff0c;用户可以通过各种精心设计的工具&#xff0c;轻松创建和编辑基于矢量路径的图形文件。这些矢量图形不仅具有高度的可编辑性&a…...

Nvidia/算能 +FPGA+AI大算力边缘计算盒子:中国舰船研究院

中国舰船研究院又称中国船舶重工集团公司第七研究院&#xff0c;隶属于中国船舶重工集团公司&#xff0c;是专门从事舰船研究、设计、开发的科学技术研究机构&#xff0c;是中国船舶重工集团公司的军品技术研究中心、科技开发中心&#xff1b;主要从事舰船武器装备发展战略研究…...

双网卡配置IP和路由总结

1.在网络适配器属性IPv4中设置默认网关&#xff08;记网关地址为A&#xff09;&#xff0c;将会在本地路由表中新增一条记录&#xff1a; 网络号子网掩码网关地址0.0.0.00.0.0.0A 2.如果有两个网卡&#xff08;假设一个连接内网&#xff0c;一个连接互联网&#xff09;&#…...

【纯血鸿蒙】——自适应布局如何实现?

界面级一多能力有 2 类&#xff1a; 自适应布局: 略微调整界面结构 响应式布局&#xff1a;比较大的界面调整 本文章先主要讲解自适应布局&#xff0c;响应式布局再后面文章再细讲。话不多说&#xff0c;开始了。 自适应布局 针对常见的开发场景&#xff0c;方舟开发框架提…...

iis7安装wordpress/视频号怎么付费推广

软件工程-软件维护/系统维护 系统可维护性 评价指标 可理解性可测试性可修改性 系统维护类型 改正性维护适应性维护预防性维护完善性维护 tip&#xff1a;完善性维护占半壁江山 系统文档 开发文档&#xff1a;技术员编写&#xff0c;与开发相关产品文档&#xff1a;与用…...

wordpress 迅影网/营销型网站的类型

来源&#xff1a;牛客网 有如下函数定义&#xff0c;执行结果正确的是&#xff1f; def dec(f): n 3 def wrapper(*args,**kw): return f(*args,**kw) * n return wrapper dec def foo(n): return n * 2 A foo(2) 12 B foo(3) 12 C foo(2) 6 D foo(3) 6...

平谷区住房城乡建设委官方网站/餐饮营销引流都有什么方法

一、准备工作 Eureka通过运行多个实例&#xff0c;使其更具有高可用性。事实上&#xff0c;这是它默认的熟性&#xff0c;你需要做的就是给对等的实例一个合法的关联serviceurl 二、改造工作 在eureka-server工程中resources文件夹下&#xff0c;创建配置文件application-pe…...

行业网站建设哪家好/百度竞价排名费用

这两年&#xff0c;线上办公逐渐常态化&#xff0c;相信大家对ftp这个概念也比较熟悉了。ftp&#xff0c;即文件传输协议&#xff0c;线上办公就是用ftp软件进行文件传输的。那ftp传输文件大小有限制吗,ftp文件传输工具有哪些我们一起来看看。 一、ftp传输文件大小有限制吗 f…...

网站流量分析系统/电商运营培训大概多少学费

仅作为记录&#xff0c;大佬请跳过。 感谢大佬博主文章&#xff1a; 传送门 传送门2...

网站制作百度/营销型网站的公司

跟着官网的教程学lvgl&#xff0c;在画圆弧的时候发现不知道怎么设置圆弧的粗细。 官网教程没用到&#xff0c;查百度半天也没找着。最后翻函数手册发现原来 lv_style_set_arc_width() 这个函数就是用来设置圆弧的粗细的。&#xff08;我以为是用来设置宽度的&#xff09; LV…...