K8S的灰度发布、滚动更新、蓝绿发布

K8S灰度发布、蓝绿发布、滚动更新

一、简介

1.1灰度发布(金丝雀发布)

金丝雀发布一般是先发1台机器,或者一个小比例,例如2%的服务器,主要做流量验证用,也称为金丝雀 (Canary) 测试,国内常称灰度测试。以前矿工下矿前,会先放一只金丝雀进去用于探测洞里是否有有毒气体,看金丝雀能否活下来,金丝雀发布由此得名。

简单的金丝雀测试一般通过手工测试验证,复杂的金丝雀测试需要比较完善的监控基础设施配合,通过监控指标反馈,观察金丝雀的健康状况,作为后续发布或回退的依据。如果测试通过,则把剩余的 V1 版本全部升级为 V2 版本。如果金丝雀测试失败,则直接回退金丝雀,发布失败。

缺点: 自动化流程不够,发布期间需要人为去操作,可能会引起服务中断等。

1.2滚动更新

在金丝雀发布基础上的进一步优化改进,是一种自动化程度较高的发布方式,用户体验比较平滑,是目前成熟型技术组织所采用的主流发布方式。

一次滚动式发布一般由若干个发布批次组成,每批的数量一般是可以配置的(可以通过发布模板定义)。例如,第一批1台(金丝雀),第二批10%,第三批 50%,第四批100%。每个批次之间留观察间隔,通过手工验证或监控反馈确保没有问题再发下一批次,所以总体上滚动式发布过程是比较缓慢的 (其中金丝雀的时间一般会比后续批次更长,比如金丝雀10 分钟,后续间隔 2分钟)。

1.3蓝绿发布

一些应用程序只需要部署一个新版本,并需要立即切到这个版本。因此,我们需要执行蓝/绿部署。在进行蓝/绿部署时,应用程序的一个新副本(绿)将与现有版本(蓝)一起部署。然后更新应用程序的入口/路由器以切换到新版本(绿)。然后,您需要等待旧(蓝)版本来完成所有发送给它的请求,但是大多数情况下,应用程序的流量将一次更改为新版本;Kubernetes不支持内置的蓝/绿部署。

目前最好的方式是创建新的部署,然后更新应用程序的服务(如service)以指向新的部署;蓝绿部署是不停老版本,部署新版本然后进行测试,确认OK后将流量逐步切到新版本。蓝绿部署无需停机,并且风险较小。需要路由或者ingress的配合。

缺点: 切换是全量的,如果新版本有问题,则对用户体验有直接影响, 需要双倍机器资源。

二、思路原理

2.1.1名词解释

总体思路在于使用deployment的滚动更新控制。首先是要学会查看K8S提供的接口以及相应的接口解释。

K8S的灰度发布、滚动更新、蓝绿发布_第1张图片

K8S的灰度发布、滚动更新、蓝绿发布_第2张图片 

如上图就是使用以下两个命令达到效果:
kubectl api-resources #展示K8S提供的接口资源

kubectl explain deployments.spec.strategy.rollingUpdate #查看K8S提供的接口资源的详细解释

具体解释如下:

type : Can be “Recreate” or “RollingUpdate”. Default is RollingUpdate.滚动发布

rollingUpdate: 仅在type为RollingUpdate时有效

rollingUpdate.maxSurge 最大可超期望的节点数,百分比 10% 或者绝对数值 5

rollingUpdate.maxUnavailable 最大不可用节点数,百分比或者绝对数值

这些接口实际上都是在yaml中可以实现调用,举例yaml如下:

apiVersion: apps/v1

kind: Deployment

metadata:

  name: rollingupdate

spec:

  strategy:

    rollingUpdate:

      maxSurge: 25%         #滚动升级时先启动的pod数量

      maxUnavailable: 25%   #滚动升级时允许的最大unavailable的pod数量

    type: RollingUpdate

  selector:

    matchLabels:

      app: rollingupdate

  replicas: 4

  template:

    metadata:

      labels:

        app: rollingupdate

    spec:

      containers:

      - name: rollingupdate

        image: rollingupdate:v1.0

        ports:

        - containerPort: 8080  

---

apiVersion: v1

kind: Service

metadata:

  name: rollingupdate

spec:

  ports:

  - port: 80

    protocol: TCP

    targetPort: 8080

  selector:

    app: rollingupdate

  type: ClusterIP

2.1.2运用原理

Deployment作为控制器,一大特点就是会保证控制器下的pod全部保持一致。但是,Deployment又有另一种很有意思的特性,就是它包含了replicaSet又比replicaSet多出了一些特性。前文提到的strategy就是本文重点讲解的新特性。

Strategy字段下包含:

   rollingUpdate        

     Rolling update config params. Present only if DeploymentStrategyType =

     RollingUpdate.

   type

     Type of deployment. Can be "Recreate" or "RollingUpdate". Default is

     RollingUpdate.

其中rollingUpdate字段下包含:

   maxSurge     

     升级时最大另外启动的pod数量

   maxUnavailable       

     升级时不可用的pod的最大数量

Deployment是一个三级结构,deployment控制replicaset,replicaset控制pod。根据Deployments的这个结构,以Deployment作为控制器,在一个deploy下可以有不同的replicaset,既然有不同的replicaset那就代表着在一个deploy下可以有不同镜像版本的pod同时存在。

金丝雀发布以及滚动发布都是基于这样的原理来做,总结起来一句话,一个deploy下有不同镜像版本的pod共存,陆续更新旧版本pod。

三、实战记录

3.1环境准备

以下操作会对每一步的命令做一个简单的解释。

1、准备两个不同版本的镜像

docker images|grep ezmon #查看宿主机从镜像仓库拉取到本地的镜像。

在这里选取下面这两个镜像,解释如下

178.28.214.7:5000/ezmon/log_exporter:v3.5.0 #镜像仓库ip:port/路径:version

178.28.214.7:5000/ezmon/log_exporter:v3.4.0

K8S的灰度发布、滚动更新、蓝绿发布_第3张图片 

2、准备deployment和service的yaml

版本v1的deployment的yaml文件rollingupdatev1.yaml实际配置如下:

apiVersion: apps/v1

kind: Deployment

metadata:

  name: rollingupdatev1

spec:

  strategy:

    rollingUpdate:

      maxSurge: 25%         #滚动升级时先启动的pod数量

      maxUnavailable: 0   #滚动升级时允许的最大unavailable的pod数量

    type: RollingUpdate

  selector:

    matchLabels:

      app: rollingupdate

  replicas: 4

  template:

    metadata:

      labels:

        app: rollingupdate

    spec:

      containers:

      - name: rollingupdate

        image: 178.28.214.7:5000/ezmon/log_exporter:v3.5.0

        ports:

        - containerPort: 8080

版本v2的deployment的yaml文件rollingupdatev2.yaml实际配置如下:

apiVersion: apps/v1

kind: Deployment

metadata:

  name: rollingupdatev2

spec:

  strategy:

    rollingUpdate:

      maxSurge: 25%         #滚动升级时先启动的pod数量

      maxUnavailable: 0   #滚动升级时允许的最大unavailable的pod数量

    type: RollingUpdate

  selector:

    matchLabels:

      app: rollingupdate

  replicas: 4

  template:

    metadata:

      labels:

        app: rollingupdate

    spec:

      containers:

      - name: rollingupdate

        image: 178.28.214.7:5000/ezmon/log_exporter:v3.4.0

        ports:

        - containerPort: 8080

对应service的yaml文件rollingupdate-svc.yaml实际配置如下:

apiVersion: v1

kind: Service

metadata:

  name: rollingupdate

spec:

  ports:

  - port: 9999

    protocol: TCP

    targetPort: 8080

  selector:

    app: rollingupdate

  type: ClusterIP

  clusterIP: 192.168.3.182

3、将这些yaml全都创建成资源

kubectl create -f rollingupdatev1.yaml

kubectl create -f rollingupdatev2.yaml

kubectl create -f rollingupdate-svc.yaml

结果展示如下,很明显地看出v1和v2的pod名有区别,有v1、v2的replicaset和deployments,有rollingupdate的clusterip类型的service。

K8S的灰度发布、滚动更新、蓝绿发布_第4张图片

通过yaml中的deployments.spec.selector这一字段的约束,保证v1、v2版本的pod都归属于同一个svc,保证了流量在两个版本中都有。这只是一种浅显的流量控制方法,更精细的需要ingress的配合或者路由的控制。可以看到下图中对应rollingupdate-svc的endpoints有8个pod。

K8S的灰度发布、滚动更新、蓝绿发布_第5张图片

3.2灰度发布实践

操作过程:

打开三个窗口,分别用做如下三个用途:执行升级回退等命令、监测pod更新过程、查看更新后pod、rs和deploy的变化。

①在第二个窗口执行如下命令来监控rollingupdatev1的pod的变化过程,初始情况如下图。

kubectl get pods -l app=rollingupdate -w

K8S的灰度发布、滚动更新、蓝绿发布_第6张图片 

②在第一个窗口执行如下命令,将rollingupdatev1的镜像更换为v3.4.0版本的,并在创建完一个新pod之后停止更新。

kubectl set image deploy/rollingupdatev1 rollingupdate=178.28.214.7:5000/ezmon/log_exporter:v3.4.0 && kubectl rollout pause deploy/rollingupdatev1

③在第二个窗口查看pod更新过程如下图,发现仅仅多了一个7546d48895的pod。而且不再有新的pod出现也没有旧pod消亡。

K8S的灰度发布、滚动更新、蓝绿发布_第7张图片

④在第三个窗口查看pod、rs、deploy、endpoints情况。发现现在有5个rollingupdatev1开头的pod,有两个不同的replicaset,没变的deployments但是READY从4/4变成了5/4。endpoints也从四个后端ip变成了5个。相信各位看到这里应该是对这些变化有自己的领悟了,也大致感觉到了这个金丝雀发布的核心原理了。

K8S的灰度发布、滚动更新、蓝绿发布_第8张图片

 

⑤观测新版本pod运行情况没问题,那就把剩下的pod全部都升级到v2版本。回到第一个窗口执行以下命令恢复被暂停的升级过程。

kubectl rollout resume deployment/rollingupdatev1

 

⑥到第二个窗口看一下pod的更新过程

K8S的灰度发布、滚动更新、蓝绿发布_第9张图片

 

⑦到第三个窗口看看pod、rs、deploy和endpoints的情况如下。Pod数量回到了4个,replicaset数量现在是有两个不同之处就在于rs下的pod数量一个是0一个是4,这两个rs也代表了新旧版本,deploy状态与未升级前一致,endpoints的后端ip也是恢复到了4个。

K8S的灰度发布、滚动更新、蓝绿发布_第10张图片

 

⑧在第一个窗口执行如下命令查看历史版本。可以看到revision有1和2,1代表旧版本、2代表新版本

kubectl rollout history deployment/rollingudatev1

 

执行如下命令进行回退

Kubectl rollout undo deployment/rollingupdatev1 --to-revision=1

 

在第三个窗口查看pod、rs、deploy和endpoints的情况如下。可以看出是回退到了升级之前的状态。

K8S的灰度发布、滚动更新、蓝绿发布_第11张图片

 

3.3滚动更新实践

操作过程:

打开三个窗口,分别用做如下三个用途:执行升级回退等命令、监测pod更新过程、查看更新后pod、rs和deploy的变化。

滚动发布和灰度发布操作过程其实是类似的,不过需要修改maxsurge和maxunavailable这两个字段的值。所以在这里我们更新一下maxsurge为20%,再将replicas的值设定为10。执行kubectl apply -f rollingupdatev1.yaml。

然后开始类似按照金丝雀发布的步骤进行操作。

①在第二个窗口执行如下命令来监控rollingupdatev1的pod的变化过程,初始情况如下图。

kubectl get pods -l app=rollingupdate -w

K8S的灰度发布、滚动更新、蓝绿发布_第12张图片

 

②在第一个窗口执行如下命令,将rollingupdatev1的镜像更换为v3.4.0版本的,并在创建完2个新pod之后停止更新。实际上下面这个命令包含了三个命令,更换镜像并记录升级操作、暂停rollout更新、查看更新状态

kubectl set image deploy/rollingupdatev1 rollingupdate=178.28.214.7:5000/ezmon/log_exporter:v3.4.0 --record && kubectl rollout pause deploy/rollingupdatev1 && kubectl rollout status deployment/rollingupdatev1

 

③在第二个窗口查看pod更新过程如下图,发现仅仅多了2个7546d48895的pod。而且不再有新的pod出现也没有旧pod消亡。

K8S的灰度发布、滚动更新、蓝绿发布_第13张图片

 

④在第三个窗口查看pod、rs、deploy、endpoints情况。发现现在有12个rollingupdatev1开头的pod,有两个不同的replicaset,没变的deployments但是READY从10/10变成了12/10。endpoints也从10个后端ip变成了12个。

K8S的灰度发布、滚动更新、蓝绿发布_第14张图片

 

⑤观测新版本pod运行情况没问题,那就把剩下的pod全部都升级到v2版本。但是既然是滚动升级那就试着一批一批地升级,第一次升级20%,第二次准备升级30%,第三次全部升级完。

首先使用kubectl edit deploy/deployment 命令修改maxsurge的值为30%再执行如下命令

kubectl rollout resume deploy/rollingupdatev1 && kubectl rollout pause deploy/rollingupdatev1

可以看到结果增加了3个新pod实际上也减少了3个旧pod

 

⑥pod的更新过程和pod、rs、deploy、endpoints等情况不再赘述

⑦再使用kubectl edit deploy/deployment 命令修改maxsurge的值为50%再执行如下命令

kubectl rollout resume deploy/rollingupdatev1 && kubectl rollout pause deploy/rollingupdatev1

可以看到最终结果是全部更新完毕的

K8S的灰度发布、滚动更新、蓝绿发布_第15张图片

 

⑧回退步骤也不再赘述

总结:金丝雀发布和滚动发布都能起到新版本在生产环境先测试再大规模部署的作用。但是,整个升级过程中主观操作较多,容易引发误操作。有时主观操作也会引起升级的不平滑或者更新速度慢的问题。

3.4蓝绿发布

蓝绿发布是最简单的,v1版本和v2版本共存,资源耗费较多,需要ingress做流量控制。相对而言显得更笨重。具体操作实际在3.1章节已经做过了,不再赘述也没必要花费大篇幅去一步步解析。

你可能感兴趣的:(kubernetes)