Spring Boot 4.0 CI/CD:自动化测试、构建、发布与回滚
前言
手工构建最大的问题不是慢,而是每次步骤都可能不同:有人忘记测试,有人用错 JDK,有人推送了镜像却没有更新部署版本。
本章把测试、构建、镜像发布和 Kubernetes 发布写进 GitHub Actions,同时提供部署失败自动回滚和按历史 revision 手动回滚。
读者需要完成 Docker/Kubernetes 部署章节,并有 GitHub Actions、GHCR 或 Kubernetes 使用经验。源码位于 dvsusan/spring-boot4-demo。
最终结果
新增:
.github/workflows/build.yml
.github/workflows/release.yml
scripts/deploy-k8s.sh
scripts/rollback-k8s.shTag 构建发布不可变镜像;手动触发时可以部署到受保护的生产环境。

图 1:流水线只有在前序阶段通过后才进入下一阶段,部署失败必须留下可恢复状态。
第 1 步:基础 CI 每次提交都测试
.github/workflows/build.yml:
name: build
on:
push:
pull_request:
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-java@v4
with:
distribution: temurin
java-version: "21"
cache: maven
- name: Build and test
run: mvn -B clean verifyCI 必须使用干净环境,避免依赖开发者本地 Maven 缓存或未提交文件。clean 防止旧 class 掩盖编译错误。
第 2 步:验证并保存可执行 JAR
release.yml 的 verify Job:
- name: Verify
run: mvn -B clean verify
- name: Package
run: mvn -B -DskipTests package
- uses: actions/upload-artifact@v4
with:
name: spring-boot4-demo
path: demo-web/target/demo-web-1.0.0-SNAPSHOT.jar
if-no-files-found: errorclean verify 已包含打包,第二步只负责生成便于下载的构建产物;团队也可以合并成一个 Maven 命令。产物上传失败必须让流水线失败,不能悄悄发布空版本。
第 3 步:构建并发布 GHCR 镜像
permissions:
contents: read
packages: write
- uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- uses: docker/build-push-action@v6
with:
context: .
push: true
tags: ${{ steps.meta.outputs.tags }}
cache-from: type=gha
cache-to: type=gha,mode=max标签由 docker/metadata-action 生成。生产使用 Git Tag 和完整 SHA,不使用 latest 作为唯一版本。
第 4 步:部署脚本保持幂等
完整的 scripts/deploy-k8s.sh:
#!/usr/bin/env bash
set -euo pipefail
image="${1:?用法: scripts/deploy-k8s.sh <image:tag>}"
namespace="${NAMESPACE:-spring-boot4-demo}"
deployment="${DEPLOYMENT:-spring-boot4-demo}"
if ! command -v kubectl >/dev/null 2>&1; then
echo "kubectl 不存在,无法部署" >&2
exit 2
fi
# 先应用 Service、探针、资源限制等稳定配置,再替换具体镜像版本。
kubectl apply -k deploy/k8s
kubectl -n "$namespace" set image \
"deployment/$deployment" \
"app=$image"
# 新版本没有在时限内就绪时自动回到上一代 ReplicaSet。
if ! kubectl -n "$namespace" rollout status \
"deployment/$deployment" \
--timeout=180s; then
echo "新版本发布失败,开始自动回滚" >&2
kubectl -n "$namespace" rollout undo "deployment/$deployment"
kubectl -n "$namespace" rollout status \
"deployment/$deployment" \
--timeout=180s
exit 1
fi重复执行同一命令不会创建第二套资源。set -euo pipefail 让缺失参数、kubectl 错误和管道失败立即停止发布。
第 5 步:部署失败自动回滚
脚本判断新 Pod 是否在 180 秒内就绪。失败时执行下面的分支:
if ! kubectl -n "$namespace" rollout status \
"deployment/$deployment" \
--timeout=180s; then
echo "新版本发布失败,开始自动回滚" >&2
kubectl -n "$namespace" rollout undo "deployment/$deployment"
kubectl -n "$namespace" rollout status \
"deployment/$deployment" \
--timeout=180s
exit 1
fi自动回滚后脚本仍返回非零,让 CI 保留失败记录。不能因为旧版本恢复成功,就把本次发布标记成成功。
第 6 步:手动指定历史版本
scripts/rollback-k8s.sh 支持“不带参数回到上一版”和“指定 revision 回到确定版本”两种方式:
#!/usr/bin/env bash
set -euo pipefail
namespace="${NAMESPACE:-spring-boot4-demo}"
deployment="${DEPLOYMENT:-spring-boot4-demo}"
revision="${1:-}"
if ! command -v kubectl >/dev/null 2>&1; then
echo "kubectl 不存在,无法回滚" >&2
exit 2
fi
if [[ -n "$revision" ]]; then
kubectl -n "$namespace" rollout undo \
"deployment/$deployment" \
"--to-revision=$revision"
else
kubectl -n "$namespace" rollout undo "deployment/$deployment"
fi
kubectl -n "$namespace" rollout status \
"deployment/$deployment" \
--timeout=180s在项目根目录执行:
scripts/rollback-k8s.sh
scripts/rollback-k8s.sh 12不带参数时回到上一代 ReplicaSet;指定 revision 时回到明确历史版本:
kubectl -n spring-boot4-demo rollout history \
deployment/spring-boot4-demorevisionHistoryLimit: 5 保留最近五个版本。数量太少会失去回退能力,数量太多会增加控制器管理成本。
第 7 步:手动部署到生产环境
GitHub Actions 手动任务使用 production Environment,并读取加密 Secret KUBE_CONFIG_DATA:
- name: Configure kubectl
run: |
mkdir -p "$HOME/.kube"
echo "$KUBE_CONFIG_DATA" | base64 --decode > "$HOME/.kube/config"
chmod 600 "$HOME/.kube/config"
env:
KUBE_CONFIG_DATA: ${{ secrets.KUBE_CONFIG_DATA }}生产 Environment 应配置审批人、分支限制和部署权限。GitHub 仓库级 Secret 不要让所有 Pull Request 都能读取。
第 8 步:验证流水线文件
本地执行:
bash -n scripts/deploy-k8s.sh
bash -n scripts/rollback-k8s.sh
mvn -B clean verify本章验证中两个 Shell 脚本语法通过,Maven 全量测试通过。GitHub Actions 与真实集群发布没有在本机执行,不能把本地 YAML 校验写成线上发布成功。
发布流程的失败边界
| 阶段失败 | 结果 | 处理 |
|---|---|---|
| 单元测试 | 不生成镜像 | 修复代码并重新提交 |
| 镜像构建 | 不推送不可用镜像 | 检查 Dockerfile 和网络 |
| 镜像推送 | 不更新 Deployment | 检查权限和仓库 |
| Pod readiness | 旧版本继续服务并自动回滚 | 查看探针和日志 |
| 回滚失败 | 进入人工事故流程 | 停止继续发布,必要时运维介入 |
常见问题排查
| 现象 | 原因 | 处理方式 |
|---|---|---|
| GHCR 推送 403 | Token 缺少 packages 权限 | 配置 workflow permissions |
| deploy Job 读不到 Secret | Environment 权限或 Secret 名称错误 | 检查生产 Environment |
| kubectl 使用错误集群 | Kubeconfig 上下文不明确 | 设置 context,部署前输出当前 context |
| 回滚没有历史版本 | revisionHistoryLimit 太小 | 保留足够历史并验证 |
| CI 通过但发布失败 | 测试环境与集群配置差异 | 增加部署后冒烟测试和探针 |
练习题
在部署后增加一次真实 HTTP 冒烟测试:请求 /actuator/health/readiness,连续失败三次则执行 rollback-k8s.sh。验收标准是错误镜像不会在流水线中标记为发布成功。
总结
CI/CD 的核心是同一提交经过同一组验证,再生成不可变产物并渐进发布。回滚能力必须在正常发布前验证,而不是事故发生时临时寻找命令。
第八章完成后,项目已经具备监控、容器部署和自动发布能力。
上一节:Docker 与 Kubernetes 部署
相关阅读:Actuator 与 OpenTelemetry
相关阅读:Testcontainers 集成测试
返回课程目录
13 个企业级实战项目 · 源码 + 教程 + 答疑
简历包装指导AI Agent 实战高并发·微服务Offer 导向持续更新3000+ 同行交流1V1 答疑3 天包退
覆盖高并发、微服务、分布式与 AI Agent:100万QPS短链、企业智能知识库、商城微服务、SaaS点餐、秒杀、刷题小程序、代码生成器、AI Agent 等。券后 ¥129(原价 ¥159),不满意 3 天包退。
