4. GitLab CI/CD核心机制回顾
在进入发布编排之前,需要把 GitLab CI/CD 的运行时骨架重新梳理清楚。发布编排并不是另起一套系统,而是把已有的 pipeline、stage、job、runner、变量、缓存和制品这些机制组合成可控、可追溯、可回滚的交付链路。理解它们各自的职责边界,才能判断一个发布流程为什么卡住、为什么产物丢失、为什么同一份代码在不同环境表现不一致。
pipeline 与 stage:从提交到执行的骨架
pipeline 是一次流水线运行的完整实例,由 .gitlab-ci.yml 定义。它由若干 stage 组成,stage 之间默认串行,同一 stage 内的 job 默认并行。这个“串行—并行”结构决定了发布节奏:编译、测试、构建镜像、部署到预发、人工确认、部署生产,通常被拆成不同 stage,以便在前一阶段失败时立即阻断后续动作。
stage 的顺序由 stages 关键字声明,例如:
stages:
- build
- test
- package
- deploy-staging
- deploy-prod
如果某个 stage 中所有 job 都失败或被跳过,后续 stage 默认不会执行。发布编排中常利用这一点做“质量门禁”:只有测试和镜像构建都通过,才允许进入部署阶段。需要注意的是,stage 只是逻辑分组,真正执行 job 的是 runner。
job 与 runner:任务定义与执行环境
job 是 pipeline 中最小的可执行单元,包含脚本、镜像、变量、依赖关系、缓存和制品声明。job 本身不执行,它由 runner 领取并运行。runner 是安装在物理机、虚拟机、容器或 Kubernetes 上的代理进程,按标签(tag)匹配 job。发布场景中,构建 job 可能跑在带 Docker 的 runner 上,部署 job 则跑在能访问目标集群的 runner 上。
job 的关键字段包括:
script:实际执行的命令序列。image:job 运行所用的容器镜像,决定工具链版本。tags:选择具备相应能力的 runner。needs:声明跨 stage 的依赖,实现有向无环图式执行,缩短等待时间。rules/only/except:控制 job 何时被创建。artifacts:声明要上传并传递给后续 job 的文件。cache:声明跨 pipeline 复用的目录,如依赖包缓存。
runner 与 job 的匹配关系是发布排障的高频点。若 job 指定了 tags: [deploy-prod],但集群中没有对应标签的 runner,job 会一直处于 pending,而不是失败。此时应检查 runner 注册标签、并发限制和是否被暂停。
变量:配置注入与作用域
变量决定 job 在不同环境中的行为。GitLab 变量分为预定义变量(如 CI_COMMIT_SHA、CI_ENVIRONMENT_NAME)、项目/组级变量、pipeline 变量和 job 内变量。优先级从高到低大致为:job 内定义、pipeline 触发时传入、项目/组级、预定义。发布编排中,镜像标签、目标命名空间、部署策略通常通过变量注入,而不是硬编码在脚本里。
受保护变量(protected)只会在受保护分支或标签上暴露,适合存放生产部署凭据。掩码变量(masked)会在日志中隐藏值,但不能替代最小权限原则。一个常见错误是把生产 kubeconfig 放在普通变量中,导致任意分支的 job 都能读取。
缓存与制品:两种不同的传递机制
缓存(cache)和制品(artifacts)经常被混用,但目标不同。缓存用于加速,跨 pipeline 复用依赖目录,例如 Maven 的 .m2、npm 的 node_modules、Go 的模块缓存。缓存不保证存在,可能被清理,也不适合作为交付物。制品用于传递和留存,由上游 job 上传,下游 job 通过 dependencies 或 needs 下载,并可在 GitLab 界面下载或保留过期时间。
发布链路中,构建 job 产出镜像 tar 包、Helm chart、版本清单或二进制文件,应使用 artifacts 而不是 cache。例如:
build-image:
stage: package
script:
- docker build -t $CI_REGISTRY_IMAGE:$CI_COMMIT_SHORT_SHA .
- docker save $CI_REGISTRY_IMAGE:$CI_COMMIT_SHORT_SHA -o image.tar
artifacts:
paths:
- image.tar
expire_in: 1 day
deploy-staging:
stage: deploy-staging
needs: ["build-image"]
script:
- docker load -i image.tar
- kubectl set image deploy/app app=$CI_REGISTRY_IMAGE:$CI_COMMIT_SHORT_SHA -n staging
这里 needs 让部署 job 直接依赖构建 job,而不必等待整个 package stage 的其他 job。制品 image.tar 被下游下载后加载,保证部署的是同一份构建产物,而不是重新构建。若把镜像 tar 放进 cache,可能因缓存未命中或过期导致部署失败,且无法追溯。
发布编排中必须明确的几个判断点
第一,产物一致性。部署 job 应使用构建 job 上传的制品或镜像摘要,而不是重新执行构建。若使用 needs 加 artifacts,可以保证同一 pipeline 内传递;若使用镜像仓库,应使用不可变标签(如 commit SHA)而非 latest。
第二,变量作用域与安全。生产部署凭据应放在受保护分支可用的受保护变量中,并限制 runner 标签,避免开发分支的 job 意外获得生产权限。环境变量 CI_ENVIRONMENT_NAME 可用于在脚本中区分 staging 与 production,但不应作为唯一安全边界。
第三,缓存与制品的取舍。依赖下载慢时用 cache;需要跨 job 传递构建结果、测试报告、部署清单时用 artifacts。cache 的 key 通常按分支或锁文件哈希生成,避免不同分支互相污染;artifacts 应设置 expire_in,防止存储无限增长。
第四,runner 容量与并发。发布窗口内多个 pipeline 同时触发时,runner 并发不足会导致 job 排队。可通过 resource_group 限制同一环境的部署串行化,避免两个 pipeline 同时部署生产。
一个可复用的最小发布骨架
下面给出一个简化但完整的示例,展示 stage、job、runner 标签、变量、制品和部署串行化的组合方式。示例参数为教学假设,实际使用时需替换镜像地址、命名空间和凭据变量。
stages:
- build
- test
- package
- deploy
variables:
IMAGE_TAG: $CI_REGISTRY_IMAGE:$CI_COMMIT_SHORT_SHA
build:
stage: build
tags: [docker]
script:
- docker build -t $IMAGE_TAG .
- docker push $IMAGE_TAG
rules:
- if: $CI_COMMIT_BRANCH
test:
stage: test
tags: [docker]
script:
- docker run --rm $IMAGE_TAG npm test
package:
stage: package
tags: [docker]
needs: ["build"]
script:
- docker pull $IMAGE_TAG
- docker save $IMAGE_TAG -o image.tar
artifacts:
paths: [image.tar]
expire_in: 1 day
deploy-staging:
stage: deploy
tags: [k8s]
needs: ["package"]
environment:
name: staging
resource_group: staging
script:
- docker load -i image.tar
- kubectl set image deploy/app app=$IMAGE_TAG -n staging
deploy-prod:
stage: deploy
tags: [k8s]
needs: ["package"]
environment:
name: production
resource_group: production
when: manual
script:
- docker load -i image.tar
- kubectl set image deploy/app app=$IMAGE_TAG -n production
这个骨架体现了几个关键点:needs 让 package 和 deploy 不必等待无关 job;artifacts 把镜像 tar 从 package 传到 deploy;resource_group 保证同一环境不会并发部署;when: manual 为生产部署提供人工确认点。理解这些机制后,后续的发布编排、版本治理和回滚策略才有稳定的执行基础。