开源之夏2021 - KubeVela 项目申请书
本文是我在「开源软件供应链点亮计划 2021」活动中的项目申请书,申请的项目是阿里云 KubeVela 的「实现 KubeVela 的 StatefulSet 和 AdvancedSatefulSet Rollout Plugin Controller」。
背景:KubeVela 的 Rollout 的主要功能是在创建或者更新 Application 时,对工作的 Pod 进行滚动升级。防止一次性地更新全部的工作负载 Pod,导致服务的中断。现在 Vela 已经对 Deployment 和 CloneSet 进行了完整的支持。
任务:实现 StatefulSet 和 AdvancedSatefulSet 的 Rollout Plugin 接口。包括两部分的 Controller:Scale Controller 和 Rollout Controller。
项目背景
KubeVela 是一个云原生平台构建引擎,提供了云原生应用管理的能力,其能力之一就是应用的滚动发布。这一小节主要介绍应用滚动发布功能的背景。
自定义资源和 Controller 的实现
KubeVela 提供了 Application
、AppDeployment
和 ApplicationConfiguration
等一系列自定义资源。通过 Operator 模式,KubeVela 可以为这些资源提供 Controller 的实现。在具体的实现上,KubeVela 使用了 Kubebuilder 来实现自定义资源和 Controller,其底层依赖于 controller-runtime 库。
在 controller-runtime
库中,Controller 由 Manager 管理,依赖 Reconciler 实现。在某个 Controller 启动后,controller-runtime
会生成一个工作队列,然后启动用户实现的 Reconciler,并监听 Controller 管理的资源的相关事件(Add
/ Update
/ Delete
)。
在自定义资源被创建、修改或删除时,需要向工作队列中添加其一个 Request 对象(包含 Namespace
和 Name
属性),但不提供其余的事件相关信息。
Reconciler 会尝试从工作队列中获取 Request 对象。如果工作队列为空,则阻塞;否则,对 Request 对象 做进一步的处理。
Request 对象的处理依赖于用户在创建 Controller 时提供的 Reconciler。每个 Reconciler 至少需要提供一个 Reconcile 函数,它接收一个 Request 对象,并在返回结果中指示工作队列接下来对该 Request 对象的处理方式。由于 Request 对象中只包含资源的 Namespace
和 Name
属性,所以 Reconciler 需要自行与集群交互,查询相关资源的状态。
根据 Reconcile 函数的返回结果进行判断,如果 Reconcile 流程出错或者需要重新执行,则需要将 Request 对象放回到队列中;而如果 Reconcile 流程执行完成,则在队列中删除对应的 Request 对象。
进一步的分析可见:自定义 Kubernetes Controller。
Rollout Plan
在默认情况下,KubeVela 会对应用执行原地发布,也就是进行全量的切换。如果用户需要进行滚动发布,则要提供 RolloutPlan
结构,其属性包括:
属性 | 类型 | 含义 |
---|---|---|
RolloutStrategy | 字符串 | 滚动发布的策略 |
TargetSize | 整数 | 目标资源的大小 |
NumBatches | 整数 | Batch 的数量(也就是滚动的次数) |
RolloutBatches | RolloutBatch 结构数组 | 各个 Batch 的数量或百分比分布 |
BatchPartition | 整数 | 滚动的次数上限 |
Paused | 布尔值 | 是否暂停滚动 |
RolloutWebhooks | RolloutWebhook 结构数组 | 用户提供的 Webhook 列表(用于接收滚动发布相关的事件信息) |
CanaryMetric | CanaryMetric 结构数组 | 用于指标的监控 |
用户可在 Application
、RolloutTrait
或 AppRollout
中提供 RolloutPlan
。在底层,滚动发布都是通过 AppRollout
的 API 来完成的。
滚动发布相关组件
与滚动发布相关的部分组件如下图:
其中,Application Controller
依赖于 AppHandler
处理滚动发布;AppHandler
依赖于 ApplicationRollout Controller
的控制逻辑完成滚动发布的具体实现;ApplicationRollout Controller
依赖于 RolloutPlan Controller
执行 RolloutPlan
;RolloutPlan Controller
依赖于 Workload Controller
接口完成具体 Workload
的滚动发布;Workload Controller
的具体实现有四个,包括 Deployment Scale Controller
、Deployment Rollout Controller
、CloneSet Scale Controller
和 CloneSet Rollout Controller
。
另外,由于 Application Controller
和 ApplicationRollout Controller
都注册到了 controller-runtime
的 Manager 中,所以它们的控制循环由 Manager 进行管理。
滚动发布相关流程
本小节将描述滚动发布的相关流程。受篇幅所限,此处仅列出关键点。
Application Controller
的控制流程如下:
-
将
Application
解析成AppFile
。 -
创建
AppHandler
,并通过AppHandler
生成应用的AppRevision
、ApplicationConfiguration
和相关组件。 -
如果
AppRevision
是新创建的或者有更新,则应用组件对应的资源。 -
如果用户提供了
RolloutPlan
,则通过AppHandler
处理滚动发布,并根据返回的结果(Requeue / RequeueAfter)决定下一步的操作。
AppHandler
处理滚动发布的流程如下:
-
设置
TargetAppRevisionName
,其值为最新AppRevision
的名称。 -
设置
SourceAppRevisionName
。如果最新AppRevision
的 ID 为 1,说明Application
是首次发布,则将SourceAppRevisionName
设置为空,表示接下来要进行Scale
操作;而如果最新AppRevision
的 ID 大于 1,则说明之前已经创建过AppRevision
,则将SourceAppRevisionName
设置为上一个AppRevision
的名称。 -
通过
SourceAppRevisionName
、TargetAppRevisionName
和RolloutPlan
等信息创建AppRollout
。 -
借助
ApplicationRollout Controller
的控制逻辑处理AppRollout
。
ApplicationRollout Controller
处理 AppRollout
的过程如下:
-
如果
Rollout
已经执行结束,且SourceAppRevisionName
和TargetAppRevisionName
均无变化,则结束流程。 -
如果
SourceAppRevisionName
非空,则通过它获取SourceAppRevision
和SourceAppContext
。 -
通过
TargetAppRevisionName
获取TargetAppRevision
和TargetAppContext
。 -
如果
SourceAppRevisionName
非空,则通过SourceAppRevision
获取SourceWorkload
,也就是旧AppRevision
中组件对应的Workload
。 -
通过
TargetAppRevisionName
获取TargetWorkload
,也就是新AppRevision
中组件对应的Workload
。 -
创建
RolloutPlan Controller
,并传入SourceWorkload
和TargetWorkload
。通过其 Reconcile 过程,执行控制循环。
RolloutPlan Controller
执行滚动发布的流程如下:
-
根据
TargetWorkload
的类型和SourceWorkload
是否为空来选择Workload Controller
。-
如果
TargetWorkload
的类型为Deployment
,则选择Deployment
相关的Workload Controller
。- 如果
SourceWorkload
为空,说明需要执行Scale
操作,选择Deployment Scale Controller
;否则,说明需要执行Rollout
操作,选择Deployment Rollout Controller
。
- 如果
-
如果
TargetWorkload
的类型为CloneSet
,则选择CloneSet
相关的Workload Controller
。- 如果
SourceWorkload
为空,说明需要执行Scale
操作,选择CloneSet Scale Controller
;否则,说明需要执行Rollout
操作,选择CloneSet Rollout Controller
。
- 如果
-
-
根据
RollingState
,依赖Workload Controller
来执行不同的操作。(在正常情况下,RollingState
的状态变化为LocatingTargetApp
->VerifyingSpec
->Initializing
->RollingInBatches
->Finalising
->RolloutSucceed
。)-
如果
RollingState
为VerifyingSpec
,说明当前阶段需要进行滚动发布前的验证,检查资源能否根据RolloutPlan
进行升级,此处需要调用Workload Controller
的VerifySpec
函数。 -
如果
RollingState
为Initializing
,说明当前阶段需要进行滚动发布前的初始化,此处需要调用Workload Controller
的Initialize
函数。 -
如果
RollingState
为RollingInBatches
,则正式进入滚动发布阶段。这一阶段会包含多种状态,由BatchRollingState
表示,后续执行的操作依赖于这个状态,直到完成所有 Batch。(在正常情况下,BatchRollingState
的状态变化为BatchInitializing
->BatchInRolling
->BatchVerifying
->BatchFinalizing
->BatchReady
。当RollingState
被设置为RollingInBatches
时,RollingState
也被设置为BatchInitializing
。)-
如果
BatchRollingState
为BatchInitializing
,则在执行当前 Batch 之前进行初始化。 -
如果
BatchRollingState
为BatchInRolling
,则调用Workload Controller
的RolloutOneBatchPods
函数,根据RolloutPlan
设置当前 Batch 的任务。 -
如果
BatchRollingState
为BatchVerifying
,则调用Workload Controller
的CheckOneBatchPods
函数,检查当前 Batch 的任务是否已经执行完成。 -
如果
BatchRollingState
为BatchFinalizing
,则调用Workload Controller
的FinalizeOneBatch
函数,在完成 Batch 任务之后进行最后的检查,以确保滚动发布符合RolloutPlan
的设定。- 如果检查结果为正确,则检查当前完成的 Batch 是不是最后一个 Batch。如果是,表明滚动升级完成,此时将
RollingState
设置为FinalisingState
;否则,说明仍有 Batch 需要执行。
- 如果检查结果为正确,则检查当前完成的 Batch 是不是最后一个 Batch。如果是,表明滚动升级完成,此时将
-
如果
BatchRollingState
为BatchReadyState
,则尝试跳转到下一个 Batch 的任务。如果跳转成功,BatchRollingState
会被设置为BatchInitializing
,继续处理下一个 Batch。
-
-
如果
RollingState
为FinalisingState
,则说明所有 Batch 任务都已经执行完成,此时调用Workload Controller
的Finalize
函数来完成最后的状态处理。 -
如果
RollingState
为RolloutSucceed
或RolloutFailed
,则不需要进一步的操作。
-
项目的意义
如上文所述,KubeVela 提供了对于 Deployment
和 CloneSet
两种资源的 Workload Controller
实现,支持了 Deployment
和 CloneSet
的滚动发布。作为一个具有高扩展性的云原生平台构建引擎,支持多种 Workload
的滚动发布是非常有必要的。因此,本项目的目标就是在 KubeVela 中支持 StatefulSet
和 AdvancedSatefulSet
两种 Workload
的滚动发布。
项目方案
如前所述,每种 Workload
具有 Scale
和 Rollout
两种操作。其中,Scale
操作表示单个版本的滚动发布,Rollout
表示两个版本之间的滚动升级。为了支持 StatefulSet
和 AdvancedSatefulSet
两种 Workload
的滚动发布,我们需要提供四个 Workload Controller
的实现:
-
StatefulSet Scale Controller
-
StatefulSet Rollout Controller
-
AdvancedSatefulSet Scale Controller
-
AdvancedSatefulSet Rollout Controller
这四个 Controller 都至少需要实现这些函数:
-
VerifySpec
-
Initialize
-
RolloutOneBatchPods
-
CheckOneBatchPods
-
FinalizeOneBatch
-
Finalize
StatefulSet Scale Controller
-
VerifySpec
函数:-
检查
RolloutPlan.TargetSize
是否已经设置。如果是,则设置为RolloutStatus.RolloutTargetSize
;否则,报错。 -
获取
StatefulSet
的Spec.Replicas
的属性(默认为 1),设置为RolloutStatus.RolloutOriginalSize
。 -
调用公共函数
verifyBatchesWithScale
,根据RolloutTargetSize
和RolloutOriginalSize
提前检查最后能否到达RolloutPlan
设置的目标数量。如果不能,则检查不通过。 -
检查当前
StatefulSet
是否在进行Scale
或者更新操作。如果是,则检查不通过。 -
检查当前
StatefulSet
是否已被某个Controller
控制。如果是,则检查不通过。
-
-
Initialize
函数:- 将
AppRollout
添加到StatefulSet
的Owner
列表中。
- 将
-
RolloutOneBatchPods
函数:- 计算执行当前 Batch 之后
StatefulSet
的目标大小,并将StatefulSet
的Spec.Replicas
属性设置为该值。
- 计算执行当前 Batch 之后
-
CheckOneBatchPods
函数:-
计算执行当前 Batch 之后的目标大小,检查当前 Batch 的
Scale
任务是否已经完成。-
如果
Scale
执行的是扩大操作,而且已经准备好的Pod
的数量与当前 Batch 的 MaxUnavailable(最多允许多少个Pod
不可用)之和大于或等于当前 Batch 的执行目标大小,则说明当前 Batch 的Scale
任务已经完成。 -
如果
Scale
执行的是缩小操作,而且已经准备好的Pod
的数量小于或等于当前 Batch 的执行目标大小,则说明当前 Batch 的Scale
任务已经完成。
-
-
-
FinalizeOneBatch
函数:- 检查
RolloutStatus.UpgradedReplicas
属性,确保执行当前 Batch 之后,已更新的Pod
数量不会超过正常值。
- 检查
-
Finalize
函数:- 在
StatefulSet
的Owner
列表中删除AppRollout
。
- 在
StatefulSet Rollout Controller
-
VerifySpec
函数:-
检查当前
SatefulSet
是否在进行更新操作。如果是,则检查不通过。 -
根据
SatefulSet
的Status.UpdateRevision
属性判断SatefulSet
是否有更新。如果无更新,则检查不通过。 -
调用公共函数
verifyBatchesWithRollout
,提前检查RolloutPlan
中的 Batch 设置是否合理。 -
检查当前
SatefulSet
是否已被Controller
控制。如果是,则检查不通过。
-
-
Initialize
函数:-
将
AppRollout
添加到SatefulSet
的Owner
列表中。 -
设置
SatefulSet
的Spec.UpdateStrategy.Partition
属性为当前SatefulSet
的大小。
-
-
RolloutOneBatchPods
函数:-
计算执行当前 Batch 之后
SatefulSet
的目标大小。 -
设置
SatefulSet
的Spec.UpdateStrategy.RollingUpdate.Partition
属性为SatefulSet
大小和本次 Batch 目标大小的差。
-
-
CheckOneBatchPods
函数:- 如果
SatefulSet
中已经准备好的Pod
的数量与当前 Batch 的 MaxUnavailable(最多允许多少个Pod
不可用)之和大于或等于当前 Batch 的执行目标大小,则说明当前 Batch 的Rollout
任务已经完成。
- 如果
-
FinalizeOneBatch
函数:- 检查
RolloutStatus.UpgradedReplicas
属性,确保执行当前 Batch 之后,已更新的Pod
数量不会超过正常值。
- 检查
-
Finalize
函数:- 在
SatefulSet
的Owner
列表中删除AppRollout
。
- 在
AdvancedSatefulSet Scale Controller
-
VerifySpec
函数:-
检查
RolloutPlan.TargetSize
是否已经设置。如果是,则设置为RolloutStatus.RolloutTargetSize
;否则,报错。 -
获取
AdvancedSatefulSet
的Spec.Replicas
的属性(默认为 1),设置为RolloutStatus.RolloutOriginalSize
。 -
调用公共函数
verifyBatchesWithScale
,根据RolloutTargetSize
和RolloutOriginalSize
提前检查最后能否到达RolloutPlan
设置的目标数量。如果不能,则检查不通过。 -
检查当前
AdvancedSatefulSet
是否在进行Scale
或者更新操作。如果是,则检查不通过。 -
检查当前
AdvancedSatefulSet
是否已被某个Controller
控制。如果是,则检查不通过。
-
-
Initialize
函数:- 将
AppRollout
添加到AdvancedSatefulSet
的Owner
列表中。
- 将
-
RolloutOneBatchPods
函数:- 计算执行当前 Batch 之后
AdvancedSatefulSet
的目标大小,并将AdvancedSatefulSet
的Spec.Replicas
属性设置为该值。
- 计算执行当前 Batch 之后
-
CheckOneBatchPods
函数:-
计算执行当前 Batch 之后的目标大小,检查当前 Batch 的
Scale
任务是否已经完成。-
如果
Scale
执行的是扩大操作,而且已经准备好的Pod
的数量与当前 Batch 的 MaxUnavailable(最多允许多少个Pod
不可用)之和大于或等于当前 Batch 的执行目标大小,则说明当前 Batch 的Scale
任务已经完成。 -
如果
Scale
执行的是缩小操作,而且已经准备好的Pod
的数量小于或等于当前 Batch 的执行目标大小,则说明当前 Batch 的Scale
任务已经完成。
-
-
-
FinalizeOneBatch
函数:- 检查
RolloutStatus.UpgradedReplicas
属性,确保执行当前 Batch 之后,已更新的Pod
数量不会超过正常值。
- 检查
-
Finalize
函数:- 在
AdvancedSatefulSet
的Owner
列表中删除AppRollout
。
- 在
AdvancedSatefulSet Rollout Controller
-
VerifySpec
函数:-
检查当前
AdvancedSatefulSet
是否在进行更新操作。如果是,则检查不通过。 -
根据
AdvancedSatefulSet
的Status.UpdateRevision
属性判断AdvancedSatefulSet
是否有更新。如果无更新,则检查不通过。 -
调用公共函数
verifyBatchesWithRollout
,提前检查RolloutPlan
中的 Batch 设置是否合理。 -
检查当前
AdvancedSatefulSet
是否已被Controller
控制。如果是,则检查不通过。
-
-
Initialize
函数:-
将
AppRollout
添加到AdvancedSatefulSet
的Owner
列表中。 -
设置
AdvancedSatefulSet
的Spec.UpdateStrategy.Partition
属性为当前AdvancedSatefulSet
的大小。
-
-
RolloutOneBatchPods
函数:-
计算执行当前 Batch 之后
AdvancedSatefulSet
的目标大小。 -
设置
AdvancedSatefulSet
的Spec.UpdateStrategy.Partition
属性为AdvancedSatefulSet
大小和本次 Batch 目标大小的差。
-
-
CheckOneBatchPods
函数:- 如果
AdvancedSatefulSet
中已经准备好的Pod
的数量与当前 Batch 的 MaxUnavailable(最多允许多少个Pod
不可用)之和大于或等于当前 Batch 的执行目标大小,则说明当前 Batch 的Rollout
任务已经完成。
- 如果
-
FinalizeOneBatch
函数:- 检查
RolloutStatus.UpgradedReplicas
属性,确保执行当前 Batch 之后,已更新的Pod
数量不会超过正常值。
- 检查
-
Finalize
函数:- 在
AdvancedSatefulSet
的Owner
列表中删除AppRollout
。
- 在