CHARLIE SAYS

查理如是说
DATE 2026-08-24
THEME
SERIES / JHIPSTER / P-233 · JHipster 项目开发

JHipster 开发 18:CI/CD——从零到自动发布

第 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 的企业
JenkinsJenkinsfile存量 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 怎么打。

策略格式用途
SNAPSHOT1.4.0-SNAPSHOTmain 每次推送,随时可覆盖,永不部署到生产
RELEASE1.4.0release 分支合并时产生,一次成型不可变
语义化版本MAJOR.MINOR.PATCH破坏性变更.功能.修复,对使用方承诺

铁律三条:

  1. 生产环境只允许部署不可变 tag——latest 与 SNAPSHOT 进生产是回滚灾难的开始;
  2. git tag、镜像 tag、Liquibase changelog 版本三者对齐v1.4.0 的镜像带着 db-1.4.0.xml 的变更),排障时三者互查秒定位;
  3. 版本号变更自动化(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
环境部署触发数据验证
devpush 即部署合成数据自动冒烟
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 脚本在加了数据的表上基本是幻想,真正的回滚策略是”前向修复”(见下节)。

回滚策略

三层递进:

  1. 配置回滚(秒级):改 ConfigMap/环境变量回滚,重新滚动实例,数据库不动;
  2. 应用回滚(分钟级)kubectl rollout undo 或重新部署上一个镜像 tag——前提是数据库前向兼容:版本 N+1 的变更未执行破坏性 DDL,或虽已加列但代码兼容缺列;
  3. 前向修复(最后手段):数据库已破坏性变更时,回滚镜像会撞上缺列/多列错误,唯一出路是发 N+2 修复版。

这正是”两段式 schema 变更”的回报:多花一次发布的耐心,换来应用层永远可回滚的自由。另外金丝雀阶段(单实例)发现问题的成本最低,值得为生产发布标配。

secrets 管理

生成 workflow 里 ${{ secrets.* }} 引用平台 secrets store(GitHub Environments / GitLab CI variables / Jenkins credentials),这是第一层。全景是三层:

内容谁管
平台 secrets镜像仓库 token、部署 kubeconfig、Sonar tokenDevOps
K8s Secrets数据库密码、JWT 密钥、Kafka 凭证部署侧(seal 或外部注入)
专用密管数据库主密码、第三方 API keyVault/云 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。

系列导航

← 软件工程 018:阿里巴巴 Java 开发手册 目录 开发工具 018:Linux:创建自建服务 →
← 返回文章列表