前言

大部分公司都已经在使用 Kubernetes 进行容器管理和编排了,但是关于 Kubernetes 的发布策略相关的概括我们很多同学还没有一个完整的认识,下面我们对 Kubernetes 的多种发布策略从整体上做一个概括的认识。Kubernetes 中常见的发布策略主要有如下六种:

重建(recreate)  :即停止一个原有的容器,然后进行容器的新建。

滚动更新(rollingUpdate) :停掉一个容器,然后更新一个容器。

蓝绿布署(blue/green ):准备一套蓝色的容器和一套绿色的容器,进行流量切换。

金丝雀发布(canary) :更新部分容器,没有问题后进行逐步替换,直到切完。

A/B测试发布:即将发布的结果面向部分用户,这块没有现成的组件,需要进行自行处理,比如使用 Istio、Linkerd、Traefik 等。这种方式采用在 Http 的 Header 上进行处理。

无损发布:现在很多发布都是将容器停掉,当没有请求的时候这个时候发布会实现无损发布。

一、重建 (reCreate)

重建就是停掉原来的容器,然后再启动容器,这种方式对于开发环境和测试环境使用还可以,但是对于正式环境就不适用了。相当于本地的服务重启一下,这样会直接影响服务的使用。

spec:replicas: 2strategy:type: Recreate

这个就是同时启动 2 个服务,当我们要新发布服务的时候,需要将这两个都停掉之后才启动新服务。如下图所示:

注意:重建这种策略缺点是十分明显的,当服务停止之后再创建新的服务。服务什么时候启动,也是根据服务启动时间决定的。

二、滚动更新(rollingUpdate)

滚动更新步骤:

1. 准备一个新版本的 POD,比如新版本为 V2,旧版本为 V1。

2. 当 V2 版本的 POD 准备好后,在负载均衡中的实例池中将 V2 的版本加入。

3. 在实例池中剔除一个 V1 版本的 POD。

4. 逐个实例替换,直到每个版本都是 V2 版本。

如下图所示:

滚动更新使用的 YAML 方式如下:

spec:replicas: 5strategy:type: RollingUpdaterollingUpdate:maxSurge: 2        # 一次可以添加多少个PodmaxUnavailable: 1  # 滚动更新期间最大多少个Pod不可用

我们准备两个版本, my-app-v1.yaml 文件。

apiVersion: v1
kind: Service
metadata:name: my-applabels:app: my-app
spec:type: NodePortports:- name: httpport: 80targetPort: httpselector:app: my-app
---
apiVersion: apps/v1
kind: Deployment
metadata:name: my-applabels:app: my-app
spec:replicas: 3selector:matchLabels:app: my-appstrategy:type: Recreateselector:matchLabels:app: my-apptemplate:metadata:labels:app: my-appversion: v1.0.0annotations:prometheus.io/scrape: "true"prometheus.io/port: "9101"spec:containers:- name: my-appimage: containersol/k8s-deployment-strategiesports:- name: httpcontainerPort: 8080- name: probecontainerPort: 8086env:- name: VERSIONvalue: v1.0.0livenessProbe:httpGet:path: /liveport: probeinitialDelaySeconds: 5periodSeconds: 5readinessProbe:httpGet:path: /readyport: probeperiodSeconds: 5

然后我们再准备一下滚动更新的 v2 版本,my-app-rolling-update.yaml 文件

apiVersion: apps/v1
kind: Deployment
metadata:name: my-applabels:app: my-app
spec:replicas: 10# maxUnavailable设置为0可以完全确保在滚动更新期间服务不受影响,还可以使用百分比的值来进行设置。strategy:type: RollingUpdaterollingUpdate:maxSurge: 1maxUnavailable: 0selector:matchLabels:app: my-apptemplate:metadata:labels:app: my-appversion: v2.0.0annotations:prometheus.io/scrape: "true"prometheus.io/port: "9101"spec:containers:- name: my-appimage: containersol/k8s-deployment-strategiesports:- name: httpcontainerPort: 8080- name: probecontainerPort: 8086env:- name: VERSIONvalue: v2.0.0livenessProbe:httpGet:path: /liveport: probeinitialDelaySeconds: 5periodSeconds: 5readinessProbe:httpGet:path: /readyport: probe# 初始延迟设置高点可以更好地观察滚动更新过程initialDelaySeconds: 15periodSeconds: 5

接下来,我们按如下步骤进行操作:

1. 启动 my-app-v1 应用

2. 启动 my-app-rollingupdate 应用

3. 观察所有容器版本变为 V2 版本

启动 my-app-v1 应用并观察,都已经启动,我们进行接口调用。

apply -f my-app-v1.yaml
kubectl get pods -l app=my-appNAME                      READY     STATUS    RESTARTS   AGE
my-app-847874cd75-h3d2e   1/1       Running   0          47s
my-app-847874cd75-p4l8f   1/1       Running   0          47s
my-app-847874cd75-qnt7p   1/1       Running   0          47s
$ kubectl get svc my-app
NAME      TYPE       CLUSTER-IP      EXTERNAL-IP   PORT(S)        AGE
my-app    NodePort   10.109.99.184   <none>        80:30486/TCP   1m
$ curl http://127.0.0.1:30486
Host: my-app-847874cd75-qnt7p, Version: v1.0.0

在当前窗口监控容器的情况,执行命令

watch kubectl get pods -l app=my-app

打开一个新的窗口,然后执行滚动更新命令

kubectl apply -f my-app-rolling-update.yaml

在 watch 的终端观察,开始的时候并没删除旧的 pod,而是创建好新的 pod 后,然后进行挨个替,我们可以使用如下命令观察接口请求, 渐渐的有了第二个版本的请求。

$ while sleep 0.1; do curl http://127.0.0.1:30486; doneHost:my-app-847874cd75-h3d2e, Version: v1.0.0
......
Host: my-app-847874cd75-h3d2e, Version: v1.0.0
Host: my-app-6b5479d97f-2fk24, Version: v2.0.0

在这个过程中,我们发现第二个版本有问题,我们需要进行回滚,此时我们需要执行一下如下命令

kubectl rollout undo deploy my-app

如果想两个版本都观察一下,这个时候需要执行命令。

kubectl rollout pause deploy my-app

如果发现第二个版本没有问题,那么我们要恢复执行

kubectl rollout resume deploy my-app

最后我们清理一下所有的部署

kubectl delete -l app=my-app

注意:滚动部署没有控制流量的情况;各个版本部署的时候需要一定的时间。

三、蓝绿部署

我们需要准备两个版本,一个蓝版本,一个绿蓝本,这两个版本同时存在,且两个版本是独立的,这个时候可以瞬间对两个版本的切换。如下图所示:

接下来,我们采用 svc 的方式,通过 label selector 来进行版本之间的选择。

selector:app: my-appversion: v1.0.0

我们创建一下 app-v1-svc.yaml 文件,我们首先创建一个 svc 服务,然后通过 label selector 来指定一下版本。

apiVersion: v1
kind: Service
metadata:name: my-applabels:app: my-app
spec:type: NodePortports:- name: httpport: 80targetPort: http# 注意这里我们匹配 app 和 version 标签,当要切换流量的时候,我们更新 version 标签的值,比如:v2.0.0selector:app: my-appversion: v1.0.0
---
apiVersion: apps/v1
kind: Deployment
metadata:name: my-app-v1labels:app: my-app
spec:replicas: 3selector:matchLabels:app: my-appversion: v1.0.0template:metadata:labels:app: my-appversion: v1.0.0annotations:prometheus.io/scrape: "true"prometheus.io/port: "9101"spec:containers:- name: my-appimage: containersol/k8s-deployment-strategiesports:- name: httpcontainerPort: 8080- name: probecontainerPort: 8086env:- name: VERSIONvalue: v1.0.0livenessProbe:httpGet:path: /liveport: probeinitialDelaySeconds: 5periodSeconds: 5readinessProbe:httpGet:path: /readyport: probeperiodSeconds: 5

接下来,我们再创建另一个版本 app-v2-svc.yaml 文件

apiVersion: apps/v1
kind: Deployment
metadata:name: my-app-v2labels:app: my-app
spec:replicas: 3selector:matchLabels:app: my-appversion: v2.0.0template:metadata:labels:app: my-appversion: v2.0.0annotations:prometheus.io/scrape: "true"prometheus.io/port: "9101"spec:containers:- name: my-appimage: containersol/k8s-deployment-strategiesports:- name: httpcontainerPort: 8080- name: probecontainerPort: 8086env:- name: VERSIONvalue: v2.0.0livenessProbe:httpGet:path: /liveport: probeinitialDelaySeconds: 5periodSeconds: 5readinessProbe:httpGet:path: /readyport: probeperiodSeconds: 5

接下来,我们按如下步骤执行:

1. 启动版本 1 服务

2. 启动版本 2 服务

3. 将版本 1 服务切换到版本 2,观察服务情况

启动版本的服务并观察,没有问题。

kubectl apply -f app-v1-svc.yaml
kubectl get pods -l app=my-app
watch kubectl get pods -l app=my-app
while sleep 0.1; do curl http://127.0.0.1:31539; done
Host: my-app-v1-7b4874cd75-dmq8f, Version: v1.0.0
Host: my-app-v1-7b4874cd75-dmq8f, Version: v1.0.0

启动版本 2 的服务, 因为版本 2 没有挂到 SVC,所以没有办法观察,但是我们可以先启动。

kubectl apply -f app-v2-svc.yaml

这个时候我们进行版本的切换,通过切换标签的方式

kubctl patch service my-ap -p '{"spec":{"selector":{"version":"v2.0.0"}}}'
while sleep 0.1; do curl http://127.0.0.1:31539; done
Host: my-app-v2-f885c8d45-r5m6z, Version: v2.0.0
Host: my-app-v2-f885c8d45-r5m6z, Version: v2.0.0  

同时,当发现问题的时候,我们再把版本切回到 v1.0.0 即可。

注意:蓝绿部署需要准备两套资源,相对有的时候需要的资源会多; 这种情况可以快速还原版本,不会出现滚动更新那样,回滚需要的时间会很久。

四、金丝雀部署

金丝雀部署就是将部分新版本发在旧的容器池里边,然后进行流量观察,比如 10% 的流量切到新服务上,90% 的流量还在旧服务上。如果你想用更精细粒度的话精使用 Istio。如下图所示:

我们这次还是采用 svc 的方式进行部署的选择,但是这次我们在选择的时候我们只使用 label,不使用版本号。新建 app-v1-canary.yaml, 里边有 10 个 pod 支撑这个服务。

apiVersion: v1
kind: Service
metadata:name: my-applabels:app: my-app
spec:type: NodePortports:- name: httpport: 80targetPort: httpselector:app: my-app
---
apiVersion: apps/v1
kind: Deployment
metadata:name: my-app-v1labels:app: my-app
spec:replicas: 10selector:matchLabels:app: my-appversion: v1.0.0template:metadata:labels:app: my-appversion: v1.0.0annotations:prometheus.io/scrape: "true"prometheus.io/port: "9101"spec:containers:- name: my-appimage: containersol/k8s-deployment-strategiesports:- name: httpcontainerPort: 8080- name: probecontainerPort: 8086env:- name: VERSIONvalue: v1.0.0livenessProbe:httpGet:path: /liveport: probeinitialDelaySeconds: 5periodSeconds: 5readinessProbe:httpGet:path: /readyport: probeperiodSeconds: 5

接下来,我们创建 v2 版本 app-v2-canary.yaml 文件

apiVersion: apps/v1
kind: Deployment
metadata:name: my-app-v2labels:app: my-app
spec:replicas: 1selector:matchLabels:app: my-appversion: v2.0.0template:metadata:labels:app: my-appversion: v2.0.0annotations:prometheus.io/scrape: "true"prometheus.io/port: "9101"spec:containers:- name: my-appimage: containersol/k8s-deployment-strategiesports:- name: httpcontainerPort: 8080- name: probecontainerPort: 8086env:- name: VERSIONvalue: v2.0.0livenessProbe:httpGet:path: /liveport: probeinitialDelaySeconds: 5periodSeconds: 5readinessProbe:httpGet:path: /readyport: probeperiodSeconds: 5

我们说一下要执行的步骤:

1. 启动 V1 的服务版本:10 个复本。

2. 启动 V2 的服务版本:1 个复本。

3. 观察 V2 流量正常的情况的话,那么启动 V2 的 10 个复本。

4. 删除 V1 的 10 个复本,流量全部到 V2 上。

启动 V1 服务,查看服务是否正确,然后观察一下服务。

kubectl apply -f app-v1-canary.yaml
kubectl get svc -l app=my-app
curl http://127.0.0.1:30760
watch kubectl get pod

新打开容器,执行命令,启动 V2 服务。 上边的 watch 将观察到新增了 1 个 pod, 此时共有 11 个 pod, 2.0.0 的版本已经上来了。

kubectl apply -f app-v2-canary.yaml
hile sleep 0.1; do curl http://127.0.0.1:30760; done
Host: my-app-v1-7b4874cd75-bhxbp, Version: v1.0.0
Host: my-app-v1-7b4874cd75-wmcqc, Version: v1.0.0
Host: my-app-v1-7b4874cd75-tsh2s, Version: v1.0.0
Host: my-app-v1-7b4874cd75-ml58j, Version: v1.0.0
Host: my-app-v1-7b4874cd75-spsdv, Version: v1.0.0
Host: my-app-v2-f885c8d45-mc2fx, Version: v2.0.0

此时我们观察版本 2 的服务是否正确,如果正确,那么我们将版本 2 扩展到 10 个副本。

kubectl scale --replicas=10 deploy my-app-v2

这个时候版本 1 和版本 2 一样了。我们再将版本 1 的 pod 给清除掉

kubectl delete deploy my-app-v1

如果没有问题可以清理所有服务

kubectl delete all -l app=my-app

注意:因为有部分版本在线上运行,我们能够对其日志进行观察和追踪、定位问题;如果有问题也能快速将新版本清理掉;如果没有问题,相比于蓝绿的话,发布较慢;对代码信心不足的情况可以采用此方法,影响范围较小。

五、A/B测试

A/B 测试这种方法一般适合于业务场景测试,比如场景 A 的情况下带来的交易额是否会增大,也就是针对不同的人展示不同的界面,然后来观察效益。这个时候我们需要在需要对流量进行按权重分发,或者是在 Header, cookie 中做文章。比如,我们可以采用 Istio 进行权限的分发。

route:
- tags:version: v1.0.0weight: 90
- tags:version: v2.0.0weight: 10

注意:这种方法可以将流量进行分发,我们需要做一个全局的链路跟踪;这种其实不属于部署范围,是流量分发的一种机制。

六、无损发布

我们经常会遇到的情况是,程序正在运行的时候,pod 突然停止了,这个时候,执行的一半的程序很可能会对数据的完整性造成影响。这个时候就需要我们精巧的设计,流量的切换,优雅的停服务。常用的解决方案:

开源的 OpenKruise v0.5.0 支持无损发布

基于 service mesh 网络服务,无损发布的方法是采用 K8S+Istio 边车模式 +envoy 实现无损发布。

阿里云也有企业级的 EDAS,支持无损发布,

Kubernetes 学习总结(24)—— Kubernetes 滚动更新、蓝绿发布、金丝雀发布等发布策略详解相关推荐

  1. OpenShift 4 Hands-on Lab (3) - 应用部署和切换策略(蓝绿、金丝雀和A/B、回滚)

    <OpenShift 4.x HOL教程汇总> 说明:本文已经在OpenShift 4.8环境中验证 文章目录 应用上线部署策略 蓝绿部署 金丝雀发布和A/B测试 通过调整Route权重实 ...

  2. python复制列表元素_Python学习教程:Python列表赋值,复制,深拷贝及5种浅拷贝详解...

    Python学习教程:Python列表赋值,复制,深拷贝及5种浅拷贝详解 概述 在列表复制这个问题,看似简单的复制却有着许多的学问,尤其是对新手来说,理所当然的事情却并不如意,比如列表的赋值.复制.浅 ...

  3. 【学习笔记】薛定谔的喵咪Cat—球盒问题(全详解)

    [学习笔记]薛定谔的喵咪Cat-球盒问题(全详解) 传送门:薛定谔的喵咪 \(Luogu-U77460\)(自自上传的题目,数据略水,尤其是 \(opt=9\) ,以后找时间补上) [题目描述] 当一 ...

  4. 神经网络学习小记录68——Tensorflow2版 Vision Transformer(VIT)模型的复现详解

    神经网络学习小记录68--Tensorflow2版 Vision Transformer(VIT)模型的复现详解 学习前言 什么是Vision Transformer(VIT) 代码下载 Vision ...

  5. @kubernetes(k8s)pod服务探针(健康检查)及回调钩子HOOK详解

    文章目录 服务探针与回调hook(健康检查) 一.存活性探针(LivenessProbe) 1.存活型检查基本用法 2.存活性探针三种使用方式 [ExecAction] [TCPSocketActio ...

  6. 一位深度学习小萌新的学渣笔记(四)GoogLeNet网络介绍及代码详解

    前言 继续学习霹雳大神的神经网络讲解视频 更新不易,希望大家可以去看原视频支持up主霹雳吧啦Wz GoogLeNet网络详解 使用pytorch搭建GoogLeNet网络 本博文记载的是基于Pytor ...

  7. OpenCv学习笔记(二)--Mat矩阵(图像容器)的创建及CV_8UC1,CV_8UC2等参数详解

    (一)Mat矩阵(图像容器)创建时CV_8UC1,CV_8UC2等参数详解 1--Mat不但是一个非常有用的图像容器类,同时也是一个通用的矩阵类 2--创建一个Mat对象的方法很多,我们现在先看一下M ...

  8. C语言学习5:机器数、真值、原码、反码和补码详解

    目录 1.前言 2.机器数 3.真值 4.原码 5.反码 6.补码 1.前言 上篇博文中,笔者介绍了计算机存储器存储容量大小的常用单位 .在未来,我们还会接触到各种各样的数据类型,并且这些数据在运算的 ...

  9. 学习笔记-Flutter 布局(四)- Baseline、FractionallySizedBox、IntrinsicHeight、IntrinsicWidth详解...

    Flutter 布局(四)- Baseline.FractionallySizedBox.IntrinsicHeight.IntrinsicWidth详解 本文主要介绍Flutter布局中的Basel ...

  10. k8s学习-持久化存储(Volumes、hostPath、emptyDir、PV、PVC)详解与实战

    目录 概念 Volumes ConfigMap && Secret hostPath 模版 emptyDir 模版 PV 模版 PVC 模版 实战 volumes - hostPath ...

最新文章

  1. java 如何把源码导出为jar包,以及如何使用导出的jar包
  2. 【Python3网络爬虫开发实战】1.6.2-Tornado的安装
  3. 【bug】掘金md文本解析器bug
  4. IDEA创建xml文件
  5. 关系代数表达式优化步骤
  6. 图形的装饰教案计算机,《有趣的图形》中班教案
  7. 导出excel 手动拼装复杂表头
  8. Mysql SQLserver Oracle 数据库中获取系统时间,年,月,日单个获取
  9. ATH9K Driver Learning Part VII: Transmission Tasklet and Interrupts
  10. 记一次网站迁移的过程
  11. GPS涉及到的各种时间转换(年月日,年积日,儒略日,GPS周及周内日或周内秒,星期几)python
  12. 高级文本编辑器UltraEdit 18 Mac中文版
  13. 为什么我们越娱乐反而会越无聊?
  14. python switch to frame_Switch to Frame
  15. 轩小陌的Python笔记-day16 模块二总结
  16. W7计算机名称原名,w7系统版本名称与区别
  17. 学数学计算机课的心得,课程学习心得体会
  18. 墨客linux工具,Releases
  19. 神州数码易拓TIPTOP ERP维护作业模板-1个单头并列3个单身查询作业
  20. ssstab(网页端)

热门文章

  1. 超媒体是什么?Hypermedia(一种采用非线性网状结构对块状多媒体信息(包括文本、图像、视频等)进行组织和管理的技术)
  2. websphere liberty
  3. Selenium-WEB自动化学习笔记--更新ing
  4. UE4读写txt文本文件(虚幻4)
  5. 华硕笔记本电脑点击桌面后,鼠标一直转圈;右键后,鼠标一直转圈?
  6. php域名查询,域名查询 PHP 代码
  7. 更新python pip 时提示操作超时错误
  8. 算法系列之二十一:实验数据与曲线拟合
  9. 对话镕铭微电子CEO朱照远:未来几年视频处理芯片将成为一个万亿级的市场
  10. Unity中制作图片字体