3. 流水线关键阶段识别:从代码提交到部署上线的全流程阶段拆解
在构建可观测的 Jenkins 流水线之前,首要任务是精确识别流水线的关键阶段(Stage)。每个阶段代表一次原子化的交付活动,也是 SLI 埋点的最小逻辑单元。只有将流水线拆解为 Checkout → Build → Test → Package → Deploy 五个核心阶段,才能针对性地定义指标口径并确定埋点位置。
3.1 全流程阶段拆解与职责定义
下表列出了从代码提交到部署上线的标准阶段划分、职责说明以及典型的失败模式。这是后续 SLI 口径设计的业务基础。
| 阶段 | 职责 | 典型失败模式 | 埋点关注点 |
|---|---|---|---|
| Checkout | 从 Git 仓库拉取指定分支/提交的源代码 | 网络超时、认证失败、分支不存在 | 拉取耗时、重试次数 |
| Build | 编译源代码,生成中间产物(如 .class、.o 文件) | 编译错误、依赖缺失、资源不足 | 编译耗时、CPU/内存峰值 |
| Test | 执行单元测试、集成测试、静态代码分析 | 测试失败、覆盖率不达标、超时 | 测试通过率、执行耗时 |
| Package | 将编译产物打包为可部署的镜像或归档(如 Docker 镜像、JAR) | 镜像构建失败、制品库推送失败 | 打包耗时、制品大小 |
| Deploy | 将制品部署到目标环境(开发/测试/生产) | 部署超时、健康检查失败、回滚失败 | 部署耗时、滚动更新速率 |
实践建议: 在 Jenkins Declarative Pipeline 中,每个
stage 块应严格对应上表中的一个阶段。不要在同一个 stage 块内混合多个职责,否则会导致 SLI 数据无法归因。
3.2 流水线阶段拓扑与 SLI 埋点位置
下图展示了从代码提交到部署上线的完整阶段拓扑,并标注了每个阶段对应的 SLI 埋点位置(红色标记)。埋点应放置在阶段开始(Start)和结束(End)处,用于采集耗时与状态。
埋点原则: 每个阶段至少埋入两个时间戳(Start / End),结合阶段状态(SUCCESS / FAILURE / ABORTED)即可计算出阶段耗时、成功率、失败率等核心 SLI。对于 Build 和 Test 阶段,建议额外埋入资源消耗指标(如 CPU 使用率、内存占用)。
3.3 关键阶段 SLI 口径定义
基于上述阶段拆解,我们为每个阶段定义具体的 SLI 口径。口径是 SLI 计算的精确公式,必须与埋点数据字段一一对应。
3.3.1 Checkout 阶段 SLI
- 阶段耗时(秒):
checkout_end_timestamp - checkout_start_timestamp - 成功率(%):
checkout_stage_status == "SUCCESS" ? 1 : 0 - 重试次数: 由 Jenkins 插件或流水线脚本显式记录
checkout_retry_count
3.3.2 Build 阶段 SLI
- 编译耗时(秒):
build_end_timestamp - build_start_timestamp - CPU 峰值(%): 从节点监控或
top命令采集build_cpu_max - 内存峰值(MB):
build_memory_max
3.3.3 Test 阶段 SLI
- 测试通过率(%):
test_passed_count / test_total_count - 测试执行耗时(秒):
test_end_timestamp - test_start_timestamp - 覆盖率(%):
test_coverage_percent(由 JaCoCo / Cobertura 等工具输出)
3.3.4 Package 阶段 SLI
- 打包耗时(秒):
package_end_timestamp - package_start_timestamp - 制品大小(MB):
package_artifact_size - 推送成功率(%):
package_push_status == "SUCCESS" ? 1 : 0
3.3.5 Deploy 阶段 SLI
- 部署耗时(秒):
deploy_end_timestamp - deploy_start_timestamp - 健康检查通过率(%):
deploy_health_check_passed / deploy_health_check_total - 回滚触发次数:
deploy_rollback_count
注意: 上述口径中的字段名(如
checkout_start_timestamp)需要在 Jenkins Pipeline 脚本中通过 env 变量或自定义步骤显式写入。不要依赖 Jenkins 内置的 currentBuild.duration 作为阶段耗时,因为它包含队列等待时间,会污染 SLI 数据。
3.4 埋点位置代码示例(Jenkins Declarative Pipeline)
以下代码展示了如何在 Checkout 和 Build 阶段嵌入 SLI 埋点。其他阶段模式相同。
pipeline {
agent any
stages {
stage('Checkout') {
steps {
script {
// 埋点:阶段开始
env.CHECKOUT_START = System.currentTimeMillis()
}
checkout scm
script {
// 埋点:阶段结束
env.CHECKOUT_END = System.currentTimeMillis()
// 计算耗时并记录到自定义 Metric
def duration = (env.CHECKOUT_END as long) - (env.CHECKOUT_START as long)
echo "CHECKOUT_DURATION_MS=${duration}"
}
}
}
stage('Build') {
steps {
script {
env.BUILD_START = System.currentTimeMillis()
}
sh 'mvn clean compile'
script {
env.BUILD_END = System.currentTimeMillis()
def duration = (env.BUILD_END as long) - (env.BUILD_START as long)
echo "BUILD_DURATION_MS=${duration}"
}
}
}
// 后续阶段(Test、Package、Deploy)同理
}
}
3.5 阶段依赖与并行策略对埋点的影响
在实际流水线中,阶段之间可能存在并行或条件依赖。这会影响 SLI 的归因方式:
- 串行阶段: 每个阶段独立埋点,SLI 直接累加即可得到端到端耗时。
- 并行阶段(如并行 Test): 需要为每个并行分支单独埋点,端到端耗时取所有分支的最大值。
- 条件阶段(如仅 master 分支执行 Deploy): 埋点逻辑必须包裹在条件判断内,避免空阶段产生无效 SLI。
下图展示了串行与并行阶段的埋点差异:
最佳实践: 在并行分支中,使用
script { parallel } 块时,在每个分支内部独立埋点。端到端 SLI 计算时,应取所有并行分支的 end_timestamp 最大值与 start_timestamp 最小值。