Linkerd 流量拆分方案

简介: Linkerd 流量拆分方案


网络异常,图片无法展示
|

在 Linkerd 中,金丝雀发布是通过流量拆分来管理的,这项功能允许你根据可动态配置的权重,将请求分配给不同的 Kubernetes 服务对象。虽然流量分割可以适用于任意的 Service 对象,但一般情况下是将一个 Service 的传入流量分给不同版本的 Service。


网络异常,图片无法展示
|


流量分割功能是通过 Linkerd 的 TrafficSplit CRD 来控制的(TrafficSplit CRD 遵循服务网接口(SMI)中定义的规范,这是 Linkerd 实现的几个 SMI API 中的一个)。创建 TrafficSplit CRD 允许我们控制 Linkerd 如何将流量代理给 TrafficSplit 引用的服务。

TrafficSplit CRD 是根据 Kubernetes Service 对象编写的,TrafficSplit 描述了一个中心根或 apex 服务,流量被发送到该服务,以及一个或多个实际接收它的后端服务,与 TrafficSplit 中还指定的权重成比例。

另外需要注意,Kubernetes 的 Service 对象不一定有后台工作负载。虽然这对于普通服务来说很少见,但是我们会在 TrafficSplitsapex 服务中大量使用该功能,因为 TrafficSplit 会导致发往 apex 的流量实际发送到后端服务,所以 apex 实际上不需要拥有自己的 Deployment。

更新服务

接下来我们还是以 Emojivoto 应用为例来创建两个新的 Service 对象,apex 服务将没有关联的 Deployment 资源,第二项服务将是 Emojivoto 的 web 服务的一个更新版本,会在页面顶部添加一些文本信息。

创建这两个服务后,我们将创建一个 TrafficSplit 资源,该资源会将发送到 apex 服务的流量在 web 服务的原始版本和更新版本之间进行拆分。

网络异常,图片无法展示
|

更新版本的 web 服务资源对象如下所示:

# web-svc-2.yamlapiVersion: v1kind: ServiceAccountmetadata:  name: web-2  namespace: emojivoto---apiVersion: v1kind: Servicemetadata:  name: web-svc-2  namespace: emojivotospec:  ports:    - name: http      port: 80      targetPort: 8080  selector:    app: web-svc-2  type: ClusterIP---apiVersion: apps/v1kind: Deploymentmetadata:  labels:    app.kubernetes.io/name: web-svc-2    app.kubernetes.io/part-of: emojivoto    app.kubernetes.io/version: linux-training-v2  name: web-svc-2  namespace: emojivotospec:  selector:    matchLabels:      app: web-svc-2      version: linux-training-v2  template:    metadata:      annotations:        linkerd.io/inject: enabled # 设置自动注入的注解      labels:        app: web-svc-2        version: linux-training-v2    spec:      containers:        - env:            - name: WEB_PORT              value: "8080"            - name: EMOJISVC_HOST              value: emoji-svc.emojivoto:8080            - name: VOTINGSVC_HOST              value: voting-svc.emojivoto:8080            - name: INDEX_BUNDLE              value: dist/index_bundle.js            - name: MESSAGE_OF_THE_DAY              value: "Welcome to version 2! Now with more words!"          image: buoyantio/emojivoto-web:lf-training          name: web-svc-2          ports:            - containerPort: 8080              name: http          resources:            requests:              cpu: 100m      serviceAccountName: web-2

直接应用上面的资源对象:

$ kubectl apply -f web-svc-2.yamlserviceaccount/web-2 createdservice/web-svc-2 createddeployment.apps/web-svc-2 created

部署后先验证更新版本的服务是否已经正确部署了。

$ kubectl get po --selector app=web-svc-2 -n emojivotoNAME                         READY   STATUS    RESTARTS   AGEweb-svc-2-f9d77474f-hgsg4    2/2     Running       0      10s$ kubectl get svc web-svc-2 -n emojivotoNAME        TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)   AGEweb-svc-2   ClusterIP   10.102.99.153   <none>        80/TCP    3m45s

部署成功后同样可以使用 kubectl port-forward 命令来暴露服务:

$ kubectl port-forward svc/web-svc-2 8080:80 -n emojivoto

同样我们还是可以在浏览器中通过 localhost:8080 访问新版本的应用。

网络异常,图片无法展示
|

在页面顶部可以看到新版本的应用多了一行字符信息。

创建 TrafficSplit

接下来我们需要创建一个 apex 服务,我们这里将其称为 web-apex,不过这次没有 Pod 运行,所以无法向服务发送任何请求,因为没有端点。

# web-apex.yamlapiVersion: v1kind: ServiceAccountmetadata:  name: web-apex  namespace: emojivoto---apiVersion: v1kind: Servicemetadata:  name: web-apex  namespace: emojivotospec:  ports:    - name: http      port: 80  selector:    app: web-apex  type: ClusterIP

同样直接应用上面的资源对象:

$ kubectl apply -f web-apex.yamlserviceaccount/web-apex createdservice/web-apex created$ kubectl get svc -n emojivoto -o wideNAME         TYPE        CLUSTER-IP       EXTERNAL-IP   PORT(S)             AGE   SELECTORemoji-svc    ClusterIP   10.103.235.14    <none>        8080/TCP,8801/TCP   8d    app=emoji-svcvoting-svc   ClusterIP   10.102.32.81     <none>        8080/TCP,8801/TCP   8d    app=voting-svcweb-apex     ClusterIP   10.104.12.249    <none>        80/TCP              84s   app=web-apexweb-svc      ClusterIP   10.106.220.250   <none>        80/TCP              8d    app=web-svcweb-svc-2    ClusterIP   10.102.99.153    <none>        80/TCP              27m   app=web-svc-2

从上面的输出可以看到 web-apex 服务和其他普通服务一样,但是他并没有端点。

$ kubectl get ep -n emojivotoNAME         ENDPOINTS                             AGEemoji-svc    10.244.1.228:8801,10.244.1.228:8080   8dvoting-svc   10.244.1.202:8801,10.244.1.202:8080   8dweb-apex     <none>                                2m55sweb-svc      10.244.1.227:8080                     8dweb-svc-2    10.244.1.233:8080                     28m

在继续之前我们可以先看看当前应用的流量状态:

$ linkerd viz stat po -n emojivotoNAME                         STATUS   MESHED   SUCCESS      RPS   LATENCY_P50   LATENCY_P95   LATENCY_P99   TCP_CONNemoji-696d9d8f95-5vn9w      Running      1/1   100.00%   2.3rps           1ms           1ms           1ms          4vote-bot-6d7677bb68-jvxsg   Running      1/1   100.00%   0.3rps           1ms           1ms           1ms          1voting-ff4c54b8d-xhjv7      Running      1/1    98.04%   0.8rps           1ms           8ms          10ms          4web-5f86686c4d-58p7k        Running      1/1   100.00%   1.4rps           1ms           6ms           9ms          2

可以清楚看到虽然我们将 web 服务的更新版本已经部署了,但是现在没有产生任何的流量。接下来我们需要去创建一个 TrafficSplit 对象,然后去拆分一部分流量到我们的新服务中去。

创建一个如下所示的 TrafficSplit 资源对象:

# web-svc-ts.ymlapiVersion: split.smi-spec.io/v1alpha2kind: TrafficSplitmetadata:  name: web-svc-ts  namespace: emojivotospec:  # 客户端用于连接到目标应用程序的 root 服务  service: web-apex  # 命名空间中的 Service 以及它们自己的 selectors、端点和配置。  backends: # 拆分的后端服务    - service: web-svc      weight: 500 # 权重    - service: web-svc-2      weight: 500

上面的资源对象中主要包括两个属性:

  • service:客户端用于连接到目标应用程序的 root 服务
  • backends:命名空间内的服务,具有自己的选择器、端点和配置(我们可以将这些服务看成服务)。
    service:与可以处理请求的 Pod 关联地具体服务的名称。
    weight:它与分配给服务的总流量的百分比相关。

现在我们来应用上面的资源对象:

$ kubectl apply -f web-svc-ts.yamltrafficsplit.split.smi-spec.io/web-svc-ts created$ kubectl get trafficsplit -n emojivotoNAME         SERVICEweb-svc-ts   web-apex

创建完成后我们可以通过 linkerd viz stat 命令的一个 trafficsplit 子命令(可以缩写为 ts),来显示所有的流量拆分统计信息。

$ linkerd viz stat ts -n emojivotoStarting in 2.12, the SMI extension will be required for traffic splitting. Please follow the SMI extension getting started guide from https://linkerd.io/2.10/tasks/linkerd-smi
NAME         APEX       LEAF        WEIGHT   SUCCESS   RPS   LATENCY_P50   LATENCY_P95   LATENCY_P99web-svc-ts   web-apex   web-svc        500         -     -             -             -             -web-svc-ts   web-apex   web-svc-2      500         -     -             -             -             -

由于投票机器人配置为将流量发送到 web-svc.emojivoto:80,所以现在我们看不到任何流量拆分的指标。所以我们先更新下 vote-bot 服务将流量发送到 web-apex 服务,而不是 web-svc。以下 kubectl 命令中使用的文件更改了 vote-bot 部署中的 WEB_HOST 环境变量,以将流量发送到 web-apex 服务,从而使 TrafficSplit 配置生效。

$ kubectl edit deploy vote-bot -n emojivoto# ......    spec:      containers:      - command:        - emojivoto-vote-bot        env:        - name: WEB_HOST          value: web-apex.emojivoto:80        image: docker.l5d.io/buoyantio/emojivoto-web:v11        imagePullPolicy: IfNotPresent        name: vote-bot# ......

更新后新的 vote-bot 服务将向 web-apex 服务发出请求,我们可以使用上面的 trafficsplit 子命令再次来验证:

$ linkerd viz stat ts -n emojivotoStarting in 2.12, the SMI extension will be required for traffic splitting. Please follow the SMI extension getting started guide from https://linkerd.io/2.10/tasks/linkerd-smi
NAME         APEX       LEAF        WEIGHT   SUCCESS      RPS   LATENCY_P50   LATENCY_P95   LATENCY_P99web-svc-ts   web-apex   web-svc        500    90.32%   1.0rps           3ms           9ms          10msweb-svc-ts   web-apex   web-svc-2      500    96.49%   0.9rps           1ms           5ms           5ms

从上面的输出可以看到 web-apex 服务是 web-svcweb-svc-2 服务的 APEX 服务,它们自己则是 LEAF 服务,输出结果还显示了每个服务的权重分布。

调整权重

接着我们再用 linkerd viz stat 命令来查看下应用的流量情况,上一次我们查看的时候 web-svc-2 服务关联的 Pod 没有收到任何流量。

$ linkerd viz stat pod -n emojivotoNAME                         STATUS   MESHED   SUCCESS      RPS   LATENCY_P50   LATENCY_P95   LATENCY_P99   TCP_CONNemoji-696d9d8f95-5vn9w      Running      1/1   100.00%   2.3rps           1ms           2ms           3ms          5vote-bot-646b9fd6fd-js526   Running      1/1   100.00%   0.3rps           1ms           1ms           1ms          1voting-ff4c54b8d-xhjv7      Running      1/1    89.74%   1.3rps           1ms           7ms           9ms          5web-5f86686c4d-58p7k        Running      1/1    97.33%   1.2rps           2ms           9ms          10ms          3web-svc-2-f9d77474f-hgsg4   Running      1/1    92.31%   1.3rps           1ms           6ms           9ms          3

这次我们可以看到与 web-svcweb-svc-2 相关的两个 Pod 都在处理请求了。证明我们的流量拆分配置是正确的。

TrafficSplit 定义中将每个服务的权重设置为 500,以平均分配流量。在实际工作中,我们可以先将 web-svc-2 的权重设置为 1%的或者很低的权重开始,以确保没有错误,然后当我们确定新版本没有问题后,可以调整慢慢调整每个服务的权重,到最终所有流量都切换到新版本上面去。

我们可以通过手动编辑 TrafficSplit 对象来手动调整这两个服务的权重。将 75% 的流量发送到 web-svc-2,将 25% 的流量发送到 web-svc

# web-svc-ts-2.ymlapiVersion: split.smi-spec.io/v1alpha2kind: TrafficSplitmetadata:  name: web-svc-ts  namespace: emojivotospec:  # 客户端用于连接到目标应用程序的 root 服务  service: web-apex  # 命名空间中的 Service 以及它们自己的 selectors、端点和配置。  backends: # 拆分的后端服务    - service: web-svc      weight: 250 # 权重    - service: web-svc-2      weight: 750

更新上面的资源对象后,再次查看流量拆分的情况。

$ linkerd viz stat ts -n emojivotoStarting in 2.12, the SMI extension will be required for traffic splitting. Please follow the SMI extension getting started guide from https://linkerd.io/2.10/tasks/linkerd-smi
NAME         APEX       LEAF        WEIGHT   SUCCESS      RPS   LATENCY_P50   LATENCY_P95   LATENCY_P99web-svc-ts   web-apex   web-svc        250    87.88%   0.6rps           6ms          17ms          20msweb-svc-ts   web-apex   web-svc-2      750    94.12%   1.4rps           2ms           8ms          10ms

在输出中,你将看到 WEIGHT 列与上面的变更相匹配,web-svc-2 为 750、web-svc 为 250。接着我们再更改 TrafficSplit 对象,将所有流量发送到 web-svc-2 服务去。

# web-svc-ts-3.ymlapiVersion: split.smi-spec.io/v1alpha2kind: TrafficSplitmetadata:  name: web-svc-ts  namespace: emojivotospec:  # 客户端用于连接到目标应用程序的 root 服务  service: web-apex  # 命名空间中的 Service 以及它们自己的 selectors、端点和配置。  backends: # 拆分的后端服务    - service: web-svc      weight: 0 # 权重    - service: web-svc-2      weight: 1

更新后我们再次查看流量拆分情况:

$ linkerd viz stat ts -n emojivotoStarting in 2.12, the SMI extension will be required for traffic splitting. Please follow the SMI extension getting started guide from https://linkerd.io/2.10/tasks/linkerd-smi
NAME         APEX       LEAF        WEIGHT   SUCCESS      RPS   LATENCY_P50   LATENCY_P95   LATENCY_P99web-svc-ts   web-apex   web-svc          0         -        -             -             -             -web-svc-ts   web-apex   web-svc-2        1    99.15%   1.9rps           2ms           3ms           3ms

正常我们可以看到 web-svc-2 服务对应的 WEIGHT 为 1,而 web-svc 为 0。

到这里我们就了解了 Linkerd 中的流量拆分的使用,为了简单起见,我们这里使用的是一个单独的 web-apex 服务,当然 apex 服务也可以是后端之一的服务,apex 和后端之一具有相同服务的 TrafficSplit 会将发往该服务的流量发送到该服务,但会与其余后端服务成比例,这是可以动态完成的,允许你在现有服务之上插入一个 TrafficSplit

例如,我们可以简单地使用 web-svc 作为 apex(并继续使用它以及 web-svc-2 作为后端),而不是使用 web-apex。在创建 TrafficSplit 的那一刻,到 web-svc 的现有流量将遵循 TrafficSplit 的规则;并且在它被删除的那一刻,到 web-svc 的流量将恢复正常。

在实践中我们往往还会将 Linkerd 的流量拆分功能与 CI/CD 系统进行集成,以自动化发布过程,Linkerd 本身就提供了相关指标,这结合起来是不是就可以实现渐进式交付了:通过将指标和流量拆分捆绑在一起,可以以增量、安全和完全自动化的方式发布新代码,前面我们介绍过 Argo Rollouts,我们也可以使用像 https://flagger.app/ 这样的项目,因为它是建立在 Linkerd 的指标和流量拆分功能之上来执行渐进式交付的。


相关实践学习
通过Ingress进行灰度发布
本场景您将运行一个简单的应用,部署一个新的应用用于新的发布,并通过Ingress能力实现灰度发布。
容器应用与集群管理
欢迎来到《容器应用与集群管理》课程,本课程是“云原生容器Clouder认证“系列中的第二阶段。课程将向您介绍与容器集群相关的概念和技术,这些概念和技术可以帮助您了解阿里云容器服务ACK/ACK Serverless的使用。同时,本课程也会向您介绍可以采取的工具、方法和可操作步骤,以帮助您了解如何基于容器服务ACK Serverless构建和管理企业级应用。 学习完本课程后,您将能够: 掌握容器集群、容器编排的基本概念 掌握Kubernetes的基础概念及核心思想 掌握阿里云容器服务ACK/ACK Serverless概念及使用方法 基于容器服务ACK Serverless搭建和管理企业级网站应用
相关文章
|
7月前
|
Cloud Native 容器 Kubernetes
基于阿里云服务网格流量泳道的全链路流量管理(三):无侵入式的宽松模式泳道
本文简要讨论了使用流量泳道来实现全链路流量灰度管理的场景与方案,并回顾了阿里云服务网格 ASM 提供的严格与宽松两种模式的流量泳道、以及这两种模式各自的优势与挑战。接下来介绍了一种基于 OpenTelemetry 社区提出的 baggage 透传能力实现的无侵入式的宽松模式泳道,这种类型的流量泳道同时具有对业务代码侵入性低、同时保持宽松模式的灵活特性的特点。同时,我们还介绍了新的基于权重的流量引流策略,这种策略可以基于统一的流量匹配规则,将匹配到的流量以设定好的比例分发到不同的流量泳道。
73542 16
基于阿里云服务网格流量泳道的全链路流量管理(三):无侵入式的宽松模式泳道
|
8月前
|
负载均衡 算法 微服务
常见的微服务流量治理策略
常见的微服务流量治理策略
510 3
|
数据采集 缓存 监控
百亿流量微服务网关的设计与实现(6)
百亿流量微服务网关的设计与实现(6)
453 0
百亿流量微服务网关的设计与实现(6)
|
Web App开发 Cloud Native 数据安全/隐私保护
基于云原生网关实现微服务的多版本线上灰度
一起体验云原生网关开箱即用的微服务多样灰度能力,支持容器服务、Nacos、ZooKeeper、Edas等多种服务发现方式。
|
缓存 负载均衡 监控
近期业务大量突增微服务性能优化总结-1.改进客户端负载均衡算法
近期业务大量突增微服务性能优化总结-1.改进客户端负载均衡算法
|
8月前
|
Java Go 数据库
OpenSergo/MSE & CloudWeGo 共同保障微服务运行时流量稳定性
微服务运行时稳定性的问题微服务的稳定性一直是开发者非常关注的话题。随着业务从单体架构向分布式架构演进以及部署方式的变化,服务之间的依赖关系变得越来越复杂,业务系统也面临着巨大的高可用挑战。大家可能都经历过以下的场景:演唱会抢票瞬间洪峰流量导致系统超出最大负载,load 飙高,用户无法正常下单;在线选...
180 0
OpenSergo/MSE & CloudWeGo 共同保障微服务运行时流量稳定性
|
负载均衡 测试技术 微服务
分布式中灰度方案实践
将版本的分支号加载到服务的元数据信息中,再结合服务名称或者IP地址,来实现对服务列表的多维度过滤,可以支撑大部分轻量级灰度策略的实现。
570 0
分布式中灰度方案实践
|
Kubernetes Cloud Native 前端开发
构建基于 Ingress 的全链路灰度能力
微服务架构下,有一些需求开发,涉及到微服务调用链路上的多个微服务同时发生了改动,通常每个微服务都会有灰度环境或分组来接受灰度流量,我们希望通过进入上游灰度环境的流量,也能进入下游灰度的环境中,确保1个请求始终在灰度环境中传递,即使这个调用链路上有一些微服务没有灰度环境,这些应用请求下游的时候依然能够回到灰度环境中。通过 MSE 提供的全链路灰度能力,可以在不需要修改任何您的业务代码的情况下,能够轻松实现上述能力。
构建基于 Ingress 的全链路灰度能力
|
8月前
|
Kubernetes Java 开发者
OpenSergo 流量路由:从场景到标准化的探索
本文从常见的微服务治理场景出发,从流量路由这个场景入手。先是根据流量路由的实践设计流量路由的 Spec,同时在 Spring Cloud Alibaba 中实践遵循 OpenSergo 标准的流量路由能力。
OpenSergo 流量路由:从场景到标准化的探索
|
Kubernetes 关系型数据库 微服务
解决微服务架构下流量有损问题的实践和探索
绝⼤多数的软件应⽤⽣产安全事故发⽣在应⽤上下线发布阶段,尽管通过遵守业界约定俗成的可灰度、可观测和可滚回的安全⽣产三板斧,可以最⼤限度的规避发布过程中由于应⽤⾃身代码问题对⽤户造成的影响。但对于⾼并发⼤流量情况下的短时间流量有损问题却仍然⽆法解决。因此,本文将围绕发布过程中如何解决流量有损问题实现应⽤发布过程中的⽆损上下线效果相关内容展开⽅案介绍。
解决微服务架构下流量有损问题的实践和探索