第 17 篇把测试跑全了,这一篇让这些测试”自己跑”——并且一路跑到生产。JHipster 的 ci-cd 子生成器能在五分钟内产出一条完整流水线,但生成器给的是骨架,发布策略(版本、环境、迁移时机、回滚)必须团队自己想清楚。这些决策正是本篇的主菜,骨架部分反而只是配菜。
子生成器:五分钟一条流水线
jhipster ci-cd
# 问答依次选择:
# ? What CI/CD pipeline do you want to generate? GitHub Actions
# ? What tasks/integrations do you want to include? (多选)
# ◉ Build a Docker image (推送到 GHCR)
# ◉ Deploy to Kubernetes cluster
# ◉ Analyze code with SonarQube
三个平台选项(GitHub Actions / GitLab CI / Jenkins)产物对照:
| 选项 | 产物 | 适合 |
|---|---|---|
| GitHub Actions | .github/workflows/*.yml | 开源/ GitHub 托管 |
| GitLab CI | .gitlab-ci.yml | 自建 GitLab 的企业 |
| Jenkins | Jenkinsfile | 存量 Jenkins 体系 |
生成的 GitHub Actions workflow 骨架(注释掉的部分按需打开):
name: Application CI
on: [push, pull_request]
jobs:
github-actions:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-java@v4
with:
distribution: temurin
java-version: 21
cache: maven
- name: Run backend tests
run: ./mvnw verify -Pprod # 单测+集成+打包,prod profile
- name: Run frontend tests
run: npm ci && npm run test -- --coverage
- name: Sonar
run: ./mvnw sonar:sonar
env:
SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
- name: Build and publish Docker image
if: github.ref == 'refs/heads/main'
run: ./mvnw -Pprod verify jib:build -DskipTests
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
值得指出的细节:-Pprod 打包(前端构建产物进 jar、War/Jar 差异在此定型)、缓存声明一行搞定、镜像构建只在 main 分支触发——PR 只验证、main 才产出,这个默认策略很健康,保留它。
镜像构建:jib 无 Dockerfile
JHipster 用 jib 构建 OCI 镜像,pom.xml 里已有插件配置,无需写 Dockerfile:
# 构建并直接推仓库(CI 推荐,不需要 docker daemon)
./mvnw -Pprod verify jib:build \
-Djib.to.image=ghcr.io/myorg/myapp:${VERSION}
# 本地调试:构建到 docker daemon
./mvnw -Pprod verify jib:dockerBuild
jib 的分层缓存(依赖层/资源层/类层分离)让重复构建只推送变化层,CI 镜像阶段从 3 分钟降到 40 秒。另一个隐性收益:没有 Dockerfile 就没有”Dockerfile 里 apt-get 了什么没人知道”的问题,构建输入全部收敛在 pom 与源码里。
版本策略:镜像 tag 是回滚的货币
发布的第一决策:镜像 tag 怎么打。
| 策略 | 格式 | 用途 |
|---|---|---|
| SNAPSHOT | 1.4.0-SNAPSHOT | main 每次推送,随时可覆盖,永不部署到生产 |
| RELEASE | 1.4.0 | release 分支合并时产生,一次成型不可变 |
| 语义化版本 | MAJOR.MINOR.PATCH | 破坏性变更.功能.修复,对使用方承诺 |
铁律三条:
- 生产环境只允许部署不可变 tag——
latest与 SNAPSHOT 进生产是回滚灾难的开始; - git tag、镜像 tag、Liquibase changelog 版本三者对齐(
v1.4.0的镜像带着db-1.4.0.xml的变更),排障时三者互查秒定位; - 版本号变更自动化(
mvn release:*或npm version式脚本),手工改版本号必然出现”忘了改一处”。
环境推进:dev → staging → prod
三个环境一套镜像,差异全部外置(配置见第 21 篇):
flowchart LR
M["main 分支合并\njib 推 GHCR"] --> D["dev\n自动部署"]
D -->|人工确认 + 冒烟通过| S["staging\n自动部署 + E2E 全量"]
S -->|发布窗口 + 审批| P["prod\n手动触发部署"]
style M fill:#e8f0fe
style P fill:#fce8e6
| 环境 | 部署触发 | 数据 | 验证 |
|---|---|---|---|
| dev | push 即部署 | 合成数据 | 自动冒烟 |
| staging | 标记 release 后自动 | 脱敏数据 | E2E 全量 + 业务验收 |
| prod | 手动按钮 + 双人审批 | 生产数据 | 金丝雀(先 1 个实例) |
staging 与 prod 的配置形状必须相同(同类环境变量、同类 secret 键名,值不同)。两环境结构漂移是”staging 好好的、生产起不来”的头号原因,K8s manifest 用同一份 + kustomize overlay 正是为此(第 19 篇展开)。
数据库迁移在流水线中的时机
Liquibase 变更随应用启动自动执行(spring.liquibase.enabled 默认开),这是 dev 的舒适设定。生产要重新决策:
| 方案 | 时机 | 适合 | 风险 |
|---|---|---|---|
| 应用启动自迁移 | 部署时 | 前向兼容的小变更 | 启动变慢;多实例并发迁移靠 Liquibase 锁 |
| 流水线独立步骤 | 部署前 | 大变更/需暂停窗口 | 需要单独的迁移 job 与回滚脚本 |
| 手动执行 | 变更窗口 | 高危 DDL | 人肉环节,慢但可控 |
我们的分级:加列/加索引(前向兼容)走启动自迁移;改列/删列必须两段式发布——版本 N 先兼容新旧结构(代码双读双写或字段冗余),版本 N+1 再清理。流水线里不做任何自动回滚 DDL:Liquibase 的 rollback 脚本在加了数据的表上基本是幻想,真正的回滚策略是”前向修复”(见下节)。
回滚策略
三层递进:
- 配置回滚(秒级):改 ConfigMap/环境变量回滚,重新滚动实例,数据库不动;
- 应用回滚(分钟级):
kubectl rollout undo或重新部署上一个镜像 tag——前提是数据库前向兼容:版本 N+1 的变更未执行破坏性 DDL,或虽已加列但代码兼容缺列; - 前向修复(最后手段):数据库已破坏性变更时,回滚镜像会撞上缺列/多列错误,唯一出路是发 N+2 修复版。
这正是”两段式 schema 变更”的回报:多花一次发布的耐心,换来应用层永远可回滚的自由。另外金丝雀阶段(单实例)发现问题的成本最低,值得为生产发布标配。
secrets 管理
生成 workflow 里 ${{ secrets.* }} 引用平台 secrets store(GitHub Environments / GitLab CI variables / Jenkins credentials),这是第一层。全景是三层:
| 层 | 内容 | 谁管 |
|---|---|---|
| 平台 secrets | 镜像仓库 token、部署 kubeconfig、Sonar token | DevOps |
| K8s Secrets | 数据库密码、JWT 密钥、Kafka 凭证 | 部署侧(seal 或外部注入) |
| 专用密管 | 数据库主密码、第三方 API key | Vault/云 KMS,应用启动拉取 |
红线:secrets 不进 git(包括 .env 文件——历史里躺着一个 .env 的项目我们都见过)、不进镜像层(jib 打的是应用层,别把凭证打进 jar)、不进日志(启动日志打印配置时确认有脱敏)。kubectl create secret 的 manifest 用 sealed-secrets 或 SOPS 加密后才能进库,明文 secret yaml 提交等于没做。
小结
jhipster ci-cd五分钟生成流水线骨架:PR 验证、main 产出、jib 镜像,默认策略健康可直接用;- 镜像 tag 三对齐(git/镜像/changelog),生产只部署不可变版本;
- 环境推进靠同一镜像外置配置,staging 与 prod 结构必须同形;
- 数据库迁移分级:兼容变更随启动,破坏变更两段式,流水线不做自动回滚 DDL;
- 回滚三层:配置回滚、应用回滚(依赖前向兼容 schema)、前向修复;secrets 三层分治,永不进 git。
系列导航
- 上一篇:测试全景——单元到端到端
- 下一篇:容器化与 Kubernetes
- 相关阅读:Liquibase 数据库迁移 · 配置与 Profile 管理 · 生产环境检查清单