GitLab CI/CD 是 GitLab 内置的持续集成与持续部署引擎:只要仓库根目录放一个 .gitlab-ci.yml,每次提交代码就能自动编译、测试、打包并发布。相比另起一套 Jenkins,它和代码同源、配置即代码、Runner 弹性伸缩,是中小团队落地 DevOps 最低门槛的方案。它把”谁触发、跑什么、跑在哪、失败怎么办”全部声明在一个文件里,新同事clone下来就能复现整条流水线,不必再口头传授那套”在我机器上是好的”。本文用一套可直接抄的流水线,讲清缓存(cache)、制品(artifacts)与多环境部署三件最常被搞混的事,再补上重试、超时与人工卡点这些上线才用得上的细节,让你提交一次就能跑完整条发布链路,而不必在”本地能跑、线上崩了”里反复救火。
.gitlab-ci.yml 的基本结构
一条流水线由 stages(阶段顺序)和若干 job(任务)组成。阶段决定了”先构建、再测试、最后部署”的串行关系;同一个 stage 内的多个 job 默认并行。下面是最精简的骨架:
stages:
- build
- test
- deploy
unit-test:
stage: test
image: node:20
script:
- npm ci
- npm test
提交后 GitLab 会按 stage 顺序调度 Runner 执行;只要任意 job 失败,后续 stage 默认不再触发,天然形成了一道质量闸。这和我们用GitHub Actions 搭建 CI/CD 流水线的思路一致,只是配置语法与触发模型略有差异。
缓存(cache)与制品(artifacts)别再搞混
新手最容易把两者当一回事。cache 用来给 job 之间、甚至不同流水线之间”加速”——典型是缓存 node_modules 跳过重装;artifacts 才是”上游产出、要传递给下游 job”的正式交付物,比如打包后的 dist/。下面这张表把四个维度一次说清:
| 维度 | cache(缓存) | artifacts(制品) |
|---|---|---|
| 目的 | 加速:避免重复下载依赖 | 传递:上游产物交给下游 |
| 落盘位置 | Runner 本地或分布式缓存 | GitLab 对象存储,可浏览器下载 |
| 跨流水线 | 默认跨流水线复用(按 key) | 默认仅本次流水线内传递 |
| 过期 | 按 key 命中即复用 | 可设 expire_in 自动清理 |
实战:一个 Node 项目的完整流水线
把”装依赖—构建—产出”三段串起来,关键点在于:用 cache 缓存 node_modules/,用 artifacts 把 dist/ 交给部署阶段。这里有个常踩的坑:若把 dist/ 同时写进 cache,下游可能拿到上次构建的旧文件,工程上一定要分清”加速用的缓存”和”要交付的制品”,二者职责不同、不能混用。
build:
stage: build
image: node:20
cache:
key: $CI_COMMIT_REF_SLUG
paths:
- node_modules/
script:
- npm ci
- npm run build
artifacts:
paths:
- dist/
expire_in: 1 week
注意 artifacts.expire_in:不设置的制品会永久累积、悄悄吃掉对象存储配额。生产里一般按周或按月过期,需要长期留档的再单独归档。真正把镜像跑起来的环节,往往依赖Docker 网络与容器编排把构建产物封进镜像并调度。
多环境部署:用 rules 区分 dev / staging / prod
一套配置管多个环境,靠的是 rules 按分支或 tag 决定”谁触发哪段部署”。最常见的做法是 develop 分支自动上预发、只有打 tag 才允许上生产——避免一次手滑把半成品推到线上。
deploy_staging:
stage: deploy
script:
- ./deploy.sh staging
environment:
name: staging
rules:
- if: $CI_COMMIT_BRANCH == "develop"
deploy_prod:
stage: deploy
script:
- ./deploy.sh prod
environment:
name: production
rules:
- if: $CI_COMMIT_TAG # 只有打 tag 才上生产
environment 让 GitLab 记录每次部署落在哪个环境,配合 production 环境还能开启”手动批准”卡点,上线前必须有人点确认。部署目标通常是Kubernetes 的 Pod/Deployment/Service,由流水线把镜像推上去滚动更新。
缓存加速的三个关键技巧
缓存配错反而会更慢。三条实战经验:① key 尽量绑定锁文件,依赖没变才命中旧缓存;② 用 policy: pull-push 控制是否回写,纯测试 job 设成 pull 省一次上传;③ 不要缓存会随构建变脏的目录,否则下游拿到旧产物。
cache:
key:
files:
- package-lock.json # 锁文件变才换缓存
paths:
- node_modules/
policy: pull-push # 默认;pull 表示只下载不回写
安全:密钥管理与最小权限
CI 里最常泄露的不是代码,而是写死在 .gitlab-ci.yml 里的密钥。正确做法是用 GitLab CI/CD Variables(Settings → CI/CD → Variables)注入,敏感项勾选 “Masked” 和 “Protected”,只在受保护分支可见。Runner 拿到的云厂商 AK/SK,和服务器一样要遵循服务器安全加固的最小权限原则——只给发布所需的那几个 API 权限,别图省事给 Administrator。若基础设施也用代码管理,可让流水线调用Terraform先造资源、再部署应用,做到环境一致、可回滚。
把流水线指标接进监控
流水线跑起来后,光看绿色对勾不够。把每阶段的耗时、成功率、缓存命中率打点到Prometheus + Grafana 监控面板,或用OpenTelemetry 链路追踪串联”提交→构建→部署”的端到端耗时,才能发现”今天构建慢了 3 倍”这类隐性劣化。没有度量,CI/CD 的优化就只是凭感觉。
失败重试、超时与人工卡点
网络抖动会让本该成功的 job 偶发失败,把”假红”挡在门外要靠两个旋钮:给不稳定的测试或部署加 retry,给可能卡死的脚本加 timeout;同时给生产部署加 when: manual 卡点,让上线变成”人确认后才发生”。下面这段是生产部署的稳健写法:
deploy_prod:
stage: deploy
script:
- ./deploy.sh prod
environment:
name: production
rules:
- if: $CI_COMMIT_TAG
when: manual # 人工确认才执行
retry: 1 # 偶发失败自动重试一次
timeout: 10 minutes # 防止脚本卡死
另外,回滚本质上也是一次部署:给上次的 tag 重新触发,或打一个 revert tag 即可。把”发布”和”回滚”用同一套 job 表达,比临时手写回滚脚本可靠得多——因为回滚往往发生在凌晨告警响起、人最不清醒的时候,越标准化越安全。流水线写得越”无聊”,线上就越稳。
小结
GitLab CI/CD 的上手成本很低,但把 cache 与 artifacts 分清、用 rules 锁死多环境部署、把密钥收进 Variables、再把指标接进监控,才是它真正省心的地方。记住三条底线:缓存只加速不传递、制品要设过期、生产只允许 tag 触发。把它和容器编排、基础设施即代码串起来,提交一次就能稳稳跑通”构建—测试—多环境发布”的全链路,把人从重复的手动部署里彻底解放出来。




