3. GitLab CI 流水线核心组件:Job、Stage、Pipeline 的结构与生命周期
在构建 SLI 采集体系之前,必须精确理解 GitLab CI 的三个核心抽象:Job(作业)、Stage(阶段)和 Pipeline(流水线)。这三者构成了流水线的执行骨架,也是 SLI 指标采集的锚点。只有厘清它们的结构关系与生命周期状态,才能设计出高精度的故障排查与告警降噪规则。
3.1 三层组件结构
GitLab CI 采用严格的层级嵌套结构:Pipeline 包含多个 Stage,Stage 包含多个 Job。每个 Job 是实际执行的最小单元,Stage 控制并行与串行顺序,Pipeline 代表一次完整的集成流程。
核心原则:SLI 指标必须绑定到具体的 Job 执行实例(Pipeline ID + Job ID),而非模糊的阶段名称。同一 Stage 内的 Job 并行执行,不同 Stage 之间串行等待。
| 组件 | 定义 | SLI 采集关注点 |
|---|---|---|
| Job | 由 script、before_script、after_script 定义的执行单元,运行在独立容器中 |
执行时长、退出码、日志大小、重试次数、artifacts 生成状态 |
| Stage | 一组并行 Job 的逻辑分组,由 stages 关键字定义顺序 |
阶段总耗时、阻塞时间、并行 Job 失败率、依赖传递延迟 |
| Pipeline | 由代码推送、合并请求或定时触发的一次完整 CI 执行 | 总耗时、通过率、失败阶段分布、触发频率、排队时间 |
3.2 Job 生命周期与状态机
Job 是 SLI 指标的最小采集单元。一个 Job 从创建到结束经历严格的状态转换,每个状态都对应可观测的指标点。
# 典型 Job 定义示例
job_build:
stage: build
script:
- echo "Compiling..."
- make build
artifacts:
paths:
- dist/
retry: 2
timeout: 30 minutes
| 状态 | 含义 | SLI 采集时机 | 常见故障模式 |
|---|---|---|---|
| pending | Job 已创建,等待 Runner 分配 | 记录入队时间戳 | Runner 资源不足导致长时间 pending |
| running | Runner 已领取 Job,开始执行 | 记录开始时间戳,启动计时 | 容器拉取镜像超时、脚本卡死 |
| passed | 所有 script 命令退出码为 0 | 记录结束时间戳,计算执行时长 | 无(正常状态) |
| failed | 至少一条命令退出码非 0 | 记录失败时间戳、退出码、日志尾部 | 编译错误、测试失败、依赖缺失 |
| canceled | 用户手动取消或上游 Job 失败导致取消 | 记录取消时间戳 | 误操作、级联取消未处理 |
| skipped | 因 rules 或 only/except 条件不满足而未执行 |
记录跳过原因 | 规则配置错误导致 Job 意外跳过 |
SLI 采集陷阱:不要仅依赖最终状态(passed/failed)判断健康度。pending 时间过长、running 阶段日志无输出超过 5 分钟、retry 次数用尽等中间状态,往往是更早的故障信号。建议对 pending 时长和 running 阶段的心跳间隔单独设置 SLI。
3.3 Stage 执行模型与依赖关系
Stage 定义了 Job 之间的执行顺序和并行边界。默认情况下,同一 Stage 内的 Job 并行执行,不同 Stage 按 stages 列表顺序串行执行。这种模型直接影响 SLI 中的“阶段阻塞时间”和“关键路径”计算。
# 阶段定义示例
stages:
- build
- test
- deploy
# 依赖控制:test 阶段需要 build 阶段的 artifacts
test_job:
stage: test
needs: ["build_job"]
script:
- npm test
3.4 Pipeline 生命周期与触发类型
Pipeline 的生命周期从触发事件开始,到所有 Stage 执行完毕或中断结束。不同的触发类型对应不同的 SLI 基线,必须分开统计。
| 触发类型 | 触发条件 | 典型 SLI 基线 | 故障排查特点 |
|---|---|---|---|
| push | 代码推送到仓库分支 | 总耗时 < 5 min,通过率 > 95% | 关注增量变更导致的编译/测试失败 |
| merge_request | 创建或更新合并请求 | 总耗时 < 10 min,通过率 > 90% | 关注合并冲突、目标分支变更 |
| schedule | 定时任务(如每日构建) | 总耗时 < 30 min,成功率 100% | 关注环境依赖过期、外部服务不可用 |
| manual | 手动触发(如紧急修复) | 无固定基线,按需监控 | 关注人为操作错误、权限问题 |
| trigger | 上游 Pipeline 或 API 调用触发 | 端到端延迟 < 2 min | 关注跨项目依赖、令牌过期 |
3.5 生命周期状态转换与 SLI 采集点
完整的 Pipeline 生命周期包含以下状态转换,每个转换点都是 SLI 采集的黄金位置:
- created → pending:Pipeline 被创建,等待 Runner 资源。采集:创建时间、排队时长。
- pending → running:第一个 Job 开始执行。采集:排队延迟、Runner 分配耗时。
- running → passed/failed:所有 Stage 执行完毕。采集:各阶段耗时、失败 Job 分布。
- running → canceled:手动取消或级联取消。采集:取消原因、已执行 Job 比例。
最佳实践:在 Pipeline 的
created 状态立即记录时间戳,而不是等到 pending。因为从 created 到 pending 的转换可能因 Runner 资源不足而延迟,这个延迟本身就是重要的 SLI 指标。建议在 Webhook 中监听 pipeline_event 的 pending 和 running 状态变化。
3.6 组件关系对 SLI 采集的影响
理解三层组件的结构关系后,可以总结出以下 SLI 采集原则:
| 采集层级 | 直接采集对象 | 聚合方式 | 告警降噪价值 |
|---|---|---|---|
| Job 级 | 单个 Job 的执行时长、退出码、重试次数 | 按 Job 名称聚合,建立历史基线 | 快速定位具体失败的脚本步骤 |
| Stage 级 | Stage 总耗时、并行 Job 失败率 | 按 Stage 名称聚合,计算阻塞时间 | 区分“单个 Job 失败”与“整个 Stage 阻塞” |
| Pipeline 级 | 总耗时、通过率、触发频率 | 按分支/触发类型聚合 | 识别全局退化趋势,避免逐 Job 排查 |