📋 GitLab CI/CD .gitlab-ci.yml 指南
作用:定义自动化流水线的"剧本"——什么时候构建、用什么环境构建、构建完怎么部署。
阅读提示:本文档为"傻瓜式"教程,看完就能独立写出适合自己项目的 CI 配置。
📑 目录
一、.gitlab-ci.yml 是什么
简单理解:它是告诉 GitLab "代码推送后自动做什么" 的说明书。
比如:
- 推代码 → 自动编译 Java → 自动打包 Vue → 自动部署到服务器
- 推代码 → 跑单元测试 → 测试通过才部署
- 推代码 → 构建 Docker 镜像 → 推送到镜像仓库
所有这些步骤,都写在 .gitlab-ci.yml 里。
二、文件放哪里
必须放在 Git 仓库的根目录,文件名必须是 .gitlab-ci.yml(注意前面有个点)。
my-project/ ← 项目根目录
├── .gitlab-ci.yml ← 放这里!
├── backend/
├── frontend/
└── README.md
⚠️ 文件名写错(如
gitlab-ci.yml少了点)、放错位置(如放在backend/里),流水线都不会触发。
三、基础结构速览
stages: # ← 定义阶段顺序
- build
- test
- deploy
build_job: # ← Job 名字,随便取
stage: build # ← 属于哪个阶段
tags: # ← 用哪个 Runner 执行
- docker
image: node:20 # ← 用什么 Docker 镜像
script: # ← 真正执行的命令
- echo "开始构建"
- npm install
- npm run build
一次推送后,GitLab 会按这个顺序执行:
build 阶段的所有 Job(并行)
↓
test 阶段的所有 Job(并行)
↓
deploy 阶段的所有 Job(并行)
四、核心字段详解
4.1 stages — 定义流水线阶段
stages:
- build-backend
- build-frontend
- test
- deploy
| 说明 | 示例 |
|---|---|
| 阶段名随便取 | build、test、deploy、package |
| 同一阶段的 Job 并行执行 | build-backend 和 build-frontend 同时跑 |
| 不同阶段 串行执行 | 必须等 build 阶段全部完成,test 才开始 |
💡 技巧:把耗时的操作放在前面并行执行,比如前后端同时构建。
4.2 variables — 全局变量
variables:
MAVEN_OPTS: "-Dmaven.repo.local=.m2/repository"
NODE_ENV: "production"
DEPLOY_PATH: "/var/www/frontend"
API_BASE: "/api"
在 script 里引用:
script:
- echo $NODE_ENV # 输出 production
- echo ${DEPLOY_PATH} # 输出 /var/www/frontend
- mvn package $MAVEN_OPTS # 等价于 mvn package -Dmaven.repo.local=.m2/repository
| 变量类型 | 说明 |
|---|---|
| 全局变量 | 写在最上面,所有 Job 都能用 |
| Job 变量 | 写在某个 Job 里,只有该 Job 能用 |
build_job:
variables:
MY_VAR: "only_for_this_job" # ← 只有 build_job 能用
script:
- echo $MY_VAR
4.3 tags — 指定 Runner(关键!)
build_job:
tags:
- docker # ← 只有标签含 docker 的 Runner 会执行
为什么重要?
你的服务器可能有多个 Runner:
docker-runner:在 Docker 容器里跑构建(安全隔离)shell-runner:直接在宿主机上跑部署(能操作本地文件)
通过 tags 告诉 GitLab:这个 Job 必须由谁执行。
| 场景 | tags 设置 |
|---|---|
| Maven 编译 Java | tags: [docker] |
| npm 构建 Vue | tags: [docker] |
| 复制文件到 Nginx 目录 | tags: [shell] |
| 执行 systemctl 重启服务 | tags: [shell] |
⚠️ 如果 tags 不匹配,Job 会永远 pending(橙色),不会执行。
4.4 image — Docker 镜像(仅 Docker Executor)
build_job:
image: maven:3.9-eclipse-temurin-21-alpine
作用:告诉 Docker Runner,启动一个什么环境的容器来执行命令。
| 技术栈 | 推荐镜像 |
|---|---|
| Java + Maven | maven:3.9-eclipse-temurin-21-alpine |
| Java + Gradle | gradle:8.5-jdk21-alpine |
| Vue/React | node:20-alpine |
| Python | python:3.11-alpine |
| Go | golang:1.22-alpine |
| .NET | mcr.microsoft.com/dotnet/sdk:8.0 |
💡 带
alpine的版本体积小(约 5MB 基础),拉取快,省时间。
4.5 script — 执行命令列表
script:
- echo "开始构建"
- cd backend
- mvn clean package -DskipTests
- ls -la target/
每行是一个独立的 shell 命令,按顺序执行。如果某一行报错,后面的命令不会执行。
多行脚本写法(更整洁):
script:
- |
echo "======== 开始构建 ========"
cd backend
mvn clean package -DskipTests
echo "======== 构建完成 ========"
条件判断:
script:
- |
if [ "$CI_COMMIT_BRANCH" = "main" ]; then
echo "部署到生产环境"
else
echo "部署到测试环境"
fi
4.6 artifacts — 产物传递(跨 Job 共享文件)
build_job:
script:
- mvn package
artifacts:
paths:
- backend/target/*.jar # ← 把 jar 包保存下来
expire_in: 1 week # ← 1 周后自动删除
为什么需要 artifacts?
build_job 在 Docker 容器里编译出 jar 包,容器一销毁文件就没了。deploy_job 需要这个 jar 包才能部署。
artifacts 的作用:把文件"暂存"起来,让后面的 Job 能下载使用。
传递多个文件:
artifacts:
paths:
- backend/target/*.jar
- frontend/dist/
- report/coverage.html
expire_in: 3 days
💡 artifacts vs cache 区别:
artifacts:同一条流水线的不同 Job 之间传递文件(如 build → deploy)cache:跨流水线复用文件(如下次构建直接用上次下载的依赖)
4.7 cache — 缓存加速
build_job:
cache:
paths:
- backend/.m2/repository # ← Maven 依赖缓存
- frontend/node_modules/ # ← npm 依赖缓存
key: "$CI_COMMIT_REF_SLUG" # ← 按分支隔离缓存
作用:避免每次构建都重新下载依赖,节省大量时间。
不同 Job 用不同缓存(避免冲突):
# 后端 Job
cache:
paths:
- backend/.m2/repository/
key: "mvn-$CI_COMMIT_REF_SLUG"
# 前端 Job
cache:
paths:
- frontend/node_modules/
key: "npm-$CI_COMMIT_REF_SLUG"
4.8 dependencies — 指定依赖哪个 Job 的产物
deploy_job:
dependencies:
- build-backend # ← 只拿 build-backend 的 artifacts
作用:控制 deploy_job 下载哪些 artifacts。如果不写,默认下载前面所有 Job 的 artifacts。
不下载任何产物(适合纯脚本 Job):
deploy_job:
dependencies: [] # ← 空数组,不下载任何文件
4.9 only / except / rules — 触发条件
旧写法:only / except(不推荐新项目用)
deploy_job:
only:
- main # ← 只有推送到 main 分支才触发
except:
- develop # ← develop 分支不触发
新写法:rules(功能更强,推荐)
deploy_job:
rules:
- if: '$CI_COMMIT_BRANCH == "main"' # main 分支才部署
when: always
- if: '$CI_COMMIT_BRANCH == "develop"' # develop 分支只构建不部署
when: never
- when: manual # 其他分支手动触发
when 值 | 含义 |
|---|---|
on_success | 前面阶段成功才执行(默认) |
on_failure | 前面阶段失败才执行(用于报错通知) |
always | 无论成败都执行(用于清理) |
manual | 手动触发(用于部署到生产环境) |
delayed | 延迟执行(如延迟 30 分钟部署) |
生产环境手动审批示例:
deploy-prod:
stage: deploy
rules:
- if: '$CI_COMMIT_BRANCH == "main"'
when: manual # ← 必须人工点击"执行"按钮
script:
- echo "✅ 生产环境已确认上线!"
4.10 when — 执行时机
deploy_job:
stage: deploy
when: manual # ← 需要手动点击才能执行
常用场景:
| 场景 | when 值 |
|---|---|
| 自动部署到测试环境 | on_success |
| 手动审批后部署到生产 | manual |
| 构建失败时发送通知 | on_failure |
| 无论成败都清理临时文件 | always |
4.11 needs — 跳过阶段,直接依赖(DAG 流水线)
默认流水线是阶段顺序执行:
build → test → deploy
用 needs 可以让前后端并行部署,不用等对方:
deploy-backend:
stage: deploy
needs: [build-backend] # ← 只要 build-backend 完成就开始
dependencies: [build-backend]
script:
- echo "部署后端"
deploy-frontend:
stage: deploy
needs: [build-frontend] # ← 只要 build-frontend 完成就开始
dependencies: [build-frontend]
script:
- echo "部署前端"
效果:前后端构建完成后同时部署,不用等对方。
4.12 before_script / after_script — 公共脚本
build_job:
before_script:
- echo "======== 开始构建 ========"
- pwd
script:
- mvn clean package
after_script:
- echo "======== 构建结束 ========"
- du -sh target/
| 字段 | 执行时机 |
|---|---|
before_script | 在 script 之前自动执行 |
after_script | 在 script 之后自动执行(即使 script 失败也会执行) |
💡 适合放公共准备操作,比如打印时间、设置环境变量、清理临时文件。
4.13 environment — 环境配置(部署追踪)
deploy_job:
stage: deploy
environment:
name: production
url: http://172.19.90.78
script:
- echo "部署到生产环境"
效果:
- GitLab 网页会显示 "Deployed to production" 按钮
- 点击按钮直接跳转到部署地址
- 可以查看每次部署的历史记录
多环境配置:
deploy-staging:
environment:
name: staging
url: http://172.19.90.78:8080
deploy-prod:
environment:
name: production
url: http://172.19.90.78
4.14 allow_failure — 允许失败
code_style_check:
stage: test
script:
- eslint src/
allow_failure: true # ← 即使检查失败,流水线也继续执行
适用场景:
- 代码风格检查(eslint、prettier)
- 非关键测试
- 可选的安全扫描
⚠️ 如果
allow_failure: false(默认),Job 失败会导致整个流水线失败。
五、高级用法
5.1 矩阵构建(多版本并行测试)
unit-tests:
parallel:
matrix:
- PROVIDER: [aws, gcp, azure]
STACK: [monitoring, app, db]
script:
- echo "Testing $PROVIDER - $STACK"
效果:自动生成 3×3=9 个并行 Job,测试不同组合。
5.2 流水线触发器(一个项目触发另一个项目)
trigger-downstream:
stage: deploy
trigger:
project: my-group/another-project
branch: main
效果:当前项目部署成功后,自动触发另一个项目的流水线。
5.3 定时触发流水线
workflow:
rules:
- if: $CI_PIPELINE_SOURCE == "schedule"
在 GitLab 网页 CI/CD → Schedules 中设置定时任务,比如每天凌晨 2 点自动跑测试。
六、实战配置模板
6.1 前后端分离项目(Java + Vue)
stages:
- build-backend
- build-frontend
- deploy
variables:
MAVEN_OPTS: "-Dmaven.repo.local=.m2/repository"
# ========== 构建 Java 后端 ==========
build-backend:
stage: build-backend
tags: [docker]
image: maven:3.9-eclipse-temurin-21-alpine
cache:
paths: [backend/.m2/repository]
key: "mvn-$CI_COMMIT_REF_SLUG"
script:
- cd backend
- mvn clean package -DskipTests
- ls -la target/
artifacts:
paths: [backend/target/*.jar]
expire_in: 1 day
# ========== 构建 Vue 前端 ==========
build-frontend:
stage: build-frontend
tags: [docker]
image: node:20-alpine
cache:
paths: [frontend/node_modules/]
key: "npm-$CI_COMMIT_REF_SLUG"
script:
- cd frontend
- npm install
- npm run build
- ls -la dist/
artifacts:
paths: [frontend/dist/]
expire_in: 1 day
# ========== 部署后端 ==========
deploy-backend:
stage: deploy
tags: [shell]
needs: [build-backend]
dependencies: [build-backend]
script:
- sudo systemctl stop backend || true
- cp backend/target/*.jar /opt/backend/app.jar
- sudo systemctl start backend
- sleep 5
- sudo systemctl status backend
# ========== 部署前端 ==========
deploy-frontend:
stage: deploy
tags: [shell]
needs: [build-frontend]
dependencies: [build-frontend]
script:
- rm -rf /var/www/frontend/*
- cp -r frontend/dist/* /var/www/frontend/
- ls -la /var/www/frontend/
6.2 多环境部署(测试环境自动 + 生产环境手动)
stages:
- build
- deploy-staging
- deploy-prod
build:
stage: build
tags: [docker]
image: maven:3.9-eclipse-temurin-21-alpine
script:
- mvn clean package -DskipTests
artifacts:
paths: [target/*.jar]
# 测试环境:自动部署
deploy-staging:
stage: deploy-staging
tags: [shell]
dependencies: [build]
environment:
name: staging
url: http://172.19.90.78:8082
rules:
- if: '$CI_COMMIT_BRANCH == "develop"'
script:
- cp target/*.jar /opt/backend-staging/app.jar
- sudo systemctl restart backend-staging
# 生产环境:手动审批
deploy-prod:
stage: deploy-prod
tags: [shell]
dependencies: [build]
environment:
name: production
url: http://172.19.90.78
rules:
- if: '$CI_COMMIT_BRANCH == "main"'
when: manual # ← 必须人工点击"执行"
script:
- cp target/*.jar /opt/backend/app.jar
- sudo systemctl restart backend
6.3 构建 Docker 镜像并推送
stages:
- build
- dockerize
build:
stage: build
tags: [docker]
image: maven:3.9-eclipse-temurin-21-alpine
script:
- mvn clean package -DskipTests
artifacts:
paths: [target/*.jar]
dockerize:
stage: dockerize
tags: [docker]
image: docker:24
services:
- docker:24-dind # ← Docker in Docker
dependencies: [build]
script:
- cp target/*.jar app.jar
- docker build -t myapp:$CI_COMMIT_SHA .
- docker push myapp:$CI_COMMIT_SHA
七、内置变量大全
| 变量 | 示例值 | 说明 |
|---|---|---|
$CI_COMMIT_BRANCH | main | 当前分支名 |
$CI_COMMIT_TAG | v1.0.0 | 当前标签名 |
$CI_COMMIT_SHA | a1b2c3d... | 当前 commit 完整哈希 |
$CI_COMMIT_SHORT_SHA | a1b2c3d | 当前 commit 短哈希 |
$CI_PROJECT_NAME | my-project | 项目名 |
$CI_PROJECT_ID | 42 | 项目 ID |
$CI_PIPELINE_ID | 123 | 流水线 ID |
$CI_JOB_NAME | build_job | 当前 Job 名 |
$CI_JOB_STAGE | build | 当前阶段名 |
$CI_REGISTRY | 172.19.90.78:8080 | GitLab 镜像仓库地址 |
$CI_REGISTRY_USER | gitlab-ci-token | 镜像仓库用户名 |
$CI_REGISTRY_PASSWORD | xxx | 镜像仓库密码 |
使用示例:
script:
- echo "当前分支是 $CI_COMMIT_BRANCH"
- echo "Commit ID 是 $CI_COMMIT_SHA"
- docker build -t myapp:$CI_COMMIT_SHORT_SHA .
八、常见错误排查
❌ 1. 流水线没触发
检查:
- 文件名是否正确?必须是
.gitlab-ci.yml(前面有点) - 文件是否在仓库根目录?
- 是否推送到 GitLab?本地修改不触发,必须
git push
❌ 2. Job 一直 pending(橙色)
原因:没有匹配的 Runner。
排查:
tags是否和 Runner 标签一致?- Runner 是否在线?GitLab 网页 Settings → CI/CD → Runners 查看
- Runner 是否分配给当前项目?
❌ 3. script config should be a string or a nested array
原因:YAML 格式错误,通常是缩进问题。
解决:
- 确保用空格缩进,不要用 Tab
- 冒号后面必须有空格:
stage: build - 列表项前面是横杠 + 空格:
- echo "hello"
❌ 4. No such file or directory
原因:artifacts 没传递,或路径写错。
排查:
dependencies是否写了?artifacts.paths路径是否正确?- 两个 Job 的
paths是否匹配?
# build_job 输出到 backend/target/
artifacts:
paths: [backend/target/*.jar]
# deploy_job 要从 backend/target/ 读取
dependencies: [build_job]
script:
- ls backend/target/ # ← 路径要对
❌ 5. Permission denied
原因:Runner 用户没有文件操作权限。
解决:
# 给目录赋权
sudo chown -R gitlab-runner:gitlab-runner /opt/backend
sudo chmod 755 /opt/backend
# 给 sudo 权限
sudo tee /etc/sudoers.d/gitlab-runner << 'EOF'
gitlab-runner ALL=(ALL) NOPASSWD: /bin/systemctl restart backend
EOF
❌ 6. 前端页面刷新 404
原因:Nginx 没有配置单页应用回退。
解决:Nginx 配置加上:
location / {
try_files $uri $uri/ /index.html;
}
🎯 总结
| 字段 | 作用 | 必填 |
|---|---|---|
stages | 定义阶段顺序 | 是 |
script | 执行命令 | 是 |
tags | 指定 Runner | 否(但建议写) |
image | Docker 镜像 | 否(Docker Executor 需要) |
artifacts | 传递产物 | 否 |
cache | 缓存依赖 | 否 |
dependencies | 依赖产物 | 否 |
rules | 触发条件 | 否 |
when | 执行时机 | 否 |
needs | 并行依赖 | 否 |
environment | 环境追踪 | 否 |
📅 文档生成时间:2026-08-18 📝 用途:GitLab CI/CD
.gitlab-ci.yml配置参考手册