在上一篇中,我们学会了如何用 .gitlab-ci.yml 定义基础的 CI 流水线。但在实际项目中,代码需要部署到开发(dev)、测试(staging)、生产(prod)等多个环境,每个环境有不同的配置(数据库地址、API 密钥、域名等)。如果为每个环境维护一份独立的 .gitlab-ci.yml,维护成本将不可接受。本文深入讲解 GitLab CI 的多环境部署策略:如何通过分支策略实现环境隔离、如何使用 rules 精细控制 Job 触发条件、如何通过 CI/CD 变量管理不同环境的配置,以及如何使用 environment 关键字记录部署历史和实现一键回滚。
一、多环境部署的典型策略
在 GitLab CI 中,最常用的多环境部署策略是 "分支即环境"(Branch as Environment) :

这种策略的核心思想是:分支决定了代码的去向。推送到不同分支,触发不同的部署行为。
💡 替代方案:如果不想用分支来区分环境,也可以使用 环境变量驱动 的方式------通过同一个分支配合不同的变量值部署到不同环境。但分支策略更直观、更安全(天然隔离),是 GitLab 官方推荐的做法。
二、使用 rules 控制 Job 触发条件
在 GitLab CI 12.3 之后,官方推荐使用 rules 替代已废弃的 only / except 语法。rules 更灵活、可读性更强,支持条件组合和作业级控制。
2.1 基础规则:按分支匹配
yaml
# 仅在 main 分支执行
deploy-prod:
stage: deploy
script:
- ./deploy.sh prod
rules:
- if: $CI_COMMIT_BRANCH == "main"
when: on_success
- when: never # 其他分支不执行
rules 数组从上到下依次匹配,第一条匹配成功的规则生效。末尾的 when: never 确保未匹配的分支不会执行该 Job。
2.2 多环境部署配置
下面是一个完整的 .gitlab-ci.yml,展示了如何通过 rules 实现分支驱动的多环境部署:
yaml
stages:
- build
- test
- deploy-dev
- deploy-staging
- deploy-prod
# 公共构建 Job(所有分支都执行)
build:
stage: build
script:
- mvn clean package
artifacts:
paths:
- target/*.jar
# 单元测试(所有分支都执行)
test:
stage: test
script:
- mvn test
# 部署到开发环境(develop 分支自动部署)
deploy-dev:
stage: deploy-dev
script:
- echo "部署到开发环境..."
- ./deploy.sh dev
environment:
name: dev
url: https://dev.myapp.example.com
rules:
- if: $CI_COMMIT_BRANCH == "develop" && $CI_PIPELINE_SOURCE == "push"
when: on_success
- when: never
# 部署到预发布环境(release/* 分支自动部署)
deploy-staging:
stage: deploy-staging
script:
- echo "部署到预发布环境..."
- ./deploy.sh staging
environment:
name: staging
url: https://staging.myapp.example.com
rules:
- if: $CI_COMMIT_BRANCH =~ /^release\/.*/ && $CI_PIPELINE_SOURCE == "push"
when: on_success
- when: never
# 部署到生产环境(main 分支手动触发)
deploy-prod:
stage: deploy-prod
script:
- echo "部署到生产环境..."
- ./deploy.sh prod
environment:
name: production
url: https://myapp.example.com
rules:
- if: $CI_COMMIT_BRANCH == "main" && $CI_PIPELINE_SOURCE == "push"
when: manual # 需要人工确认
allow_failure: false
- when: never
关键设计:
deploy-dev 和 deploy-staging 使用 when: on_success,代码推送后自动部署
deploy-prod 使用 when: manual,需要人工点击按钮确认后才执行
$CI_PIPELINE_SOURCE == "push" 确保只有代码推送才触发,排除合并请求(MR)等场景
2.3 使用 changes 缩小触发范围
如果只想在特定文件变更时才触发部署(例如只有 src/ 目录变化才构建),可以结合 changes 关键字:
yaml
deploy-prod:
stage: deploy-prod
script:
- ./deploy.sh prod
rules:
- if: $CI_COMMIT_BRANCH == "main"
changes:
- "src/**/*"
- "Dockerfile"
when: manual
- when: never
三、CI/CD 变量:环境配置的核心
不同环境的差异(数据库地址、API 密钥、域名等)应该通过 CI/CD 变量(Variables) 来管理,而不是硬编码在 .gitlab-ci.yml 中。
3.1 变量的层级与优先级
GitLab CI 的变量分为多个层级,优先级从高到低依次为:
手动触发变量:通过 API 或 UI 手动触发时传入的变量(优先级最高)
项目级变量:在项目 Settings → CI/CD → Variables 中定义
组级变量:在组级别定义,该组下所有项目共享
实例级变量:在整个 GitLab 实例级别定义
流水线级变量:在 .gitlab-ci.yml 中使用 variables 关键字定义
内置预定义变量:GitLab 自动提供的变量(如 CI_COMMIT_SHA)
💡 实践建议:通用配置(如 Docker Registry 地址)放在组级变量;环境敏感信息(如生产数据库密码)放在项目级变量并开启保护;硬编码的默认值放在 .gitlab-ci.yml 的 variables 中。
3.2 定义变量
在 UI 中添加变量:
进入项目 → Settings → CI/CD
展开 Variables 部分
点击 Add variable
填写 Key 和 Value
关键选项:

⚠️ 重要提醒:Mask 只能隐藏长度 ≥ 8 个字符且不包含特殊字符的值。已在日志中打印过的值无法再设置为 Masked。
3.3 在 .gitlab-ci.yml 中使用变量
在脚本中通过 $变量名 引用变量:
yaml
deploy-prod:
stage: deploy-prod
script:
- echo "部署到 $CI_ENVIRONMENT_NAME 环境"
- curl -H "Authorization: Bearer $DEPLOY_TOKEN" https://api.example.com/deploy
3.4 为不同环境配置不同变量
在 GitLab UI 中,可以为不同环境定义同名的变量,通过 环境范围(Environment Scope) 区分:

在 Job 中通过 environment: name 匹配对应的变量值:
yaml
deploy-dev:
environment:
name: dev
script:
- echo "数据库地址: $DB_HOST" # 自动使用 dev 的值
3.5 使用文件类型变量存储证书
GitLab CI 支持 文件类型变量(File) ,适合存储证书、私钥等文件内容:
在 UI 中添加变量时,将 Type 选择为 File
将证书内容粘贴到 Value 中
GitLab 会在 Runner 中自动生成一个临时文件,路径通过变量名传递
yaml
deploy:
script:
- cat $CERT_FILE # $CERT_FILE 是临时文件路径
- curl --cacert $CERT_FILE https://secure-api.example.com
3.6 在 Job 间传递动态变量
before_script 中声明的变量在后续 Job 中无法直接访问。如果需要跨 Job 传递动态值(如动态生成的版本号),可以使用 dotenv 报告:
yaml
stages:
- build
- deploy
build:
stage: build
script:
- echo "VERSION=v1.0.0" > version.env
artifacts:
reports:
dotenv: version.env # 将 version.env 中的变量暴露给后续 Job
deploy:
stage: deploy
script:
- echo "部署版本: $VERSION" # 可以读取到 build Job 中设置的 VERSION
四、使用 environment 记录部署历史
environment 关键字不仅用于变量匹配,还能在 GitLab UI 中记录每次部署的历史,支持一键回滚。
4.1 基本用法
yaml
deploy-prod:
stage: deploy-prod
script:
- ./deploy.sh
environment:
name: production
url: https://myapp.example.com
在 GitLab 项目的 Operations → Environments 页面,可以看到:
每个环境的部署历史(时间、版本、提交信息)
当前部署的版本
回滚按钮:点击即可回滚到上一个版本
4.2 动态环境名称
对于动态生成的环境(如每个 feature 分支独立部署),可以使用预定义变量动态生成环境名:
yaml
deploy-feature:
stage: deploy
script:
- ./deploy.sh
environment:
name: review/$CI_COMMIT_REF_SLUG
url: https://$CI_COMMIT_REF_SLUG.myapp.example.com
rules:
- if: $CI_COMMIT_BRANCH =~ /^feature\/.*/
$CI_COMMIT_REF_SLUG 是 GitLab 预定义变量,会将分支名转换为合法的 URL 友好字符串。
五、完整的多环境部署示例
下面是一个完整的 .gitlab-ci.yml,集成了本文介绍的所有技术:
yaml
stages:
- build
- test
- deploy-dev
- deploy-staging
- deploy-prod
variables:
MAVEN_OPTS: "-Dmaven.repo.local=.m2/repository"
DOCKER_REGISTRY: "registry.gitlab.com"
cache:
paths:
- .m2/repository/
# ===== 构建 =====
build:
stage: build
image: maven:3.8.4-openjdk-17
script:
- mvn clean package
artifacts:
paths:
- target/*.jar
# ===== 测试 =====
test:
stage: test
image: maven:3.8.4-openjdk-17
script:
- mvn test
artifacts:
reports:
junit: target/surefire-reports/*.xml
# ===== 部署到开发环境 =====
deploy-dev:
stage: deploy-dev
image: docker:latest
services:
- docker:dind
script:
- docker build -t $DOCKER_REGISTRY/$CI_PROJECT_PATH:dev .
- docker push $DOCKER_REGISTRY/$CI_PROJECT_PATH:dev
- kubectl set image deployment/myapp myapp=$DOCKER_REGISTRY/$CI_PROJECT_PATH:dev -n dev
environment:
name: dev
url: https://dev.myapp.example.com
rules:
- if: $CI_COMMIT_BRANCH == "develop" && $CI_PIPELINE_SOURCE == "push"
when: on_success
- when: never
# ===== 部署到预发布环境 =====
deploy-staging:
stage: deploy-staging
image: docker:latest
services:
- docker:dind
script:
- docker build -t $DOCKER_REGISTRY/$CI_PROJECT_PATH:staging .
- docker push $DOCKER_REGISTRY/$CI_PROJECT_PATH:staging
- kubectl set image deployment/myapp myapp=$DOCKER_REGISTRY/$CI_PROJECT_PATH:staging -n staging
environment:
name: staging
url: https://staging.myapp.example.com
rules:
- if: $CI_COMMIT_BRANCH == "release" && $CI_PIPELINE_SOURCE == "push"
when: on_success
- when: never
# ===== 部署到生产环境 =====
deploy-prod:
stage: deploy-prod
image: docker:latest
services:
- docker:dind
script:
- docker build -t $DOCKER_REGISTRY/$CI_PROJECT_PATH:prod .
- docker push $DOCKER_REGISTRY/$CI_PROJECT_PATH:prod
- kubectl set image deployment/myapp myapp=$DOCKER_REGISTRY/$CI_PROJECT_PATH:prod -n production
environment:
name: production
url: https://myapp.example.com
rules:
- if: $CI_COMMIT_BRANCH == "main" && $CI_PIPELINE_SOURCE == "push"
when: manual
allow_failure: false
- when: never
六、小结
多环境策略:使用 "分支即环境" 策略,develop → dev、release/* → staging、main → prod
触发控制:使用 rules 替代 only/except,按分支、来源、文件变更精细控制 Job 触发
变量管理:通过 CI/CD 变量存储环境差异,利用 Protect 和 Mask 保障安全
部署记录:使用 environment 关键字记录部署历史,支持一键回滚