Deployment 可控的滚动升级实现
最近有个需求是要实现一个Deployment的灰度部署。一般来说的化,可以通过2个Deployment来实现,并修改Service中的Label来引导流量灰度。实际生成中是按照这个方案来开发和实施。
不过,这个方案是有几个小问题:
- 占用资源只增不减(不调整老的副本数量)
- Deployment名称发生改变
- 需要不断调整副本数
- 回滚需要一次新的部署
所以研究了一下,如何介入Deployment控制器来实现滚动更新的可控操作。
Deployment 的更新
我们都知道Deployment控制器是不直接管理Pod的,而是通过ReplicaSet控制器来实现。
在Deployment的对象spec.strategy中有一个关于更新策略的定义
type DeploymentStrategy struct {
// 更新类型: Recreate, RollingUpdate 2中,默认是 RollingUpdate
Type DeploymentStrategyType `json:"type,omitempty" protobuf:"bytes,1,opt,name=type,casttype=DeploymentStrategyType"`
RollingUpdate *RollingUpdateDeployment `json:"rollingUpdate,omitempty" protobuf:"bytes,2,opt,name=rollingUpdate"`
}
type RollingUpdateDeployment struct {
// 最大不可用数
MaxUnavailable *intstr.IntOrString `json:"maxUnavailable,omitempty" protobuf:"bytes,1,opt,name=maxUnavailable"`
// 最大波峰数
MaxSurge *intstr.IntOrString `json:"maxSurge,omitempty" protobuf:"bytes,2,opt,name=maxSurge"`
}
DeploymentStrategyType 有2中:
RecreateRollingUpdate
默认且最常用的就是RollingUpdate。我们主要要了解也就是RollingUpdate的逻辑。
我们注意到RollingUpdate有一个专门的配置项目包含2个值MaxUnavailable和MaxSurge,控制器的只要控制流程也是微软这2个值来展开。
MaxUnavailable: 最大不可用数MaxSurge: 最大波峰数
代码分析
控制器的主要代码在kubernetes/pkg/controller/deployment/deployment_controller.go。
入口的主要逻辑,
- 入口函数
Run,接收workers数量参数,来启动多个协程运行worker函数 worker的功能很简单就是死循环不断的执行processNextWorkItemprocessNextWorkItem从队列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是什么时候被加入数据的。
可控的滚动升级实现
我们回到要解决问题:让滚动更新能够被控制地进行灰度部署。
当然了,这个方案还是存在一个比较明显的弊端:灰度的数量不能为任意数量,只能是按照上述的算法计算后的某个值。

Zoe
全栈开发 · AI 工具制造者 · Go / Flutter / Rust · 开源偏执狂
