如何在Service Mesh微服务架构中实现金丝雀发布?

开发 架构
在介绍Kubernetes中的金丝雀(灰度)发布之前,先来了解下Kubernetes中最重要的应用部署方式——“滚动升级”。

[[427635]]

本文转载自微信公众号「无敌码农」,作者无敌码农。转载本文请联系无敌码农公众号。

今天的文章继续聊聊有关Service Mesh微服务架构的话题,如果对之前的聊过的话题还不了解,可以参考文末的推荐阅读。今天要聊的话题是:如何在Service Mesh微服务架构中实现“金丝雀发布”?

什么是金丝雀发布

既然要聊具体的实现,那么在开始之前,先科普下什么是“金丝雀发布”。金丝雀发布也叫“灰度发布”,具体来说就是在发布线上版本时,先将少量的生产流量打到服务的新版本,以验证新版本的准确性和可靠性,待发布的新版本得到线上流量的全面验证后,在逐步将所有流量放入新版本,以实现生产服务版本的稳定更新。

为什么叫金丝雀发布呢,是因为金丝雀对矿场中的毒气比较敏感,所以在矿场开工前工人们会放一只金丝雀进去,以验证矿场是否存在毒气,这便是金丝雀发布名称的由来。

在不同技术栈场景中,金丝雀发布的实现方式也不尽相同:有通过nginx实现的、也有借助A/B测试实现的。而随着以Kubernetes为代表的云原生基础设施的普及,金丝雀发布作为一项基本的服务发布功能,其实现方式也有了一些新的趋势——那就是逐步与云原生基础设施融为一体,成为基础设施服务的一部分。

Kubernetes中的金丝雀(灰度)发布

接下来,先看看在Kubernetes中是如何实现版本更新的。以下内容假设你已经有了一套可用的Kubernetes环境,如果没有可以查看文末推荐阅读的文章链接,参考相关分享自行部署。

1.滚动更新

在介绍Kubernetes中的金丝雀(灰度)发布之前,先来了解下Kubernetes中最重要的应用部署方式——“滚动升级”。

所谓“滚动升级”:是指当更新了Kubernetes中Deployment编排资源的Pod模版(例如更新镜像版本号)之后,Deployment就需要遵循一种叫做“滚动更新(rolling update)”的方式,来升级现有的容器,从而实现应用对外服务的“不中断更新部署”。Kubernetes实现“滚动升级”的示意图如下:

如上图所示,滚动升级的过程为:

1)当容器开始升级时,集群中会先启动一个新版本的Pod,并终止一个旧版本的Pod。

2)如果此时,新版本的Pod有问题启动不了,那么“滚动升级”就会停止,并允许开发和运维人员介入。而在这个过程中,由于应用本身还有两个旧版本的Pod在线,所以服务并不会受到太大的影响。

3)而如果新版本的Pod启动成功,且服务访问正常,则继续滚动升级,直至按照Deployment编排器设置的副本数量,完成后续旧版本Pod的升级。

在Kubernetes中Deployment还可以通过相应地“滚动升级”策略,来控制Pod的滚动升级行为,以进一步保证服务的连续性。例如:“在任何时间窗口内,只有指定比例的Pod处于离线状态;在任何时间窗口内,只有指定比例的新Pod被创建出来"。可以通过相应地控制参数进行设置,如下:

  1. ... 
  2. spec: 
  3.   selector: 
  4.     matchLabels: 
  5.       app: micro-api 
  6.   replicas: 3 
  7.   #设置滚动升级策略 
  8.   #Kubernetes在等待设置的时间后才开始进行升级,例如5秒 
  9.   minReadySeconds: 5 
  10.   strategy: 
  11.     type: RollingUpdate 
  12.     rollingUpdate: 
  13.       #升级过程中最多可以比原先设置多出的Pod数量 
  14.       maxSurge: 1 
  15.       #升级过程中Deployment控制器最多可以删除多少个旧Pod,主要用于提供缓冲时间 
  16.       maxUnavailable: 1 
  17. ... 

在上面RollingUpdate Strategy(滚动升级策略)的配置中:

  • maxSurge:指定的是,除了设定的Pod副本数量之外,在一次“滚动”中,Deployment控制器还可以创建多少个新的Pod。
  • maxUnavailable:指的是,在一次“滚动”中,Deployment控制器可以删除多少个旧Pod。

通过这种精确的“滚动升级”策略,可以使得Kubernetes服务版本发布的过程更加平滑。此外,这两个配置还可以通过百分比的方式来表示,比如“maxUnavailable=50%”,指的是Deployment控制器最多可以一次删除“50%*设定Pod副本数”个Pod。

接下来具体演示下在Kubernetes中进行服务滚动升级的详细过程。

使用的示例代码说明:

本文及本公众号之前或之后与Service Mesh(服务网格、Istio)技术相关的分享,均使用《干货|如何步入Service Mesh微服务架构时代》、《实战|Service Mesh微服务架构实现服务间gRPC通信》这两篇文章所展示的项目。

该项目以Spring Boot编写的Java服务为主,在体验上更接近真实的项目开发场景。项目的结构如下:

该项目所在的GitHub地址为:

https://github.com/manongwudi/istio-micro-service-demo

“滚动升级”演示:

这里先借助示例项目中的“micro-api”服务来演示其在Kubernetes中进行“滚动升级”的过程,步骤如下:

(1)首先准备“micro-api”服务的k8s发布文件(如:micro-api.yaml)。代码如下:

  1. apiVersion: v1 
  2. kind: Service 
  3. metadata: 
  4.   name: micro-api 
  5. spec: 
  6.   type: ClusterIP 
  7.   ports: 
  8.     - name: http 
  9.       port: 19090 
  10.       targetPort: 9090 
  11.   selector: 
  12.     app: micro-api 
  13.  
  14. --- 
  15. apiVersion: apps/v1 
  16. kind: Deployment 
  17. metadata: 
  18.   name: micro-api 
  19. spec: 
  20.   selector: 
  21.     matchLabels: 
  22.       app: micro-api 
  23.   replicas: 3 
  24.   #设置滚动升级策略 
  25.   #Kubernetes在等待设置的时间后才开始进行升级,例如5秒 
  26.   minReadySeconds: 5 
  27.   strategy: 
  28.     type: RollingUpdate 
  29.     rollingUpdate: 
  30.       #升级过程中最多可以比原先设置多出的Pod数量 
  31.       maxSurge: 1 
  32.       #升级过程中Deployment控制器最多可以删除多少个旧Pod 
  33.       maxUnavailable: 1 
  34.   template: 
  35.     metadata: 
  36.       labels: 
  37.         app: micro-api 
  38.     spec: 
  39.       #设置的阿里云私有镜像仓库登陆信息的secret(对应2.1.2的设置) 
  40.       imagePullSecrets: 
  41.         - name: regcred 
  42.       containers: 
  43.         - name: micro-api 
  44.           image: registry.cn-hangzhou.aliyuncs.com/wudimanong/micro-api:1.0-SNAPSHOT 
  45.           imagePullPolicy: Always 
  46.           tty: true 
  47.           ports: 
  48.             - name: http 
  49.               protocol: TCP 
  50.               containerPort: 19090 

上述部署文件设置了“micro-api”服务的Pod副本个数为“3”,并且设置了相应地滚动升级策略。

(2)接下来执行k8s部署命令如下:

  1. $ kubectl apply -f micro-api.yaml  

成功后,查看Deployment创建后的状态信息,命令效果如下:

  1. $ kubectl get deployments 
  2. NAME          READY   UP-TO-DATE   AVAILABLE   AGE 
  3. micro-api     3/3     3            3           190d 

从上述命令的返回结果中,可以看到三个状态字段,它们的含义如下所示:

  • READY:表示用户期望的Pod副本个数,以及当前处于Running状态的Pod个数。
  • UP-TO-DATE:当前处于最新版本的Pod个数。所谓最新版本,指的是Pod的Spec部分与Deployment中Pod模版里定义的完全一致。
  • AVAILABLE:当前已经可用的Pod的个数——既是Running状态,又是最新版本,并且已经处于Ready(监控检查正确)状态的Pod个数。

(3)模拟服务版本升级,触发滚动升级。

接下来重新构建“micro-api”服务的版本,并将其上传至私有镜像仓库。之后,通过命令修改“micro-api”的Deployment所使用的镜像,并触发滚动升级。

修改Deployment所使用的镜像的命令如下:

  1. $ kubectl set image deployment/micro-api micro-api=registry.cn-hangzhou.aliyuncs.com/wudimanong/micro-api:1.1-SNAPSHOT 
  2. deployment.apps/micro-api image updated 

这里使用了“kubectl set image”指令,主要是为了方便操作,也可以直接在k8s部署文件中进行镜像版本的修改。

修改完Deployment的镜像版本后,Kubernetes会立即触发“滚动升级”的过程。可以通过“kubectl rollout status”指令来查看Deployment资源的状态变化。具体如下:

  1. $ kubectl rollout status deployment/micro-api 
  2.  
  3. Waiting for deployment "micro-api" rollout to finish: 2 out of 3 new replicas have been updated... 
  4. Waiting for deployment "micro-api" rollout to finish: 2 out of 3 new replicas have been updated... 
  5.  
  6. Waiting for deployment "micro-api" rollout to finish: 2 out of 3 new replicas have been updated... 
  7. Waiting for deployment "micro-api" rollout to finish: 2 of 3 updated replicas are available... 
  8. Waiting for deployment "micro-api" rollout to finish: 2 of 3 updated replicas are available... 
  9. deployment "micro-api" successfully rolled out 

这时,也可以通过查看Deployment的Events,看到这个“滚动升级”的过程。具体如下:

  1. $ kubectl describe deployment micro-api 
  2. ... 
  3. OldReplicaSets:  <none> 
  4. NewReplicaSet:   micro-api-d745d8649 (3/3 replicas created) 
  5. Events: 
  6.   Type    Reason             Age   From                   Message 
  7.   ----    ------             ----  ----                   ------- 
  8.   Normal  ScalingReplicaSet  12m   deployment-controller  Scaled up replica set micro-api-677dd4d5b6 to 1 
  9.   Normal  ScalingReplicaSet  12m   deployment-controller  Scaled down replica set micro-api-57c7cb5b74 to 2 
  10.   Normal  ScalingReplicaSet  12m   deployment-controller  Scaled up replica set micro-api-677dd4d5b6 to 2 
  11.   Normal  ScalingReplicaSet  5m1s  deployment-controller  Scaled down replica set micro-api-677dd4d5b6 to 0 
  12.   Normal  ScalingReplicaSet  5m    deployment-controller  Scaled up replica set micro-api-d745d8649 to 2 
  13.   Normal  ScalingReplicaSet  56s   deployment-controller  Scaled down replica set micro-api-57c7cb5b74 to 0 
  14.   Normal  ScalingReplicaSet  56s   deployment-controller  Scaled up replica set micro-api-d745d8649 to 3 

可以看到,当你修改了Deployment里的Pod定义后,"Deployment Controller"会使用这个修改后的Pod模版,创建一个新的ReplicaSet,这个新的ReplicaSet的初始Pod副本数是:0。

然后在Age=12 m的位置,开始将这个新的ReplicaSet所控制的Pod副本数从0个变成1个。

紧接着,在Age=12 m的位置,又将旧ReplicaSet所控制的Pod副本数减少1个,即“水平收缩”成两个副本。

如此交替进行,新ReplicaSet所管理的Pod的副本数,从0个变成1个,再变成2个,最后变成3个;而旧ReplicaSet所管理的Pod的副本数则从3个变成2个,最后变成0个。

这样,就完成了一组Pod的版本升级过程。而像这样将一个Kubernetes集群中正在运行的多个Pod版本,交替逐一升级的过程,就是“滚动升级”。

2.金丝雀(灰度)发布

前面“1.”小标题中,比较详细的演示了Kubernetes的“滚动升级”的方式,虽然通过滚动升级的方式可以方便、平滑的实现版本更新,但是这个过程,并没有灰度功能。滚动升级的方式,虽然中间有缓冲交替的过程,但这种过程是自动的、迅速的,滚动升级过程结束就相当于直接进行了新版本的全量发布。

而对于需要进行金丝雀(灰度)发布的场景,“滚动升级”的方式很显然是不够用的。那么,在Kubernetes中应该如何结合版本更新做到金丝雀(灰度)发布呢?

具体步骤如下:

(1)编写实现新版本灰度发布的部署文件。

为了实现在Kubernetes中的金丝雀(灰度)发布过程的可观测,我们重新定义下具体的k8s发布文件(如:micro-api-canary.yaml)的内容如下:

  1. apiVersion: apps/v1 
  2. kind: Deployment 
  3. metadata: 
  4.   name: micro-api 
  5. spec: 
  6.   selector: 
  7.     matchLabels: 
  8.       app: micro-api 
  9.   replicas: 3 
  10.   #设置滚动升级策略 
  11.   #Kubernetes在等待设置的时间后才开始进行升级,例如5秒 
  12.   minReadySeconds: 5 
  13.   strategy: 
  14.     type: RollingUpdate 
  15.     rollingUpdate: 
  16.       #升级过程中最多可以比原先设置多出的Pod数量 
  17.       maxSurge: 1 
  18.       #升级过程中Deployment控制器最多可以删除多少个旧Pod,主要用于提供缓冲时间 
  19.       maxUnavailable: 1 
  20.   template: 
  21.     metadata: 
  22.       labels: 
  23.         app: micro-api 
  24.         #增加新的标签(演示k8s的灰度发布) 
  25.         track: canary 
  26.     spec: 
  27.       #设置的阿里云私有镜像仓库登陆信息的secret(对应2.1.2的设置) 
  28.       imagePullSecrets: 
  29.         - name: regcred 
  30.       containers: 
  31.         - name: micro-api 
  32.           image: registry.cn-hangzhou.aliyuncs.com/wudimanong/micro-api:1.3-SNAPSHOT 
  33.           imagePullPolicy: Always 
  34.           tty: true 
  35.           ports: 
  36.             - name: http 
  37.               protocol: TCP 
  38.               containerPort: 19090 

上述发布文件与“1.”小标题中演示滚动升级时,发布文件的内容一致,只是为了方便观察灰度发布过程的实现,这里通过“track: canary”对新发布的Pod版本进行标记。

设置新版本的镜像为:“micro-api:1.3-SNAPSHOT”。并且通过“spec.selector.matchLabels.app:micro-api”与历史版本Pod所对应的Service(micro-api.yaml文件中定义的Service)资源定义匹配。

(2)执行"滚动升级"发布命令,实现“灰度发布”效果。

  1. $ kubectl apply -f micro-api-canary.yaml && kubectl rollout pause deployment/micro-api 

上面通过"kubectl rollout pause"命令实现对Deployment的金丝雀(灰度发布)。执行发布命令之后的运行效果如下:

  1. $ kubectl get pods --show-labels -o wide 
  2. NAME                         READY   STATUS    RESTARTS   AGE     IP          NODE         NOMINATED NODE   READINESS GATES   LABELS 
  3. micro-api-57c7cb5b74-mq7m9   1/1     Running   0          6m20s   10.32.0.3   kubernetes   <none>           <none>            app=micro-api,pod-template-hash=57c7cb5b74 
  4. micro-api-57c7cb5b74-ptptj   1/1     Running   0          6m20s   10.32.0.4   kubernetes   <none>           <none>            app=micro-api,pod-template-hash=57c7cb5b74 
  5. micro-api-7dbb6c5d66-4rbdc   1/1     Running   0          5m33s   10.32.0.6   kubernetes   <none>           <none>            app=micro-api,pod-template-hash=7dbb6c5d66,track=canary 
  6. micro-api-7dbb6c5d66-cfk9l   1/1     Running   0          5m33s   10.32.0.5   kubernetes   <none>           <none>            app=micro-api,pod-template-hash=7dbb6c5d66,track=canary 

查看Deployment的滚动升级情况,命令如下:

  1. $ kubectl get deployments 
  2. NAME            READY   UP-TO-DATE   AVAILABLE   AGE 
  3. micro-api       4/3     2            4           194d 

可以看到此时“micro-api” ready的数量为4,其中两个旧版本Pod,两个新版本Pod。

(3)接下来进行流量测试。

查询两组Pod版本所对应的Service资源的IP,命令如下:

  1. # kubectl get svc micro-api 
  2. NAME        TYPE        CLUSTER-IP       EXTERNAL-IP   PORT(S)     AGE 
  3. micro-api   ClusterIP   10.110.169.161   <none>        19090/TCP   194d 

接下来,模拟对服务的接口进行批量访问,命令如下:

  1. for i in {1..10}; do curl 10.110.169.161:19090/test/test; done 
  2.  
  3. {"code":0,"data":"V3|无依赖测试接口返回->OK!","message":"成功"
  4. {"code":0,"data":"V3|无依赖测试接口返回->OK!","message":"成功"
  5. {"code":0,"data":"无依赖测试接口返回->OK!","message":"成功"
  6. {"code":0,"data":"无依赖测试接口返回->OK!","message":"成功"
  7. {"code":0,"data":"V3|无依赖测试接口返回->OK!","message":"成功"
  8. {"code":0,"data":"V3|无依赖测试接口返回->OK!","message":"成功"
  9. {"code":0,"data":"无依赖测试接口返回->OK!","message":"成功"
  10. {"code":0,"data":"无依赖测试接口返回->OK!","message":"成功"
  11. {"code":0,"data":"V3|无依赖测试接口返回->OK!","message":"成功"
  12. {"code":0,"data":"V3|无依赖测试接口返回->OK!","message":"成功"

可以看到,此时流量会随机的流向旧版本和新版本(日志标记为V3)的服务。

(4)将服务版本升级为新版本。

如果新版本的服务经过线上流量测试验证没有问题,则可以通过"rollout resume"命令将整体服务的版本升级为新版本。命令如下:

  1. $ kubectl rollout resume deployment micro-api 
  2. deployment.apps/micro-api resumed 

升级后的效果如下:

  1. $ kubectl get pods --show-labels -o wide 
  2. NAME                         READY   STATUS    RESTARTS   AGE   IP          NODE         NOMINATED NODE   READINESS GATES   LABELS 
  3. micro-api-7dbb6c5d66-4rbdc   1/1     Running   0          18m   10.32.0.6   kubernetes   <none>           <none>            app=micro-api,pod-template-hash=7dbb6c5d66,track=canary 
  4. micro-api-7dbb6c5d66-bpjtg   1/1     Running   0          84s   10.32.0.3   kubernetes   <none>           <none>            app=micro-api,pod-template-hash=7dbb6c5d66,track=canary 
  5. micro-api-7dbb6c5d66-cfk9l   1/1     Running   0          18m   10.32.0.5   kubernetes   <none>           <none>            app=micro-api,pod-template-hash=7dbb6c5d66,track=canary 

可以看到,此时目标服务已经通过“滚动升级”的方式完成了全量更新。而如果存在问题,则通过“kubectl rollout undo”命令进行回滚即可!

从上述过程可以看到,Kubernetes中的金丝雀(灰度发布)主要是通过操纵(如:pause)“滚动升级”的过程来实现的——通过发布一定数量的新版本Pod,并利用Service资源类型本身的负载均衡能力来实现流量在新/旧Pod之间的随机交替。

这样的方式虽然已经可以满足一些简单的场景,但是没有办法做到更精准的灰度流量控制。这时候就需要借助 Service Mesh 中的解决方案了,下面我们来看看在 Istio 中如何做到精准流量的金丝雀(灰度)发布。

Istio中的金丝雀(灰度)发布

以下内容默认你已经在Kubernetes中安装了Istio环境,如果还没有安装可以参考《干货|如何步入Service Mesh微服务架构时代》中分享的内容。

Istio与Kubernetes实现金丝雀(灰度)发布的方式不一样,Istio通过Envoy(SideCar)强大的路由规则管理能力,可以非常灵活地控制对应版本的流量占比,从而实现具备精准流量控制能力的金丝雀(灰度)发布功能。

Istio通过Envoy(SideCar)实现金丝雀(灰度)发布的流量路由示意图如下(继续以“micro-api”服务为例):

从上图中可以大致看出,Istio具备强大的流量管理能力,而这种能力对于实现流量精准控制的金丝雀(灰度)发布功能来说,自然是水到渠成的。

具体来说,在Istio中是通过VirtualService(虚拟服务)这种特定的资源在服务网格中实现流量路由的。通过VirtualService可以方便地定义流量路由规则,并在客户端试图连接到服务时应用这些规则,并最终到达目标服务。

接下来,具体演示如何在Istio中通过VirtualService实现金丝雀(灰度)发布。步骤如下:

(1)首先发布一个v1版本的服务。

要在Istio中实现更精准的版本控制,需要在发布Pod资源时,通过明确的“版本标签”进行指定。准备“micro-api”服务v1版本的k8s部署文件(micro-api-canary-istio-v1.yaml):

  1. apiVersion: v1 
  2. kind: Service 
  3. metadata: 
  4.   name: micro-api 
  5. spec: 
  6.   type: ClusterIP 
  7.   ports: 
  8.     - name: http 
  9.       port: 19090 
  10.       targetPort: 9090 
  11.   selector: 
  12.     app: micro-api 
  13.  
  14. --- 
  15.  
  16. apiVersion: apps/v1 
  17. kind: Deployment 
  18. meta data: 
  19.   name: micro-api-v1 
  20. spec: 
  21.   selector: 
  22.     matchLabels: 
  23.       app: micro-api 
  24.       #这里是关键,需要设置版本标签,以便实现灰度发布 
  25.       version: v1 
  26.   replicas: 3 
  27.   #设置滚动升级策略 
  28.   #Kubernetes在等待设置的时间后才开始进行升级,例如5秒 
  29.   minReadySeconds: 5 
  30.   strategy: 
  31.     type: RollingUpdate 
  32.     rollingUpdate: 
  33.       #升级过程中最多可以比原先设置多出的Pod数量 
  34.       maxSurge: 1 
  35.       #升级过程中Deployment控制器最多可以删除多少个旧Pod,主要用于提供缓冲时间 
  36.       maxUnavailable: 1 
  37.   template: 
  38.     metadata: 
  39.       labels: 
  40.         app: micro-api 
  41.         #设置版本标签,便于灰度发布 
  42.         version: v1 
  43.     spec: 
  44.       #设置的阿里云私有镜像仓库登陆信息的secret 
  45.       imagePullSecrets: 
  46.         - name: regcred 
  47.       containers: 
  48.         - name: micro-api 
  49.           image: registry.cn-hangzhou.aliyuncs.com/wudimanong/micro-api:1.1-SNAPSHOT 
  50.           imagePullPolicy: Always 
  51.           tty: true 
  52.           ports: 
  53.             - name: http 
  54.               protocol: TCP 
  55.               containerPort: 19090 

“spec.selector.matchLabels.version:v1”标签用来标注服务的版本,该标签是后续Istio的流量管理规则中,识别服务版本的主要依据。

准备好发布文件后,执行发布命令:

  1. $ kubectl apply -f micro-api-canary-istio-v1.yaml 

此时,一个低版本的服务就运行成功了!接下来我们模拟对其实施金丝雀(灰度)发布。

(2)发布一个v2版本的服务(升级的目标版本)。

与v1版本服务一样,发布的v2版本的服务也需要明确版本标签,其发布文件(micro-api-canary-istio-v2.yaml)的内容如下:

  1. apiVersion: apps/v1 
  2. kind: Deployment 
  3. metadata: 
  4.   name: micro-api-v2 
  5. spec: 
  6.   selector: 
  7.     matchLabels: 
  8.       app: micro-api 
  9.       #设置好版本标签,便于灰度发布 
  10.       version: v2 
  11.   replicas: 3 
  12.   #设置滚动升级策略 
  13.   #Kubernetes在等待设置的时间后才开始进行升级,例如5秒 
  14.   minReadySeconds: 5 
  15.   strategy: 
  16.     type: RollingUpdate 
  17.     rollingUpdate: 
  18.       #升级过程中最多可以比原先设置多出的Pod数量 
  19.       maxSurge: 1 
  20.       #升级过程中Deployment控制器最多可以删除多少个旧Pod,主要用于提供缓冲时间 
  21.       maxUnavailable: 1 
  22.   template: 
  23.     metadata: 
  24.       labels: 
  25.         app: micro-api 
  26.         #设置好版本标签,便于灰度发布 
  27.         version: v2 
  28.     spec: 
  29.       #设置的阿里云私有镜像仓库登陆信息的secret 
  30.       imagePullSecrets: 
  31.         - name: regcred 
  32.       containers: 
  33.         - name: micro-api 
  34.           image: registry.cn-hangzhou.aliyuncs.com/wudimanong/micro-api:1.3-SNAPSHOT 
  35.           imagePullPolicy: Always 
  36.           tty: true 
  37.           ports: 
  38.             - name: http 
  39.               protocol: TCP 
  40.               containerPort: 19090 

执行发布命令:

  1. $ kubectl apply -f micro-api-canary-istio-v2.yaml  
  2. deployment.apps/micro-api-v2 created 

此时,系统中就存在了两组版本的Pod资源,具体如下:

  1. # kubectl get pods 
  2. NAME                            READY   STATUS    RESTARTS   AGE 
  3. micro-api-v1-565d749dd4-7c66z   1/1     Running   2          13h 
  4. micro-api-v1-565d749dd4-7dqfb   1/1     Running   2          13h 
  5. micro-api-v1-565d749dd4-l62wc   1/1     Running   2          13h 
  6. micro-api-v2-6f98c598c9-5stlw   1/1     Running   0          82s 
  7. micro-api-v2-6f98c598c9-f2ntq   1/1     Running   0          82s 
  8. micro-api-v2-6f98c598c9-l8g4j   1/1     Running   0          82s 

接下来将演示如何利用Istio强大的流量管理功能,来实现流量在这两组版本Pod资源之间的精确控制!

(3)创建Istio网关资源。

在Istio中要实现流量的精确控制,需要将VirtualService绑定到具体的Ingressgateway(入口网关)资源。因此在创建VirtualService资源实现流量路由及控制前,需要创建一个Istio网关。部署文件(micro-gateway.yaml)的内容如下:

  1. apiVersion: networking.istio.io/v1alpha3 
  2. kind: Gateway 
  3. metadata: 
  4.   name: micro-gateway 
  5. spec: 
  6.   selector: 
  7.     istio: ingressgateway 
  8.   servers: 
  9.     - port: 
  10.         number: 80 
  11.         name: http 
  12.         protocol: HTTP 
  13.       hosts: 
  14.         - "*" 

上述部署文件执行后将创建一个名称为“micro-gateway”的Istio网关,并允许所有主机(hosts:"*"指定)通过该网关。

(4)创建Istio虚拟服务资源VirtualService。

前面提到过在Istio中主要是通过VirtualService(虚拟服务)来实现服务网格内的流量路由及控制。接下来我们看看VirtualService资源的具体创建方式,准备资源文件(如virtual-service-all.yaml),内容如下:

  1. apiVersion: networking.istio.io/v1alpha3 
  2. kind: VirtualService 
  3. metadata: 
  4.   name: micro-api-route 
  5. spec: 
  6.   #用于定义流量被发送到的目标主机(这里为部署在k8s中的micro-api服务) 
  7.   hosts: 
  8.     - micro-api.default.svc.cluster.local 
  9.   #将VirtualService绑定到Istio网关,通过网关来暴露路由目标 
  10.   gateways: 
  11.     - micro-gateway 
  12.   http: 
  13.     - route: 
  14.         #设置旧版本(V1)版本的流量占比为70% 
  15.         - destination: 
  16.             host: micro-api.default.svc.cluster.local 
  17.             subset: v1 
  18.           #通过权重值来设置流量占比 
  19.           weight: 70 
  20.         #设置新版本(V2)版本的流量占比为30% 
  21.         - destination: 
  22.             host: micro-api.default.svc.cluster.local 
  23.             subset: v2 
  24.           weight: 30 

如上所示,VirtualService资源具备针对http的精准流量控制能力,可以将指定占比的流量路由到特定的“subset”指定的版本。而为了实现这一能力,VirtualService资源还需要与Istio网关绑定,通过Istio网关来暴露路由目标。

(5)创建Istio目标路由规则资源。

虚拟服务VirtualService在Istio中主要用于控制流量的行为,而定义流量行为的路由规则则需要通过“DestinationRule”路由规则资源来定义。创建路由规则文件(destination-rule-all.yaml),具体内容如下:

  1. apiVersion: networking.istio.io/v1alpha3 
  2. kind: DestinationRule 
  3. metadata: 
  4.   name: micro-api-destination 
  5. spec: 
  6.   #与Deployment资源对应的Service资源名称关联 
  7.   host: micro-api 
  8.   #流量策略设置:负载均衡策略、连接池大小、局部异常检测等,在路由发生后作用于流量 
  9.   trafficPolicy: 
  10.     #限流策略 
  11.     connectionPool: 
  12.       tcp: 
  13.         maxConnections: 10 
  14.       http: 
  15.         http1MaxPendingRequests: 1 
  16.         maxRequestsPerConnection: 1 
  17.     #设置目的地的负债均衡算法 
  18.     loadBalancer: 
  19.       simple: ROUND_ROBIN 
  20.   #目的地指的是不同的子集(subset)或服务版本。通子集(subset),可以识别应用程序的不同版本,以实现流量在不同服务版本之间的切换 
  21.   subsets: 
  22.     - name: v1 
  23.       labels: 
  24.         version: v1 
  25.     - name: v2 
  26.       labels: 
  27.         version: v2 

如上所示,通过subsets属性,定义了VirtualService资源用于路由的具体版本标签匹配信息。至此,针对两个版本服务的灰度流量控制规则就设置好了,接下来测试具体的金丝雀(灰度)发布效果。

(6)测试Istio实现金丝雀(灰度)发布的流量控制效果。

在正式测试之前,可以通过命令查看下当前的部署资源情况:

  1. #查看部署的Deployment资源 
  2. kubectl get deploy  | grep micro-api 
  3.  
  4. micro-api-v1             3/3     3            3           21h 
  5. micro-api-v2             3/3     3            3           8h 
  1. #查看两组版本Pod资源对应的K8s-Service的服务IP 
  2. kubectl get svc micro-api 
  3.  
  4. NAME        TYPE        CLUSTER-IP       EXTERNAL-IP   PORT(S)     AGE 
  5. micro-api   ClusterIP   10.110.169.161   <none>        19090/TCP   205d 
  1. #查看VirtualService资源定义 
  2. kubectl get vs 
  3.  
  4. NAME              GATEWAYS          HOSTS                                   AGE 
  5. micro-api-route   [micro-gateway]   [micro-api.default.svc.cluster.local]   7h34m 
  1. #查看定义的路由规则资源 
  2. kubectl get dr 
  3.  
  4. NAME                    HOST        AGE 
  5. micro-api-destination   micro-api   7h27m 

通过上面的资源信息查看,这里我们已经可以查到Deployments对应的K8s-Service资源的IP,但如果通过K8s-Service资源来进行测试的话,会发现流量的控制并不精准,并不能达到我们设置的70%流量流向v1,30%的流量流向v2(因为这是随机流量)。

因此,要使用Istio的精准流量控制功能,还需要使用Istio的Ingressgateway。查看Istio的Ingressgateway资源IP的命令如下:

  1. #查看ingress的IP 
  2. kubectl get svc -n istio-system | grep ingress 
  3.  
  4. istio-ingressgateway   LoadBalancer   10.98.178.61     <pending>     15021:31310/TCP,80:32113/TCP,443:31647/TCP,31400:30745/TCP,15443:30884/TCP   7h54m 

接下来,通过Ingress的IP来访问“micro-api”服务,命令及效果如下:

  1. for i in {1..10}; do curl -H "Host:micro-api.default.svc.cluster.local" 10.98.178.61:80/test/test; done 
  2.  
  3. {"code":0,"data":"V3|无依赖测试接口返回->OK!","message":"成功"
  4. {"code":0,"data":"无依赖测试接口返回->OK!","message":"成功"
  5. {"code":0,"data":"无依赖测试接口返回->OK!","message":"成功"
  6. {"code":0,"data":"无依赖测试接口返回->OK!","message":"成功"
  7. {"code":0,"data":"无依赖测试接口返回->OK!","message":"成功"
  8. {"code":0,"data":"无依赖测试接口返回->OK!","message":"成功"
  9. {"code":0,"data":"无依赖测试接口返回->OK!","message":"成功"
  10. {"code":0,"data":"无依赖测试接口返回->OK!","message":"成功"
  11. {"code":0,"data":"V3|无依赖测试接口返回->OK!","message":"成功"
  12. {"code":0,"data":"V3|无依赖测试接口返回->OK!","message":"成功"

如上所示,流量按照设定的比例(v1:70%;v2:30%)进行了分流。

(7)测试将流量全部切向新版本。

为了更明显地验证Istio的流量控制效果,接下来,我们通过变更VirtualService资源的流量设置占比,将流量全部切到新版本。变更后的VirtualService资源的配置文件内容如下:

  1. apiVersion: networking.istio.io/v1alpha3 
  2. kind: VirtualService 
  3. metadata: 
  4.   name: micro-api-route 
  5. spec: 
  6.   #用于定义流量被发送到的目标主机(这里为部署在k8s中的micro-api服务) 
  7.   hosts: 
  8.     - micro-api.default.svc.cluster.local 
  9.   #将VirtualService绑定到Istio网关,通过网关来暴露路由目标 
  10.   gateways: 
  11.     - micro-gateway 
  12.   http: 
  13.     - route: 
  14.         #设置旧版本(V1)版本的流量占比为70% 
  15.         - destination: 
  16.             host: micro-api.default.svc.cluster.local 
  17.             subset: v1 
  18.           #通过权重值来设置流量占比 
  19.           weight: 0 
  20.         #设置新版本(V2)版本的流量占比为30% 
  21.         - destination: 
  22.             host: micro-api.default.svc.cluster.local 
  23.             subset: v2 
  24.           weight: 100 

继续通过Istio网关访问目标服务,命令如下:

  1. for i in {1..10}; do curl -H "Host:micro-api.default.svc.cluster.local" 10.98.178.61:80/test/test; done 
  2.  
  3. {"code":0,"data":"V3|无依赖测试接口返回->OK!","message":"成功"
  4. {"code":0,"data":"V3|无依赖测试接口返回->OK!","message":"成功"
  5. {"code":0,"data":"V3|无依赖测试接口返回->OK!","message":"成功"
  6. {"code":0,"data":"V3|无依赖测试接口返回->OK!","message":"成功"
  7. {"code":0,"data":"V3|无依赖测试接口返回->OK!","message":"成功"
  8. {"code":0,"data":"V3|无依赖测试接口返回->OK!","message":"成功"
  9. {"code":0,"data":"V3|无依赖测试接口返回->OK!","message":"成功"
  10. {"code":0,"data":"V3|无依赖测试接口返回->OK!","message":"成功"
  11. {"code":0,"data":"V3|无依赖测试接口返回->OK!","message":"成功"
  12. {"code":0,"data":"V3|无依赖测试接口返回->OK!","message":"成功"

可以观察到,此时流量已经全部切换到了新版本服务!

后记

在微服务时代,不同的服务之间相互联系,关系错综复杂,部署升级一个服务,可能造成整个系统的瘫痪,因此,需要选择合适的部署方式,从而将风险降到最低。金丝雀(灰度)发布只是多种部署方式的一种,还有蓝绿部署、滚动部署(如K8s的滚动升级)等,可以根据不同的业务场景选择不同的发布形式。

 

责任编辑:武晓燕 来源: 无敌码农
相关推荐

2023-10-08 07:34:04

2021-07-13 06:35:11

Argo Rollou GitOpsKubernetes

2022-02-17 13:09:55

金丝雀部署服务集群测试

2021-10-14 18:21:52

架构IstioService

2022-11-30 08:00:00

金丝雀部署IT测试

2021-07-29 05:09:54

Linkerd金丝雀部署Flagger

2021-06-15 05:52:33

Linkerd canary网络技术

2022-08-22 10:40:40

Kubernete部署分析运行

2021-02-28 07:52:24

蠕虫数据金丝雀

2021-06-08 07:04:45

Service Mes微服务熔断

2023-09-05 07:24:33

Traefik加权轮询

2021-06-03 05:48:58

GitOps 云原生Kubernetes

2024-01-18 08:24:08

2021-12-08 17:54:55

架构控制平面

2024-04-01 13:04:01

停机部署滚动部署蓝绿部署

2019-03-20 09:28:42

Service Mes高可用架构

2022-08-15 20:48:28

Chrome安卓网页

2023-11-19 22:59:48

微服务开发

2023-11-09 07:23:57

Istio路由分析

2021-06-12 07:38:21

Linkerd 2.Service Mes微服务
点赞
收藏

51CTO技术栈公众号