4. 部署频率 SLI 口径(一):从 K8s Deployment/StatefulSet 的 revision 历史中提取部署事件,计算单位时间内的部署次数
4.1 为什么选择 Revision 历史作为部署事件源
在 DORA 度量体系中,部署频率(Deployment Frequency, DF) 是最核心的四个指标之一,它直接反映团队交付吞吐能力。对于运行在 Kubernetes 上的应用,最精确、最无侵入的部署事件来源并非 CI/CD 管道的触发日志,而是 Kubernetes 控制面自身记录的 Revision 历史。
Kubernetes 的 Deployment 和 StatefulSet 控制器在每次 Pod 模板(PodTemplateSpec)发生变化时,会自动创建一个新的 Revision,并记录在 .spec.revisionHistoryLimit 控制的滚动历史中。这个机制天然具备以下优势:
- 数据权威性:Revision 由 kube-controller-manager 在 API Server 上原子性地创建,不受 CI/CD 工具链故障或网络中断影响。
- 无侵入采集:无需在 CI/CD 管道中埋点,无需修改应用代码或 Helm Chart。
- 统一口径:无论团队使用 Jenkins、GitLab CI、ArgoCD 还是手动
kubectl apply,最终都会在 Revision 历史中留下记录。 - 粒度精确:每个 Revision 对应一次真实的 Pod 模板变更,天然过滤掉“空部署”(如只修改副本数、只修改 label 等不触发新 Revision 的操作)。
关键原则:部署频率 SLI 只统计 触发新 Revision 的变更。仅修改 replicas、修改滚动更新策略、修改 label/annotation(不影响 Pod 模板)等操作,不产生新 Revision,也不计入部署次数。
4.2 Revision 历史的数据结构与提取方法
4.2.1 Revision 的存储方式
Kubernetes 将每个 Revision 以 ReplicaSet(对于 Deployment)或 ControllerRevision(对于 StatefulSet)的形式持久化在 etcd 中。两种资源的 Revision 提取方式略有不同:
| 工作负载类型 | Revision 载体 | Revision 标识字段 | 创建时间字段 |
|---|---|---|---|
| Deployment | ReplicaSet | metadata.annotations['deployment.kubernetes.io/revision'] |
metadata.creationTimestamp |
| StatefulSet | ControllerRevision | metadata.name 中的 revision 数字(如 web-7d8f9c6b5) |
metadata.creationTimestamp |
4.2.2 提取部署事件的步骤
- 确定目标工作负载:按 namespace、label selector 或 deployment/statefulset 名称列表筛选。
- 获取所有关联的 Revision 载体:
- 对于 Deployment:通过
kubectl get replicasets -l <selector> -n <ns>获取所有 ReplicaSet。 - 对于 StatefulSet:通过
kubectl get controllerrevisions -n <ns>获取所有 ControllerRevision。
- 对于 Deployment:通过
- 过滤出有效的部署事件:
- 只保留
metadata.ownerReferences指向目标 Deployment/StatefulSet 的 Revision。 - 排除当前活跃的 Revision(即
.status.currentRevision或.status.observedGeneration对应的最新 Revision),因为活跃 Revision 的创建时间不代表本次部署完成时间。
- 只保留
- 提取时间戳:使用
metadata.creationTimestamp作为部署事件的发生时间。 - 按时间窗口聚合:将时间戳按天、周或月进行分组计数。
生产环境建议:不要直接在生产集群上频繁执行
kubectl get replicasets --all-namespaces。推荐使用 Prometheus 的 kube_replicaset_created 指标或通过 Kubernetes API 的 Watch 机制增量采集,避免对 API Server 造成过大压力。
4.3 部署频率 SLI 的计算口径
部署频率 SLI 的通用计算公式为:
部署频率 = 有效部署事件总数 / 时间窗口长度(天)
在 Kubernetes Revision 历史场景下,具体口径定义如下:
| 口径参数 | 取值 | 说明 |
|---|---|---|
| 事件定义 | 每个非活跃的 Revision 创建事件 | 排除当前正在运行的 Revision,避免重复计数 |
| 时间窗口 | 滚动窗口(如最近 7 天 / 30 天) | 推荐使用 7 天窗口,兼顾稳定性和灵敏度 |
| 聚合粒度 | 按工作负载(deployment/statefulset)聚合 | 也可按 namespace、团队、服务层级聚合 |
| 排除项 | 回滚操作、金丝雀发布中的中间 Revision | 回滚会创建新 Revision,但应视为一次部署;金丝雀的中间 Revision 是否计入取决于团队定义 |
4.4 部署事件提取流程架构
下图展示了从 Kubernetes 集群中提取 Revision 历史并计算部署频率的完整数据流:
4.5 实际计算示例
假设我们有一个名为 payment-service 的 Deployment,在最近 7 天内产生了以下 Revision 历史:
| Revision 编号 | ReplicaSet 名称 | 创建时间 | 是否活跃 |
|---|---|---|---|
| 1 | payment-service-6b8d9f4c7 | 2025-03-01 10:00:00 | 否 |
| 2 | payment-service-7c9e0a5d8 | 2025-03-03 14:30:00 | 否 |
| 3 | payment-service-8d0f1b6e9 | 2025-03-05 09:15:00 | 否 |
| 4 | payment-service-9e1g2c7f0 | 2025-03-07 16:45:00 | 是(当前运行) |
计算过程:
- 有效部署事件总数:3(Revision 1、2、3,排除活跃的 Revision 4)
- 时间窗口:7 天
- 部署频率 = 3 / 7 ≈ 0.43 次/天
常见陷阱:如果团队频繁执行
kubectl rollout undo 回滚,每次回滚都会创建一个新的 Revision(即使 Pod 模板与历史版本相同)。此时部署频率会虚高。建议在 SLI 口径中明确:回滚操作是否计入部署次数。通常建议计入,因为回滚同样代表一次生产变更,且可能引入风险。
4.6 与 CI/CD 管道部署次数的差异对比
| 对比维度 | K8s Revision 历史口径 | CI/CD 管道触发口径 |
|---|---|---|
| 数据来源 | Kubernetes 控制面(etcd) | Jenkins / GitLab CI / ArgoCD 日志 |
| 可靠性 | 高(集群原生,不受管道故障影响) | 中(管道故障、日志丢失会导致数据缺失) |
| 粒度 | 精确到 Pod 模板变更 | 可能包含空部署、构建失败等无效事件 |
| 回滚处理 | 自动生成新 Revision,可识别 | 需额外逻辑判断是否为回滚 |
| 采集复杂度 | 低(直接调用 Kubernetes API) | 高(需对接多种 CI/CD 工具) |
| 口径一致性 | 统一(所有工作负载使用相同机制) | 不统一(不同工具、不同团队定义不同) |
从 SRE 和可观测性角度,强烈推荐优先使用 K8s Revision 历史作为部署频率 SLI 的数据源。它提供了最干净、最可靠、最统一的部署事件视图,是构建 DORA 度量体系的基础设施级最佳实践。