利用kubernetes的滚动更新时,可能经常遇到发布“太快不稳定”或“太慢体验差”的情况。本文将介绍kubernetes滚动更新控制速率的特性。
金丝雀发布
金丝雀发布这个术语源自20世纪初期,当时英国的煤矿工人在下井采矿之前,会把笼养的金丝雀携带到矿井中,如果矿井中一氧化碳等有毒气体的浓度过高,在影响矿工之前,金丝雀相比人类表现的更加敏感快速,金丝雀中毒之后,煤矿工人就知道该立刻撤离。金丝雀发布是在将整个软件的新版本发布给所有用户之前,先发布给部分用户,用真实的客户流量来测试,以保证软件不会出现严重问题,降低发布风险。
在实践中,金丝雀发布一般会先发布到一个小比例的机器,比如 2% 的服务器做流量验证,然后从中快速获得反馈,根据反馈决定是扩大发布还是回滚。金丝雀发布通常会结合监控系统,通过监控指标,观察金丝雀机器的健康状况。如果金丝雀测试通过,则把剩余的机器全部升级成新版本,否则回滚代码。
优势:
-
对用户体验影响较小,在金丝雀发布过程中,只有少量用户会受影响
-
发布安全能够得到保障
劣势:
-
金丝雀的机器数量比较少, 有一些问题并不能够暴露出来
适用场景:
-
监控比较完备且与发布系统集成
灰度/滚动发布
灰度发布是金丝雀发布的延伸,是将发布分成不同的阶段/批次,每个阶段/批次的用户数量逐级增加。如果新版本在当前阶段没有发现问题,就再增加用户数量进入下一个阶段,直至扩展到全部用户。
灰度发布可以减小发布风险,是一种零宕机时间的发布策略。它通过切换线上并存版本之间的路由权重,逐步从一个版本切换为另一个版本。整个发布过程会持续比较长的时间, 在这段时间内,新旧代码共存,所以在开发过程中,需要考虑版本之间的兼容性,新旧代码共存不能影响功能可用性和用户体验。当新版本代码出现问题时,灰度发布能够比较快的回滚到老版本的代码上。
结合特性开关等技术,灰度发布可以实现更复杂灵活的发布策略。
优势:
-
用户体验影响比较小, 不需要停机发布
-
能够控制发布风险
劣势:
-
发布时间会比较长
-
需要复杂的发布系统和负载均衡器
-
需要考虑新旧版本共存时的兼容性
适用场景:
-
适合可用性较高的生产环境发布
滚动发布
kubernetes默认的更新策略也就是主流发布方案是滚动更新。每次只升级一个或多个服务,升级完成后加入生产环境, 不断执行这个过程,直到集群中的全部旧版升级新版本。
Kubernetes的默认发布策略。
特点:
- 用户无感知,平滑过渡
缺点:
- 部署周期长(需要健康检查,等它准备就绪,然后升级下一个,健康检查还是需要花费一些时间的)
- 发布策略较复杂
- 不易回滚
- 有影响范围较大
含义
服务在滚动更新时,deployment控制器的目的是:给旧版本(old_rs)副本数减少至0、给新版本(new_rs)副本数量增至期望值(replicas)。大家在使用时,通常容易忽视控制速率的特性,以下是kubernetes提供的两个参数:
- maxUnavailable:和期望ready的副本数比,不可用副本数最大比例(或最大值),这个值越小,越能保证服务稳定,更新越平滑;
- maxSurge:和期望ready的副本数比,超过期望副本数最大比例(或最大值),这个值调的越大,副本更新速度越快。
取值范围
数值
1. maxUnavailable: [0, 副本数]
2. maxSurge: [0, 副本数]
注意:两者不能同时为0。
比例
1. maxUnavailable: [0%, 100%] 向下取整,比如10个副本,5%的话==0.5个,但计算按照0个;
2. maxSurge: [0%, 100%] 向上取整,比如10个副本,5%的话==0.5个,但计算按照1个;
注意:两者不能同时为0。
建议配置
1. maxUnavailable == 0
2. maxSurge == 1
这是我们生产环境提供给用户的默认配置。即“一上一下,先上后下”最平滑原则:1个新版本pod ready(结合readiness)后,才销毁旧版本pod。此配置适用场景是平滑更新、保证服务平稳,但也有缺点,就是“太慢”了。
自定义策略
Deployment controller调整replicaset数量时,严格通过以下公式来控制发布节奏。所以,如需快速发布,可根据实际情况去调整这两个值:
(目标副本数-maxUnavailable) <= 线上实际Ready副本数 <= (目标副本数+maxSurge)
举例:如果期望副本数是10,期望能有至少80%数量的副本能稳定工作,所以:maxUnavailable = 2,maxSurge = 2 (可自定义,建议与maxUnavailable保持一致)
8 <= 线上实际Ready副本数 <= 12
这样,更新过程中,线上能够正常提供服务的pod数总会保持在这个区间内。
现象(maxUnavailable = 1,maxSurge = 1)
#我这里跟新了镜像然后应用,也就是滚动更新
apiVersion: apps/v1
kind: Deployment
metadata:
name: web
namespace: default
spec:
replicas: 3
selector:
matchLabels:
app: web
strategy:
type: RollingUpdate
rollingUpdate:
maxSurge: 1
maxUnavailable: 1
可以看到是先删除一个旧版本,待新版本创建并且running再删除一个旧版本
[root@k8s-master ~]# kubectl get ep -w
NAME ENDPOINTS AGE
kubernetes 192.168.179.102:6443 29d
web 10.244.169.141:80,10.244.169.142:80,10.244.36.78:80 8m8s
web 10.244.169.141:80,10.244.36.78:80 8m58s
web 10.244.169.141:80,10.244.36.78:80 10m
web 10.244.169.141:80,10.244.169.143:80,10.244.36.78:80 10m
web 10.244.169.143:80,10.244.36.78:80 10m
web 10.244.169.143:80,10.244.36.78:80 10m
web 10.244.169.143:80,10.244.169.144:80,10.244.36.78:80 10m
web 10.244.169.143:80,10.244.169.144:80 10m
web 10.244.169.143:80,10.244.169.144:80 11m
web 10.244.169.143:80,10.244.169.144:80,10.244.36.79:80 11m
[root@k8s-master ~]# kubectl get pod -w
NAME READY STATUS RESTARTS AGE
web-655569c6d8-96kmp 1/1 Running 0 15m
web-655569c6d8-n8nvk 1/1 Running 0 15m
web-655569c6d8-vmnj6 1/1 Running 0 15m
web-7cf4f6bf9f-kktpq 0/1 Pending 0 0s
web-7cf4f6bf9f-kktpq 0/1 Pending 0 0s
web-655569c6d8-n8nvk 1/1 Terminating 0 15m
web-7cf4f6bf9f-bbchz 0/1 Pending 0 0s
web-7cf4f6bf9f-bbchz 0/1 Pending 0 1s
web-655569c6d8-n8nvk 1/1 Terminating 0 15m
web-655569c6d8-n8nvk 0/1 Terminating 0 15m
web-655569c6d8-n8nvk 0/1 Terminating 0 15m
web-655569c6d8-n8nvk 0/1 Terminating 0 15m
web-7cf4f6bf9f-bbchz 0/1 Pending 0 15s
web-7cf4f6bf9f-bbchz 0/1 ContainerCreating 0 15s
web-7cf4f6bf9f-bbchz 0/1 ContainerCreating 0 18s
web-7cf4f6bf9f-bbchz 0/1 Running 0 63s
web-7cf4f6bf9f-bbchz 1/1 Running 0 75s
web-655569c6d8-vmnj6 1/1 Terminating 0 16m
web-7cf4f6bf9f-rgq9w 0/1 Pending 0 0s
web-7cf4f6bf9f-rgq9w 0/1 Pending 0 0s
web-655569c6d8-vmnj6 1/1 Terminating 0 16m
web-655569c6d8-vmnj6 0/1 Terminating 0 16m
web-655569c6d8-vmnj6 0/1 Terminating 0 16m
web-655569c6d8-vmnj6 0/1 Terminating 0 16m
web-7cf4f6bf9f-kktpq 0/1 Pending 0 79s
web-7cf4f6bf9f-kktpq 0/1 ContainerCreating 0 80s
web-7cf4f6bf9f-kktpq 0/1 ContainerCreating 0 81s
web-7cf4f6bf9f-kktpq 0/1 Running 0 82s
web-7cf4f6bf9f-kktpq 1/1 Running 0 92s
web-655569c6d8-96kmp 1/1 Terminating 0 17m
web-655569c6d8-96kmp 1/1 Terminating 0 17m
web-655569c6d8-96kmp 0/1 Terminating 0 17m
web-655569c6d8-96kmp 0/1 Terminating 0 17m
web-655569c6d8-96kmp 0/1 Terminating 0 17m
web-7cf4f6bf9f-rgq9w 0/1 Pending 0 19s
web-7cf4f6bf9f-rgq9w 0/1 ContainerCreating 0 19s
web-7cf4f6bf9f-rgq9w 0/1 ContainerCreating 0 21s
web-7cf4f6bf9f-rgq9w 0/1 Running 0 59s
web-7cf4f6bf9f-rgq9w 1/1 Running 0 68s
总结
本文解释了kubernetes最易忽略的“滚动更新策略中控制更新速率”的特性:maxUnavailable与maxSurge,希望能对你在发布版本时有所帮助。
Kubectl 滚动更新命令
## 查看历史
kubectl rollout history deployment/anyops-devopsdocker-ui
## 查看具体某一个历史版本信息
kubectl rollout history deployment/anyops-devopsdocker-ui --revision=2
## 回滚上个版本
kubectl rollout undo deployment/anyops-devopsdocker-ui -n anyops
## 回滚指定版本
kubectl rollout undo deployment/nginx --to-revision=2