☰
OpenKruise CloneSet 滚动更新进度超时检测:progressDeadlineSeconds 字段设计与实现解析
2026/10/13 14:49:12 网站建设 项目流程
  • 云原生
  • 容器编排
  • 微服务
  • 运维

【免费下载链接】kruise

Automated management of large-scale applications on Kubernetes (incubating project under CNCF)

项目地址:https://gitcode.com/gh_mirrors/kr/kruise
点击查看免费下载

导读

progressDeadlineSeconds是 Kubernetes Deployment 中用于控制滚动更新"是否在规定时间内取得进展"的经典字段,一旦超时,控制器会在.status.conditions中标记ProgressDeadlineExceeded条件,帮助用户及时发现卡死的发布。本篇文章基于 OpenKruise 的 功能提案文档 及其在仓库中的完整落地实现,讲解 CloneSet 如何引入这一字段:包括与 Deployment 在语义上的关键差异(partition 暂停时间不计入 deadline)、API 字段与校验规则、Progressing条件的状态机、控制器源码级的超时判定与 requeue 机制,以及对应的单元测试与 E2E 验证。读完本文,你将掌握 CloneSet 发布进度超时检测的配置方法、运行原理和排查手段。

背景与动机:为什么 CloneSet 需要 progressDeadlineSeconds

在原生 Kubernetes 中,spec.progressDeadlineSeconds是 Deployment 的可选字段,用于指定系统在判定 Deployment"进展失败"之前愿意等待的秒数。一旦超时,Deployment 控制器会在.status.conditions中添加如下条件:

type: Progressing status: "False" reason: ProgressDeadlineExceeded

该机制对发布管控非常有价值:当新版本镜像拉取失败、Pod 持续 CrashLoop、探针不健康或配额不足时,发布会长时间"原地打转",而progressDeadlineSeconds能把这种隐性故障显式暴露出来,供上层监控和运维人员感知。

CloneSet 是 OpenKruise 面向大规模应用的有状态工作负载(支持原地升级、partition 分批发布、指定删除等能力),其滚动更新同样存在"卡住"的风险,因此 提案 提出为 CloneSet 增加同名字段。该功能已在仓库中落地(参见 CHANGELOG.md,对应 PR #2108),在 v1alpha1 与 v1beta1 两个 API 版本中均可用。

设计决策:与 Deployment 的差异及社区取舍

提案中明确指出,由于 CloneSet 支持partition(分批发布),progressDeadlineSeconds在 CloneSet 中的行为不能简单照搬 Deployment。核心争议点在于:CloneSet 因partition而"暂停"的时间,是否应计入 progress deadline。

提案给出了两种可能的解释:

  1. 重新定义语义:progressDeadlineSeconds指 CloneSet 到达"完成状态"或"因 partition 导致的暂停状态"所花费的时间。此时,暂停期间的时间不计入deadline。
  2. 仅在未设置 partition 时生效:一旦设置了 partition,progressDeadlineSeconds就不适用或不起作用。

社区讨论最终选择了第一种解释——即:当 CloneSet 到达"完成"或"暂停(paused)"状态时,需要重置计时起点。这一决策在 API 注释中也有明确体现:cloneset_types.go 中写道:

ProgressDeadlineSeconds specifies the maximum time for the CloneSet to reach available or paused state due to partitioning. The controller will set a ProgressDeadlineExceeded condition when timeout occurs, while excluding paused state duration from the deadline calculation. This field is optional. If not set, the controller will not track progress deadlines or add the condition.

翻译过来即:该字段限定 CloneSet 到达可用(available)或"因分区而暂停"状态的最大时间;超时后控制器写入ProgressDeadlineExceeded条件;暂停时长从 deadline 计算中排除;字段为可选,不设置则完全不跟踪进度超时。这一语义从根上解决了"分批发布暂停期间被误判超时"的问题,是本功能与 Deployment 最重要的区别。

API 字段与配置

完整 YAML 示例

在 CloneSet 的spec下直接配置progressDeadlineSeconds,默认值参照 Kubernetes 官方 Deployment 语义为 600 秒:

apiVersion: apps.kruise.io/v1alpha1 kind: CloneSet metadata: name: cloneset-example spec: replicas: 3 progressDeadlineSeconds: 600 selector: matchLabels: app: nginx template: metadata: labels: app: nginx spec: containers: - name: nginx image: nginx:1.14.2

字段定义位于 apis/apps/v1alpha1/cloneset_types.go:

// ProgressDeadlineSeconds specifies the maximum time for the CloneSet to reach available // or paused state due to partitioning. The controller will set a ProgressDeadlineExceeded // condition when timeout occurs, while excluding paused state duration from the deadline calculation. // This field is optional. If not set, the controller will not track progress deadlines or add the condition. ProgressDeadlineSeconds *int32 `json:"progressDeadlineSeconds,omitempty"`

同时该字段也出现在生成的 CRD 中(v1alpha1 与 v1beta1 两个版本均包含,见 apps.kruise.io_clonesets.yaml)。

字段语义与校验规则

结合字段注释与实现,progressDeadlineSeconds的使用规则可以归纳为:

取值情况行为
未设置(nil)控制器完全不跟踪进度超时,不会写入Progressing条件(已有条件会被移除)
设置为math.MaxInt32(2147483647)等价于"永不超时",同样不跟踪进度 deadline(见 cloneset_utils.go 的HasProgressDeadline)
设置为正常正整数值超时后写入ProgressDeadlineExceeded条件;期间如进入 available / paused / partition-available 状态则重置计时

在校验 Webhook 中,创建或更新 CloneSet 时会对该字段做两项检查(见 pkg/webhook/cloneset/validating/validation.go):

  1. 字段值必须为非负数(ValidateNonnegativeField);
  2. 若值不等于math.MaxInt32,则必须大于minReadySeconds,否则报错"must be greater than minReadySeconds"。

第二条约束有其合理性:minReadySeconds定义了 Pod 需保持 ready 多久才算 available,若 deadline 比它还小,任何发布都必然超时,属于无意义的配置。另外,从 validation.go 可见,v1alpha1 与 v1beta1 之间转换时该字段会被保留,不会因版本转换而丢失。

Condition 体系:如何暴露进度状态

超时与否、是否处于暂停,全部通过 CloneSet 的.status.conditions中的Progressing条件对外暴露。相关的类型定义在 cloneset_types.go:

type CloneSetConditionReason string const ( CloneSetProgressDeadlineExceeded CloneSetConditionReason = "ProgressDeadlineExceeded" CloneSetProgressPaused CloneSetConditionReason = "CloneSetPaused" CloneSetProgressUpdated CloneSetConditionReason = "CloneSetUpdated" CloneSetProgressPartitionAvailable CloneSetConditionReason = "ProgressPartitionAvailable" CloneSetAvailable CloneSetConditionReason = "CloneSetAvailable" ) type CloneSetConditionType string const ( CloneSetConditionFailedScale CloneSetConditionType = "FailedScale" CloneSetConditionFailedUpdate CloneSetConditionType = "FailedUpdate" CloneSetConditionTypeProgressing CloneSetConditionType = "Progressing" )

CloneSetCondition结构体除Type、Status、Reason、Message外,还包含LastUpdateTime与LastTransitionTime(见 cloneset_types.go)。其中LastUpdateTime承担了提案中"保存 starttime(计时起点)"的职责——超时判定正是基于该时间戳计算的。

一个典型的超时后的status片段如下:

status: conditions: - lastTransitionTime: "2021-11-26T20:52:12Z" lastUpdateTime: "2021-11-26T20:52:12Z" message: CloneSet has minimum availability. reason: MinimumReplicasAvailable status: "True" type: Available - lastTransitionTime: "2024-03-10T12:00:00Z" lastUpdateTime: "2024-03-10T12:00:00Z" message: 'CloneSet revision 2 has timed out progressing' reason: ProgressDeadlineExceeded status: "False" type: Progressing

注意实际落地实现中,超时消息为CloneSet revision <rev> has timed out progressing(由 cloneset_status.go 生成),与提案中的示例措辞略有差异,以源码实现为准。

控制器实现原理:进度状态计算状态机

提案中给出的伪代码思路是:在控制器中加一个 timer 检查进度,超时则追加Progressing=False / ProgressDeadlineExceeded条件;到达 paused 或完成状态则重置计时。这一思路最终落地为realStatusUpdater.calculateProgressingStatus函数(见 pkg/controller/cloneset/cloneset_status.go),它在每次状态更新时被调用(cloneset_status.go),并按优先级依次命中以下分支:

  1. 未启用进度跟踪:HasProgressDeadline为 false(字段为 nil 或MaxInt32)时,直接移除已有Progressing条件并返回-1(不要求 requeue)。这对应"用户删除了该字段/改用永不超时"的场景,测试用例见 cloneset_status_test.go(pds is updated to nil与pds is updated to maxInt32两个用例)。

  2. CloneSetAvailable(发布完成):当CurrentRevision == UpdateRevision、Replicas、UpdatedReplicas、UpdatedAvailableReplicas均等于期望副本数时(cloneset_utils.go),写入Progressing=True / CloneSetAvailable条件并重置计时(返回-1)。

  3. CloneSetBePaused(手动暂停):当rollingUpdate.paused=true时(cloneset_utils.go),写入Progressing=True / CloneSetPaused,暂停期间不计入 deadline——这正是提案"第一种解释"的直接体现。对应测试用例可见 cloneset_status_test.go:即使此前已经ProgressDeadlineExceeded,一旦进入 paused 状态,条件会被重置为CloneSetPaused且不再要求 requeue。

  4. CloneSetPartitionAvailable(分区暂停到位):当用户配置了partition且ExpectedUpdatedReplicas < replicas、ExpectedUpdatedReplicas <= UpdatedAvailableReplicas时(cloneset_utils.go),说明分批发布已推进到 partition 规定的数量,写入Progressing=True / ProgressPartitionAvailable并重置计时。这实现了提案中"当 CloneSet 到达 paused(因 partition)状态时重置 timer"的要求。

  5. CloneSetDeadlineExceeded(超时):判定逻辑见 cloneset_utils.go:

func CloneSetDeadlineExceeded(cs *appsv1beta1.CloneSet, newStatus *appsv1beta1.CloneSetStatus, now time.Time) bool { condition := GetCloneSetCondition(*newStatus, appsv1beta1.CloneSetConditionTypeProgressing) if condition == nil { return false } else if condition.Reason == string(appsv1beta1.CloneSetProgressDeadlineExceeded) { return true } return condition.LastUpdateTime.Add(time.Duration(*cs.Spec.ProgressDeadlineSeconds) * time.Second).Before(now) }

即:以Progressing条件的LastUpdateTime为计时起点,若LastUpdateTime + progressDeadlineSeconds早于当前时间,则判定超时,写入Progressing=False / ProgressDeadlineExceeded条件(消息为CloneSet revision <rev> has timed out progressing)。一旦处于超时状态,条件会持续保持,直至状态机进入前面几种"重置"分支(如发布完成、进入暂停、或pds被改为 nil/MaxInt32)。

  1. 默认分支(正在推进):写入Progressing=True / CloneSetUpdated(消息为 "CloneSet is progressing"),并返回一个 requeue 时长,用于在 deadline 临近时精确唤醒控制器。

与提案伪代码对应的核心判断——"UpdatedReadyReplicas == Replicas或replicas - updatedReplicas == partition时重置 starttime;paused时重置 starttime"——在实际实现中以上述 2、3、4 三个分支的形式落地,且计时起点统一保存在条件的LastUpdateTime字段中,保证了 controller 重启后仍能基于持久化状态继续计时,而不是依赖内存中的starttime变量。

超时判定与 requeue 机制

在"正在推进"的默认分支中,函数会调用getRequeueSecondsFromCondition(cloneset_status.go)计算距离 deadline 还剩多少秒:

func getRequeueSecondsFromCondition(condition *appsv1beta1.CloneSetCondition, progressDeadlineSeconds int32, now time.Time) time.Duration { if condition == nil { return -1 } after := condition.LastUpdateTime.Time.Add(time.Duration(progressDeadlineSeconds) * time.Second).Sub(now) if after < time.Second { return after } // this helps avoid milliseconds skew in AddAfter. return after + time.Second }

该返回值通过DurationStore(见 cloneset_utils.go)传递给调和循环,最终作为reconcile.Result{RequeueAfter: ...}返回(cloneset_controller.go),从而在超时临界点精确唤醒控制器执行超时判定,而不是依赖随机周期性的重新入队。这样既避免了对 API Server 的无效高频请求,又能保证超时条件在 deadline 到达后第一时间被写入。此外,hasProgressingConditionChanged(cloneset_status.go)会将Progressing条件的 Status/Reason 变化纳入状态不一致检测,确保条件变化能被持久化到集群中。

单元测试与 E2E 验证

该功能在仓库中有充分的测试覆盖,可作为理解与验证的参考:

  • 单元测试:pkg/controller/cloneset/cloneset_status_test.go 中的TestSyncProgressingStatus使用testingclock.NewFakeClock模拟时间,覆盖了大量场景,包括:pds为 nil /MaxInt32/ 正常值、手动 paused、partition 暂停、ProgressDeadlineExceeded状态下修改pds、超时后进入暂停、超时后开始新 revision(此时重新计时,见 cloneset_status_test.go)等;
  • 工具函数测试:pkg/controller/cloneset/utils/cloneset_utils_test.go 验证了HasProgressDeadline对 nil、600、MaxInt32三种取值的判断;
  • E2E 测试:test/e2e/apps/v1alpha1/cloneset.go 中包含了"以MaxInt32创建progressDeadlineSeconds"、"动态更新progressDeadlineSeconds"、以及大量minReadySeconds与progressDeadlineSeconds组合(如 10 与 600)的滚动更新场景,用于在真实集群中验证超时与不超时的行为。

实战使用与排查指南

  1. 开启进度超时检测:在 CloneSetspec中添加progressDeadlineSeconds(如 600)。建议与minReadySeconds配合使用,且 deadline 必须大于minReadySeconds,否则会被 Webhook 拒绝。
  2. 观察发布是否卡死:发布期间持续查看.status.conditions,若出现type: Progressing, status: "False", reason: ProgressDeadlineExceeded,说明发布在 deadline 内未取得可用进展,需要排查镜像、探针、资源配额、partition配置等。
  3. 区分"真超时"与"假超时":手动暂停(rollingUpdate.paused=true)或 partition 暂停期间的时间不计入 deadline,条件会显示为CloneSetPaused/ProgressPartitionAvailable且status: "True",这属于正常状态,不会被误判为超时。
  4. 解除超时状态:超时条件本身不会自愈。修复根因后,发布推进到完成(CloneSetAvailable)或进入暂停状态时会自动重置计时并更新条件;也可以通过将progressDeadlineSeconds改为更大的值、MaxInt32或直接删除该字段来停止跟踪。
  5. 配合监控告警:可以基于reason=ProgressDeadlineExceeded的条件变化配置告警,让"发布静默卡死"变成可被感知的告警事件。

总结

progressDeadlineSeconds为 OpenKruise CloneSet 补齐了与原生 Deployment 对齐的发布进度管控能力,同时通过"暂停与分区到达即重置计时"的设计,妥善处理了 CloneSet 特有的 partition 分批发布与手动暂停场景。从 提案文档 到 状态计算实现、判定工具函数、Webhook 校验 以及 E2E 用例,整条链路清晰完整。读者可以结合上述源码路径深入阅读,在实际集群中通过kubectl get cloneset -o yaml观察Progressing条件,验证并掌握这一发布运维利器。

  • 云原生
  • 容器编排
  • 微服务
  • 运维

【免费下载链接】kruise

Automated management of large-scale applications on Kubernetes (incubating project under CNCF)

项目地址:https://gitcode.com/gh_mirrors/kr/kruise
点击查看免费下载
上一篇:Julia 边界检查(Bounds Checking)完全指南:@inbounds 与 @boundscheck 的编译期消除机制与安全实践
下一篇:深入理解 JavaScript 基本包装类型:String、Number、Boolean 的隐式转换机制

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询