返回到博客
Deployment 可控的滚动升级实现
最近有个需求是要实现一个Deployment的灰度部署。一般来说的化,可以通过2个Deployment来实现,并修改Service中的Label来引导流量灰度。实际生成中是按照这个方案来开发和实施。
不过,这个方案是有几个小问题:
- 占用资源只增不减(不调整老的副本数量)
- Deployment名称发生改变
- 需要不断调整副本数
- 回滚需要一次新的部署
所以研究了一下,如何介入Deployment控制器来实现滚动更新的可控操作。
Deployment
的更新
我们都知道Deployment
控制器是不直接管理Pod
的,而是通过ReplicaSet
控制器来实现。
在Deployment
的对象spec.strategy
中有一个关于更新策略的定义
1type DeploymentStrategy struct {2 // 更新类型: Recreate, RollingUpdate 2中,默认是 RollingUpdate3 Type DeploymentStrategyType `json:"type,omitempty" protobuf:"bytes,1,opt,name=type,casttype=DeploymentStrategyType"`4 RollingUpdate *RollingUpdateDeployment `json:"rollingUpdate,omitempty" protobuf:"bytes,2,opt,name=rollingUpdate"`5}6
7type RollingUpdateDeployment struct {8 // 最大不可用数9 MaxUnavailable *intstr.IntOrString `json:"maxUnavailable,omitempty" protobuf:"bytes,1,opt,name=maxUnavailable"`10 // 最大波峰数11 MaxSurge *intstr.IntOrString `json:"maxSurge,omitempty" protobuf:"bytes,2,opt,name=maxSurge"`12
13}
DeploymentStrategyType
有2中:
Recreate
RollingUpdate
默认且最常用的就是RollingUpdate
。我们主要要了解也就是RollingUpdate
的逻辑。
我们注意到RollingUpdate
有一个专门的配置项目包含2个值MaxUnavailable
和MaxSurge
,控制器的只要控制流程也是微软这2个值来展开。
MaxUnavailable
: 最大不可用数MaxSurge
: 最大波峰数
代码分析#
控制器的主要代码在kubernetes/pkg/controller/deployment/deployment_controller.go
。
入口的主要逻辑,
- 入口函数
Run
,接收workers
数量参数,来启动多个协程运行worker
函数 worker
的功能很简单就是死循环不断的执行processNextWorkItem
processNextWorkItem
从队列queue
中取出一个key
然后交给syncHandler
去处理syncHandler
在创建对象时被设置为syncDeployment
,就是主要的逻辑
下面我们来看下syncDeployment
函数,代码还是很清晰
- 将
key
拆分成namespace
和namespace
- 取出
Deployment
对象 - 取出
Deployment
对象对应的ReplicaSet
对象 - 根据
RS
分组来取出所有的Pod
- 判断是被删除状态则更新
Pod
状态 - 检查是否是暂停状态
- 判断是否是在更新过程中
- 启动
Recreate
或RollingUpdate
更新
我们看下Recreate
类型的流程,很简单粗暴,
- 将所有老的
ReplicaSet
的副本数设置为0 - 如果还有老的Pod在运行就直接返回(待下一次
syncHandler
进入) - 将新的
ReplicaSet
的副本数设置为Deployment
的副本数
由于具体Pod
对象的创建过程交给了ReplicaSet
控制器,这里我们顺便也看一下ReplicaSet
的主要流程。
至此我们已经分析完了整个控制器的处理过程,不过还有一个地方没有讲到,那就是这个queue
是什么时候被加入数据的。
可控的滚动升级实现#
我们回到要解决问题:让滚动更新能够被控制地进行灰度部署。
当然了,这个方案还是存在一个比较明显的弊端:灰度的数量不能为任意数量,只能是按照上述的算法计算后的某个值。