第六篇:《GitLab CI 进阶:多环境部署与环境变量管理》

在上一篇中,我们学会了如何用 .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 关键字记录部署历史,支持一键回滚

相关推荐
浮江雾9 小时前
Flutter第五节------Flutter开发环境配置与项目创建指南
开发语言·前端·git·学习·flutter·入门·函数
aramae13 小时前
C++11:现代C++的里程碑
c语言·开发语言·c++·windows·git·后端
BerryS3N15 小时前
VS Code Git 工作树:解锁多分支并行开发新体验
大数据·git·elasticsearch
工具派17 小时前
从 git commit 到交付 PDF:我的一条在线工具流实录
git·elasticsearch·pdf
xixingzhe21 天前
软件开发测试分层与CI/CD、沙箱使用完整指南
ci/cd·测试
小二·1 天前
Next.js 15 企业级实战:从项目搭建到CI/CD全链路(App Router + Server Components + Turbopack)
javascript·ci/cd·turbopack
咋吃都不胖lyh1 天前
提交(Commit)到本地的 Git 仓库,然后才能推送(Push)到远程的 Git 仓库
git
__zRainy__1 天前
VS Code 自动同步如何重写 Git 历史:一次重复合并冲突的排查与恢复
git
IT码农-爱吃辣条2 天前
AI Codeview 安装与使用教程
git·ai