一、stages:定义流水线的骨架
1.1 基本语法
stages:
- build
- test
- deploy三个要点:
顺序即执行顺序:列表的书写顺序就是阶段执行顺序——build → test → deploy
前一阶段全部成功,才会进入下一阶段:如果 build 中任何一个 Job 失败,test 根本不会执行
同一阶段内的 Job 并行执行:这是天然的并行能力,无需额外配置
1.2 特殊阶段:.pre 和 .post
stages:
- .pre # ← 在所有自定义阶段之前执行
- build
- test
- deploy
- .post # ← 在所有自定义阶段之后执行(无论成败).pre:适合系统级初始化——比如拉取全局凭证、预热缓存等
.post:适合清理和通知——比如发送 Slack 通知、清理临时资源等。即使前面的阶段失败,.post 也会执行
1.3 阶段设计最佳实践
# ✅ 推荐:清晰的职责分离
stages:
- lint # 代码风格检查(最快,最先失败)
- test # 单元测试 + 集成测试
- build # 构建产物
- deploy-staging # 先部署测试环境
- deploy-prod # 再部署生产环境
# ❌ 不推荐:阶段太少,串行等待过长
stages:
- build
- deploy💡 技巧:把最快的检查放在最前面(如 lint、类型检查),让"快速失败"来得越早越好,节省等待时间。
二、Job 结构全解
一个完整的 Job 由以下要素构成:
job-name: # 1. 名称(必填,字母数字连字符)
stage: build # 2. 归属阶段(建议填写)
image: node:20 # 3. 执行环境镜像
tags: # 4. Runner 选择标签
- docker
script: # 5. 核心脚本(必填!)
- npm install
- npm run build
before_script: # 6. 前置脚本
- echo "Job 开始"
after_script: # 7. 后置脚本
- echo "Job 结束"
only/except/rules: # 8. 触发条件(第四篇详解)
when: on_success # 9. 执行时机2.1 script:Job 的心脏
# 写法一:列表(推荐,清晰)
script:
- npm ci
- npm run lint
- npm run build
# 写法二:多行字符串
script: |
npm ci
npm run lint
npm run build
# 写法三:单行(不推荐超过一条命令)
script: npm run build⚠️ 关键行为:script 中任意一条命令返回非零退出码,整个 Job 立即失败。
2.2 image:指定 Docker 镜像
# 使用官方镜像
image: node:20-alpine
# 使用精确版本(推荐生产环境)
image: node:20.11.1-alpine
# 使用私有仓库镜像
image: registry.example.com/my-team/build-image:v1.2.0
# 每个 Job 可以用不同的镜像
build-backend:
image: golang:1.22
script: go build ./...
build-frontend:
image: node:20
script: npm run build⚠️ image 只在 Docker / Kubernetes Executor 中生效。Shell Executor 忽略此配置。
2.3 tags:选择 Runner
# 这个 Job 只会被同时拥有 docker 和 linux 标签的 Runner 执行
docker-build:
tags:
- docker
- linux
script: docker build -t myapp .如果没有 Runner 匹配,Job 会一直卡在 pending 状态——这是新手最常见的踩坑之一。在 GitLab UI 中,你可以看到 Job 显示为 "stuck" 并提示 "no runner with tags matching xxx"。
2.4 before_script 和 after_script
default:
before_script:
- echo "=== 准备环境 ==="
- npm config set registry https://registry.npmmirror.com
build-job:
stage: build
script:
- npm run build
after_script:
- echo "构建完成,清理临时文件"
- rm -rf /tmp/build-*关键行为对比:
三、variables:变量体系深度解析
3.1 变量的四个来源与优先级
GitLab CI/CD 变量有多个来源,当同名变量出现在不同来源时,有明确的优先级。以下从低到高排列:
① default 关键字中定义的 (最低)
② .gitlab-ci.yml 全局 variables
③ Project / Group / Instance 设置 中的变量
④ Job 级别 variables
⑤ 手动触发 Pipeline 时传入的变量 (最高)换句话说:Job 级别的变量可以覆盖全局变量,手动传入的变量覆盖一切。
3.2 在 .gitlab-ci.yml 中定义变量
# 全局变量
variables:
NODE_VERSION: "20"
DEPLOY_ENV: "staging"
# Job 级变量
build:
variables:
NODE_ENV: "production"
CACHE_DIR: "/tmp/build-cache"
script:
- echo "Node 版本: $NODE_VERSION" # 全局变量
- echo "环境: $NODE_ENV" # Job 变量3.3 变量类型
设置方法:在 GitLab UI 中 → Settings → CI/CD → Variables → Add variable
# 使用文件类型变量(值会自动写入临时文件)
deploy:
script:
- cat $KUBE_CONFIG # 打印文件内容(K8s 配置文件)
- kubectl --kubeconfig=$KUBE_CONFIG get pods3.4 动态变量:巧用预定义变量
variables:
# 根据分支动态决定环境名
DEPLOY_TARGET: "${CI_COMMIT_BRANCH}"
# 构建带时间戳的镜像标签
IMAGE_TAG: "${CI_COMMIT_SHORT_SHA}-${CI_PIPELINE_ID}"
deploy:
script:
- echo "部署到: $DEPLOY_TARGET"
- docker tag myapp:$IMAGE_TAG registry.io/myapp:$IMAGE_TAG四、cache:加速流水线的缓存策略
4.1 缓存的本质
cache 是为了在不同的 Pipeline 之间共享依赖文件,避免每次从头下载。典型使用场景:
node_modules(Node.js)
.m2/repository(Maven)
~/.cache/pip(Python)
vendor/bundle(Ruby)
4.2 基本语法
cache:
key: ${CI_COMMIT_REF_SLUG} # 缓存键(决定了如何区分不同缓存)
paths:
- node_modules/
- .npm/
policy: pull-push # 缓存策略三个核心字段:
4.3 cache:key 的策略
# 策略一:按分支缓存(最常用)
cache:
key: ${CI_COMMIT_REF_SLUG}
paths:
- node_modules/
# 策略二:按文件哈希缓存(更精确)
cache:
key:
files:
- package-lock.json # package-lock.json 变化时才重建缓存
paths:
- node_modules/
# 策略三:全局共享缓存
cache:
key: global-cache
paths:
- .m2/repository/4.4 cache:policy 的妙用
# install 阶段:写入缓存
install-deps:
stage: build
cache:
key: ${CI_COMMIT_REF_SLUG}
paths:
- node_modules/
policy: pull-push # ← 读写
script:
- npm ci
# test 阶段:只读缓存(不写回,更快)
unit-test:
stage: test
cache:
key: ${CI_COMMIT_REF_SLUG}
paths:
- node_modules/
policy: pull # ← 只读,节省写入时间
script:
- npm test4.5 cache vs artifacts:一张表搞懂区别
💡 一句话口诀:依赖用 cache,产物用 artifacts。
五、artifacts:构建产物的生命周期管理
5.1 基本用法
build:
stage: build
script:
- npm run build
artifacts:
paths:
- dist/ # 要保留的目录
- build/
exclude: # 排除不必要文件(GitLab 14+)
- dist/**/*.map
expire_in: 7 days # 7 天后自动删除5.2 跨 Stage 传递产物
这是 artifacts 最核心的应用场景:
stages:
- build
- deploy
build:
stage: build
script:
- npm run build
artifacts:
paths:
- dist/
deploy:
stage: deploy
script:
- ls dist/ # ← 能访问到 build 阶段的产物!
- scp -r dist/ server:/var/www/默认情况下,所有后续阶段的所有 Job 都可以获取之前所有 Job 的 artifacts。如果需要精确控制,使用 dependencies:
# build-frontend 和 build-backend 同时运行
build-frontend:
stage: build
artifacts:
paths: [dist/]
build-backend:
stage: build
artifacts:
paths: [target/app.jar]
# deploy-frontend 只需要前端的产物
deploy-frontend:
stage: deploy
dependencies:
- build-frontend # ← 只获取前端产物
script:
- ls dist/ # ✅ 有
- ls target/ # ❌ 没有
# deploy-backend 只需要后端的产物
deploy-backend:
stage: deploy
dependencies:
- build-backend
script:
- ls target/app.jar # ✅ 有5.3 测试报告集成
unit-test:
stage: test
script:
- npm test
artifacts:
reports:
junit: junit.xml # JUnit 测试报告 → GitLab 自动解析
coverage_report:
coverage_format: cobertura
path: coverage/cobertura-coverage.xml # 覆盖率报告GitLab 会自动解析这些标准格式的报告,在 MR(Merge Request)页面中展示测试结果和覆盖率变化。这也是 artifacts 区别于 cache 的核心价值之一——结构化报告的自动解析和可视化。
5.4 expire_in 的实用策略
# 按用途设置不同的过期时间
build:
artifacts:
paths: [dist/]
expire_in: 1 day # 构建产物保留 1 天即可(每次 MR 会重建)
release:
artifacts:
paths: [release/]
expire_in: never # 正式发布产物永久保留六、综合示例:一个高效的 Node.js CI 配置
将本篇所有知识点串联起来:
stages:
- install
- lint
- test
- build
- deploy
# 全局缓存策略
cache:
key:
files:
- package-lock.json
paths:
- node_modules/
- .npm/
# 全局默认值
default:
image: node:20-alpine
before_script:
- echo "=== 开始执行 Job ==="
- date
variables:
NODE_ENV: "production"
# 阶段 1:安装依赖(写入缓存)
install:
stage: install
cache:
policy: pull-push
script:
- npm ci
artifacts:
paths:
- node_modules/
expire_in: 1 hour
# 阶段 2:代码检查(只读缓存)
lint:
stage: lint
cache:
policy: pull
script:
- npm run lint
# 阶段 3:测试(生成 JUnit 报告)
test:
stage: test
cache:
policy: pull
script:
- npm test
artifacts:
reports:
junit: junit.xml
expire_in: 30 days
# 阶段 4:构建
build:
stage: build
cache:
policy: pull
script:
- npm run build
artifacts:
paths:
- dist/
expire_in: 1 day
# 阶段 5:部署
deploy:
stage: deploy
script:
- echo "部署版本: ${CI_COMMIT_SHORT_SHA}"
- echo "模拟部署到 ${DEPLOY_ENV:-staging}..."
environment:
name: staging
only:
- main七、小结与下篇预告
核心知识点回顾
下篇预告
掌握了"静态"配置后,接下来要学习如何让流水线"长脑子"——根据分支、文件变更、变量值等条件,智能地决定"是否运行"和"如何运行"。第四篇《流程控制:rules / workflow / when / needs / parallel》将带来 GitLab CI/CD 中表达式系统和 DAG 并行编排的完整讲解。
参考资料: