开源之夏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 提供了 ApplicationAppDeploymentApplicationConfiguration 等一系列自定义资源。通过 Operator 模式,KubeVela 可以为这些资源提供 Controller 的实现。在具体的实现上,KubeVela 使用了 Kubebuilder 来实现自定义资源和 Controller,其底层依赖于 controller-runtime 库。

controller-runtime 库中,Controller 由 Manager 管理,依赖 Reconciler 实现。在某个 Controller 启动后,controller-runtime 会生成一个工作队列,然后启动用户实现的 Reconciler,并监听 Controller 管理的资源的相关事件(Add / Update / Delete)。

在自定义资源被创建、修改或删除时,需要向工作队列中添加其一个 Request 对象(包含 NamespaceName 属性),但不提供其余的事件相关信息。

Reconciler 会尝试从工作队列中获取 Request 对象。如果工作队列为空,则阻塞;否则,对 Request 对象 做进一步的处理。

Request 对象的处理依赖于用户在创建 Controller 时提供的 Reconciler。每个 Reconciler 至少需要提供一个 Reconcile 函数,它接收一个 Request 对象,并在返回结果中指示工作队列接下来对该 Request 对象的处理方式。由于 Request 对象中只包含资源的 NamespaceName 属性,所以 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 结构数组 用于指标的监控

用户可在 ApplicationRolloutTraitAppRollout 中提供 RolloutPlan。在底层,滚动发布都是通过 AppRollout 的 API 来完成的。

滚动发布相关组件

与滚动发布相关的部分组件如下图:

其中,Application Controller 依赖于 AppHandler 处理滚动发布;AppHandler 依赖于 ApplicationRollout Controller 的控制逻辑完成滚动发布的具体实现;ApplicationRollout Controller 依赖于 RolloutPlan Controller 执行 RolloutPlanRolloutPlan Controller 依赖于 Workload Controller 接口完成具体 Workload 的滚动发布;Workload Controller 的具体实现有四个,包括 Deployment Scale ControllerDeployment Rollout ControllerCloneSet Scale ControllerCloneSet Rollout Controller

另外,由于 Application ControllerApplicationRollout Controller 都注册到了 controller-runtime 的 Manager 中,所以它们的控制循环由 Manager 进行管理。

滚动发布相关流程

本小节将描述滚动发布的相关流程。受篇幅所限,此处仅列出关键点。

Application Controller 的控制流程如下:

  • Application 解析成 AppFile

  • 创建 AppHandler,并通过 AppHandler 生成应用的 AppRevisionApplicationConfiguration 和相关组件。

  • 如果 AppRevision 是新创建的或者有更新,则应用组件对应的资源。

  • 如果用户提供了 RolloutPlan,则通过 AppHandler 处理滚动发布,并根据返回的结果(Requeue / RequeueAfter)决定下一步的操作。

AppHandler 处理滚动发布的流程如下:

  • 设置 TargetAppRevisionName,其值为最新 AppRevision 的名称。

  • 设置 SourceAppRevisionName。如果最新 AppRevision 的 ID 为 1,说明 Application 是首次发布,则将 SourceAppRevisionName 设置为空,表示接下来要进行 Scale 操作;而如果最新 AppRevision 的 ID 大于 1,则说明之前已经创建过 AppRevision,则将 SourceAppRevisionName 设置为上一个 AppRevision 的名称。

  • 通过 SourceAppRevisionNameTargetAppRevisionNameRolloutPlan 等信息创建 AppRollout

  • 借助 ApplicationRollout Controller 的控制逻辑处理 AppRollout

ApplicationRollout Controller 处理 AppRollout 的过程如下:

  • 如果 Rollout 已经执行结束,且 SourceAppRevisionNameTargetAppRevisionName 均无变化,则结束流程。

  • 如果 SourceAppRevisionName 非空,则通过它获取 SourceAppRevisionSourceAppContext

  • 通过 TargetAppRevisionName 获取 TargetAppRevisionTargetAppContext

  • 如果 SourceAppRevisionName 非空,则通过 SourceAppRevision 获取 SourceWorkload,也就是旧 AppRevision 中组件对应的 Workload

  • 通过 TargetAppRevisionName 获取 TargetWorkload,也就是新 AppRevision 中组件对应的 Workload

  • 创建 RolloutPlan Controller,并传入 SourceWorkloadTargetWorkload。通过其 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。)

    • 如果 RollingStateVerifyingSpec,说明当前阶段需要进行滚动发布前的验证,检查资源能否根据 RolloutPlan 进行升级,此处需要调用 Workload ControllerVerifySpec 函数。

    • 如果 RollingStateInitializing,说明当前阶段需要进行滚动发布前的初始化,此处需要调用 Workload ControllerInitialize 函数。

    • 如果 RollingStateRollingInBatches,则正式进入滚动发布阶段。这一阶段会包含多种状态,由 BatchRollingState 表示,后续执行的操作依赖于这个状态,直到完成所有 Batch。(在正常情况下,BatchRollingState 的状态变化为 BatchInitializing -> BatchInRolling -> BatchVerifying -> BatchFinalizing -> BatchReady。当 RollingState 被设置为 RollingInBatches 时,RollingState 也被设置为 BatchInitializing。)

      • 如果 BatchRollingStateBatchInitializing,则在执行当前 Batch 之前进行初始化。

      • 如果 BatchRollingStateBatchInRolling,则调用 Workload ControllerRolloutOneBatchPods 函数,根据 RolloutPlan 设置当前 Batch 的任务。

      • 如果 BatchRollingStateBatchVerifying,则调用 Workload ControllerCheckOneBatchPods 函数,检查当前 Batch 的任务是否已经执行完成。

      • 如果 BatchRollingStateBatchFinalizing,则调用 Workload ControllerFinalizeOneBatch 函数,在完成 Batch 任务之后进行最后的检查,以确保滚动发布符合 RolloutPlan 的设定。

        • 如果检查结果为正确,则检查当前完成的 Batch 是不是最后一个 Batch。如果是,表明滚动升级完成,此时将 RollingState 设置为 FinalisingState;否则,说明仍有 Batch 需要执行。
      • 如果 BatchRollingStateBatchReadyState,则尝试跳转到下一个 Batch 的任务。如果跳转成功,BatchRollingState 会被设置为 BatchInitializing,继续处理下一个 Batch。

    • 如果 RollingStateFinalisingState,则说明所有 Batch 任务都已经执行完成,此时调用 Workload ControllerFinalize 函数来完成最后的状态处理。

    • 如果 RollingStateRolloutSucceedRolloutFailed,则不需要进一步的操作。

项目的意义

如上文所述,KubeVela 提供了对于 DeploymentCloneSet 两种资源的 Workload Controller 实现,支持了 DeploymentCloneSet 的滚动发布。作为一个具有高扩展性的云原生平台构建引擎,支持多种 Workload 的滚动发布是非常有必要的。因此,本项目的目标就是在 KubeVela 中支持 StatefulSetAdvancedSatefulSet 两种 Workload 的滚动发布。

项目方案

如前所述,每种 Workload 具有 ScaleRollout 两种操作。其中,Scale 操作表示单个版本的滚动发布,Rollout 表示两个版本之间的滚动升级。为了支持 StatefulSetAdvancedSatefulSet 两种 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;否则,报错。

    • 获取 StatefulSetSpec.Replicas 的属性(默认为 1),设置为 RolloutStatus.RolloutOriginalSize

    • 调用公共函数 verifyBatchesWithScale,根据 RolloutTargetSizeRolloutOriginalSize 提前检查最后能否到达 RolloutPlan 设置的目标数量。如果不能,则检查不通过。

    • 检查当前 StatefulSet 是否在进行 Scale 或者更新操作。如果是,则检查不通过。

    • 检查当前 StatefulSet 是否已被某个 Controller 控制。如果是,则检查不通过。

  • Initialize 函数:

    • AppRollout 添加到 StatefulSetOwner 列表中。
  • RolloutOneBatchPods 函数:

    • 计算执行当前 Batch 之后 StatefulSet 的目标大小,并将 StatefulSetSpec.Replicas 属性设置为该值。
  • CheckOneBatchPods 函数:

    • 计算执行当前 Batch 之后的目标大小,检查当前 Batch 的 Scale 任务是否已经完成。

      • 如果 Scale 执行的是扩大操作,而且已经准备好的 Pod 的数量与当前 Batch 的 MaxUnavailable(最多允许多少个 Pod 不可用)之和大于或等于当前 Batch 的执行目标大小,则说明当前 Batch 的 Scale 任务已经完成。

      • 如果 Scale 执行的是缩小操作,而且已经准备好的 Pod 的数量小于或等于当前 Batch 的执行目标大小,则说明当前 Batch 的 Scale 任务已经完成。

  • FinalizeOneBatch 函数:

    • 检查 RolloutStatus.UpgradedReplicas 属性,确保执行当前 Batch 之后,已更新的 Pod 数量不会超过正常值。
  • Finalize 函数:

    • StatefulSetOwner 列表中删除 AppRollout

StatefulSet Rollout Controller

  • VerifySpec 函数:

    • 检查当前 SatefulSet 是否在进行更新操作。如果是,则检查不通过。

    • 根据 SatefulSetStatus.UpdateRevision 属性判断 SatefulSet 是否有更新。如果无更新,则检查不通过。

    • 调用公共函数 verifyBatchesWithRollout,提前检查 RolloutPlan 中的 Batch 设置是否合理。

    • 检查当前 SatefulSet 是否已被 Controller 控制。如果是,则检查不通过。

  • Initialize 函数:

    • AppRollout 添加到 SatefulSetOwner 列表中。

    • 设置 SatefulSetSpec.UpdateStrategy.Partition 属性为当前 SatefulSet 的大小。

  • RolloutOneBatchPods 函数:

    • 计算执行当前 Batch 之后 SatefulSet 的目标大小。

    • 设置 SatefulSetSpec.UpdateStrategy.RollingUpdate.Partition 属性为 SatefulSet 大小和本次 Batch 目标大小的差。

  • CheckOneBatchPods 函数:

    • 如果 SatefulSet 中已经准备好的 Pod 的数量与当前 Batch 的 MaxUnavailable(最多允许多少个 Pod 不可用)之和大于或等于当前 Batch 的执行目标大小,则说明当前 Batch 的 Rollout 任务已经完成。
  • FinalizeOneBatch 函数:

    • 检查 RolloutStatus.UpgradedReplicas 属性,确保执行当前 Batch 之后,已更新的 Pod 数量不会超过正常值。
  • Finalize 函数:

    • SatefulSetOwner 列表中删除 AppRollout

AdvancedSatefulSet Scale Controller

  • VerifySpec 函数:

    • 检查 RolloutPlan.TargetSize 是否已经设置。如果是,则设置为 RolloutStatus.RolloutTargetSize;否则,报错。

    • 获取 AdvancedSatefulSetSpec.Replicas 的属性(默认为 1),设置为 RolloutStatus.RolloutOriginalSize

    • 调用公共函数 verifyBatchesWithScale,根据 RolloutTargetSizeRolloutOriginalSize 提前检查最后能否到达 RolloutPlan 设置的目标数量。如果不能,则检查不通过。

    • 检查当前 AdvancedSatefulSet 是否在进行 Scale 或者更新操作。如果是,则检查不通过。

    • 检查当前 AdvancedSatefulSet 是否已被某个 Controller 控制。如果是,则检查不通过。

  • Initialize 函数:

    • AppRollout 添加到 AdvancedSatefulSetOwner 列表中。
  • RolloutOneBatchPods 函数:

    • 计算执行当前 Batch 之后 AdvancedSatefulSet 的目标大小,并将 AdvancedSatefulSetSpec.Replicas 属性设置为该值。
  • CheckOneBatchPods 函数:

    • 计算执行当前 Batch 之后的目标大小,检查当前 Batch 的 Scale 任务是否已经完成。

      • 如果 Scale 执行的是扩大操作,而且已经准备好的 Pod 的数量与当前 Batch 的 MaxUnavailable(最多允许多少个 Pod 不可用)之和大于或等于当前 Batch 的执行目标大小,则说明当前 Batch 的 Scale 任务已经完成。

      • 如果 Scale 执行的是缩小操作,而且已经准备好的 Pod 的数量小于或等于当前 Batch 的执行目标大小,则说明当前 Batch 的 Scale 任务已经完成。

  • FinalizeOneBatch 函数:

    • 检查 RolloutStatus.UpgradedReplicas 属性,确保执行当前 Batch 之后,已更新的 Pod 数量不会超过正常值。
  • Finalize 函数:

    • AdvancedSatefulSetOwner 列表中删除 AppRollout

AdvancedSatefulSet Rollout Controller

  • VerifySpec 函数:

    • 检查当前 AdvancedSatefulSet 是否在进行更新操作。如果是,则检查不通过。

    • 根据 AdvancedSatefulSetStatus.UpdateRevision 属性判断 AdvancedSatefulSet 是否有更新。如果无更新,则检查不通过。

    • 调用公共函数 verifyBatchesWithRollout,提前检查 RolloutPlan 中的 Batch 设置是否合理。

    • 检查当前 AdvancedSatefulSet 是否已被 Controller 控制。如果是,则检查不通过。

  • Initialize 函数:

    • AppRollout 添加到 AdvancedSatefulSetOwner 列表中。

    • 设置 AdvancedSatefulSetSpec.UpdateStrategy.Partition 属性为当前 AdvancedSatefulSet 的大小。

  • RolloutOneBatchPods 函数:

    • 计算执行当前 Batch 之后 AdvancedSatefulSet 的目标大小。

    • 设置 AdvancedSatefulSetSpec.UpdateStrategy.Partition 属性为 AdvancedSatefulSet 大小和本次 Batch 目标大小的差。

  • CheckOneBatchPods 函数:

    • 如果 AdvancedSatefulSet 中已经准备好的 Pod 的数量与当前 Batch 的 MaxUnavailable(最多允许多少个 Pod 不可用)之和大于或等于当前 Batch 的执行目标大小,则说明当前 Batch 的 Rollout 任务已经完成。
  • FinalizeOneBatch 函数:

    • 检查 RolloutStatus.UpgradedReplicas 属性,确保执行当前 Batch 之后,已更新的 Pod 数量不会超过正常值。
  • Finalize 函数:

    • AdvancedSatefulSetOwner 列表中删除 AppRollout

Updated: