带人工确认的生产部署流水线
.gitlab-ci.yml
============================================
1. 定义流水线阶段,按顺序执行
============================================
stages:
-
build # 第1阶段:编译 + 打包(install 会下载依赖并打包成 jar/war)
-
test # 第2阶段:单元测试 + 集成测试(并行执行)
-
deploy # 第3阶段:部署到各环境
============================================
2. 全局变量,所有 job 都可以引用
============================================
variables:
APP_NAME: "my-app" # 应用名称
DOCKER_IMAGE: "registry.example.com/{APP_NAME}:{CI_COMMIT_SHA}" # Docker 镜像地址(带提交SHA唯一标识)
MAVEN_OPTS: "-Dmaven.repo.local=.m2/repository" # Maven 本地仓库路径(利用 CI cache 加速)
DOCKER_DRIVER: overlay2 # Docker 存储驱动(Runner 机器配置)
============================================
3. Build 阶段 - 编译 + 打包 + 构建 Docker 镜像
============================================
build:
stage: build # 属于 build 阶段
image: maven:3.9-openjdk-17 # Maven + JDK 17 运行环境
services: # 关联 Docker 服务(让 Maven 能够运行 Docker 命令)
- docker:24-dind # Docker-in-Docker,Runner 内运行 Docker
script: # 核心执行命令
---------- 编译打包 ----------
-
mvn clean package -DskipTests # 编译代码 + 跳过测试打包 jar(Dockerfile 需要)
-
ls -la target/*.jar # 确认 jar 包生成成功(调试用)
---------- Docker 登录 ----------
- echo "CI_REGISTRY_PASSWORD" \| docker login -u "CI_REGISTRY_USER" --password-stdin $CI_REGISTRY # 登录镜像仓库
---------- 构建镜像 ----------
-
docker build -t ${DOCKER_IMAGE} . # 根据 Dockerfile 构建镜像,标签为本次提交SHA # ---------- 推送镜像 ----------
-
docker push ${DOCKER_IMAGE} # 推送到镜像仓库,供 K8s 部署使用
artifacts: # 产物:下游 job 可以使用
paths:
- target/*.jar # 打包产物(如果需要传递给其他 job)
expire_in: 1 hour # 产物过期时间
reports: # 可选:SonarQube 代码质量报告
junit: target/surefire-reports/*.xml
cache: # 缓存 Maven 依赖,加速下次构建
key: "${CI_COMMIT_REF_SLUG}" # 按分支缓存,避免分支间互相覆盖
paths:
- .m2/repository # Maven 本地仓库(下载一次后复用)
only: # 触发条件
- main # 仅 main 分支 push 时执行# ============================================
4. Test 阶段 - 单元测试
============================================
test_unit:
stage: test # 属于 test 阶段
image: maven:3.9-openjdk-17 # Maven + JDK 17 运行环境
script: # 执行命令
- mvn test # 运行 src/test/java 下的单元测试
artifacts: # 产物:测试报告(GitLab UI 直接查看)
when: always # 即使失败也生成报告
paths:
- target/surefire-reports/ # Maven Surefire 测试报告(XML 格式)
reports: # 解析为标准格式,供 GitLab 展示
junit: target/surefire-reports/TEST-*.xml # GitLab 自动解析,显示测试通过/失败数
coverage_report: # 可选:JaCoCo 覆盖率报告
coverage_format: cobertura
path: target/site/jacoco/jacoco.xml
coverage: '/Total.*?([0-9]{1,3})%/' # 从日志中提取覆盖率百分比
needs: [build] # 等待 build job 完成后才执行(从 build 的 artifacts 继承)
cache:
key: "${CI_COMMIT_REF_SLUG}"
paths:
- .m2/repository
only:
- main
============================================
5. Test 阶段 - 集成测试
============================================
test_integration:
stage: test # 属于 test 阶段(与 test_unit 并行)
image: maven:3.9-openjdk-17 # Maven + JDK 17 运行环境
services: # 集成测试需要依赖真实中间件
- postgres:13 # PostgreSQL 数据库容器(测试时连接)
- redis:7 # Redis 缓存容器(如果项目用到)
variables: # Job 局部变量,覆盖全局变量
---------- Spring Boot 测试配置(通过系统属性注入) ----------
SPRING_DATASOURCE_URL: "jdbc:postgresql://postgres:5432/testdb" # 数据库连接地址(postgres 是 service 名称)
SPRING_DATASOURCE_USERNAME: "test" # 数据库用户名
SPRING_DATASOURCE_PASSWORD: "test" # 数据库密码
script:
---------- 集成测试(使用 Spring Boot Test) ----------
- mvn verify -Dspring.profiles.active=ci # 运行集成测试(激活 ci 配置,排除本地文件依赖)
---------- 可选:运行指定集成测试类 ----------
- mvn test -Dtest=OrderServiceIntegrationTest
artifacts:
when: always
paths:
-
target/surefire-reports/ # Maven Surefire 报告
-
target/failsafe-reports/ # Maven Failsafe 集成测试报告(如果用 failsafe 插件)
reports:
junit: target/surefire-reports/TEST-*.xml # GitLab 解析测试结果
needs: [build] # 等待 build 完成
cache:
key: "${CI_COMMIT_REF_SLUG}"
paths:
- .m2/repository
allow_failure: true # ⚠️ 允许失败,不阻塞后续流程(根据业务决定)
only:
- main
============================================
6. Deploy 阶段 - 部署到测试环境(自动化,无需人工确认)
============================================
deploy_test:
stage: deploy # 属于 deploy 阶段
image: bitnami/kubectl:latest # kubectl 操作 K8s
before_script: # 执行 script 前的准备工作
- kubectl config use-context test-cluster # 切换到测试集群上下文
script:
---------- 替换镜像版本 ----------
- kubectl set image deployment/{APP_NAME} app={DOCKER_IMAGE} # 更新 K8s Deployment 的镜像版本
---------- 等待滚动更新 ----------
- kubectl rollout status deployment/${APP_NAME} --timeout=300s # 等待滚动更新完成,超时5分钟
---------- 确认版本 ----------
-
kubectl rollout history deployment/${APP_NAME} # 查看滚动历史(调试用)
-
kubectl get pods -l app=${APP_NAME} # 查看 pod 状态(确认启动成功)
environment: # 声明环境,GitLab UI 追踪部署历史
name: test # 环境名称
url: https://test.example.com # 测试环境访问地址
needs: [test_unit, test_integration] # 等待两个测试 job 都完成
only:
- main
============================================
7. Deploy 阶段 - 部署到预发布环境(自动化)
============================================
deploy_staging:
stage: deploy # 属于 deploy 阶段
image: bitnami/kubectl:latest # kubectl 操作 K8s
before_script:
- kubectl config use-context staging-cluster # 切换到预发布集群
script:
-
kubectl set image deployment/{APP_NAME} app={DOCKER_IMAGE} # 更新镜像版本
-
kubectl rollout status deployment/${APP_NAME} --timeout=300s # 等待滚动更新完成
-
kubectl get pods -l app=${APP_NAME} # 确认 Pod 运行正常
environment:
name: staging # 环境名称
url: https://staging.example.com # 预发布环境地址
on_stop: rollback_staging # 🔑 环境停止时触发回滚 job
needs: [test_unit, test_integration] # 等待测试完成
only:
- main
============================================
8. 预发布环境人工审批(关键卡点:确保验收通过后再上生产)
============================================
approve_staging: # job 名称(纯人工审批节点)
stage: deploy # 同一 deploy 阶段(不占 Runner 资源)
script:
---------- 无实际命令,GitLab UI 会显示 Play 按钮 ----------
- echo "✅ 预发布环境验收通过,等待生产部署审批" # GitLab 日志中显示提示信息
environment:
name: staging # 关联到 staging 环境
action: prepare # 🔑 暂停预发布环境,等待后续操作
needs: [deploy_staging] # 必须等预发布部署完成
when: manual # 🔑 核心:人工手动点击才触发,不自动执行
allow_failure: false # 不允许失败
only:
- main
============================================
9. 生产环境部署(最高风险:人工确认 + 前置审批通过)
============================================
deploy_production: # job 名称
stage: deploy # 属于 deploy 阶段
image: bitnami/kubectl:latest # kubectl 操作生产 K8s 集群
before_script:
- kubectl config use-context prod-cluster # 切换到生产集群(⚠️ 操作需谨慎)
script:
---------- 正式部署 ----------
-
kubectl set image deployment/{APP_NAME} app={DOCKER_IMAGE} # 更新生产镜像版本
-
kubectl rollout status deployment/${APP_NAME} --timeout=600s # 等待滚动更新完成(生产超时可设更长)
---------- 健康检查确认 ----------
-
kubectl get pods -l app=${APP_NAME} # 确认所有 Pod 运行成功
-
kubectl describe deployment ${APP_NAME} | grep -A5 "RollingUpdateStrategy:" # 查看滚动策略
---------- 记录部署信息 ----------
- echo "🚀 生产环境部署完成!镜像:{DOCKER_IMAGE},提交:{CI_COMMIT_SHA}"
environment:
name: production # 环境名称
url: https://www.example.com # 生产环境地址
on_stop: rollback_production # 🔑 环境停止时触发回滚 job
needs: [approve_staging] # 🔑 关键:必须等预发布审批通过
when: manual # 🔑 核心:必须人工点击,不自动执行
allow_failure: false # 不允许失败,失败则阻塞流水线
only:
- main
resource_group: production # 🔑 可选:生产环境加锁,防止多人同时触发
============================================
10. 生产环境回滚(快速回退到上一稳定版本)
============================================
rollback_production: # job 名称
stage: deploy # 同一 deploy 阶段
image: bitnami/kubectl:latest # kubectl 操作 K8s
before_script:
- kubectl config use-context prod-cluster # 切换到生产集群
script:
---------- 回滚到上一版本 ----------
- kubectl rollout undo deployment/{APP_NAME} # 🔑 kubectl 内置回滚命令,秒级回退 - kubectl rollout status deployment/{APP_NAME} --timeout=300s # 等待回滚完成
---------- 确认回滚成功 ----------
-
kubectl rollout history deployment/${APP_NAME} # 查看当前版本历史
-
kubectl get pods -l app=${APP_NAME} # 确认 Pod 恢复
-
echo "⏪ 生产环境已回滚到上一版本"
environment:
name: production # 关联生产环境
action: stop # 🔑 标记环境为停止状态
when: manual # 🔑 手动触发,不自动执行
only:
- main
============================================
11. 预发布环境回滚
============================================
rollback_staging: # job 名称
stage: deploy
image: bitnami/kubectl:latest
before_script:
- kubectl config use-context staging-cluster # 切换到预发布集群
script:
-
kubectl rollout undo deployment/${APP_NAME} # 回滚到上一版本
-
kubectl rollout status deployment/${APP_NAME} --timeout=300s # 等待回滚完成
-
kubectl get pods -l app=${APP_NAME} # 确认 Pod 恢复
-
echo "⏪ 预发布环境已回滚"
environment:
name: staging action: stop # 停止预发布环境
when: manual # 手动触发
only:
- main