- Introduced integration layer design with Resilience4j for external vendor calls. - Established API design standards with unified response structures and global exception handling. - Defined configuration and service governance using Kubernetes native solutions. - Implemented observability practices including trace ID propagation and structured logging. - Outlined build and multi-environment deployment strategies using Gradle and GitLab CI/CD. - Specified testing strategies across different layers, utilizing JUnit, MockK, Testcontainers, and WireMock.
4.5 KiB
4.5 KiB
09. 构建与多环境部署
决策
Gradle 多模块统一构建,bootstrap 产出单一 jar/Docker 镜像;沿用现有 GitLab CI/CD → Azure 部署链路(见 Architecture-Diagram/gitlab-cicd-azure-deployment-diagram.drawio)。
结构约定
settings.gradle # include 所有 platform-* / domains/*(见 01-project-structure.md)
build.gradle # 根工程统一 Kotlin/Spring Boot 插件版本、依赖约束
bootstrap/build.gradle # bootJar,构建出可执行 jar
Dockerfile # 基于 bootstrap 的 jar 打镜像
.gitlab-ci.yml # build -> test -> docker build/push -> deploy
Dockerfile 示例(多阶段构建)
# Dockerfile
FROM eclipse-temurin:21-jdk AS build
WORKDIR /workspace
COPY . .
RUN ./gradlew :bootstrap:bootJar --no-daemon
FROM eclipse-temurin:21-jre
WORKDIR /app
COPY --from=build /workspace/bootstrap/build/libs/*.jar app.jar
ENTRYPOINT ["java", "-jar", "app.jar"]
多阶段构建的好处:最终镜像只包含 JRE + 一个 jar,不带 Gradle 缓存、源码、编译工具链,镜像体积和攻击面都更小。
GitLab CI 示例(节选)
# .gitlab-ci.yml
stages:
- build
- test
- package
- deploy
build:
stage: build
script:
- ./gradlew build -x test --no-daemon
test:
stage: test
script:
- ./gradlew test --no-daemon
artifacts:
reports:
junit: '**/build/test-results/test/TEST-*.xml'
docker-build:
stage: package
script:
- docker build -t $CI_REGISTRY_IMAGE:$CI_COMMIT_SHORT_SHA .
- docker push $CI_REGISTRY_IMAGE:$CI_COMMIT_SHORT_SHA
deploy-uat:
stage: deploy
environment: uat
only:
- main
script:
- kubectl set image deployment/conti-backend conti-backend=$CI_REGISTRY_IMAGE:$CI_COMMIT_SHORT_SHA -n retailapp-uat
具体 stage 划分、部署到 Azure AKS 的凭证配置沿用现有 gitlab-cicd-azure-deployment-diagram 里已经跑通的流程,这里只体现"单一镜像、按环境部署"这条主线。
关键规则
- 多环境(dev/uat/prod)通过 K8s namespace + ConfigMap/Secret 区分(见 07-config-governance.md),镜像本身不区分环境,同一个镜像跨环境部署,只是挂载的 ConfigMap/Secret 和
SPRING_PROFILES_ACTIVE不同——避免"UAT 验证过的镜像和 Prod 部署的镜像不是同一个产物"这种环境不一致风险。 - CI 流程顺序:Gradle build(含单元测试)→ 打 Docker 镜像 → 推送镜像仓库 → GitLab CI/CD 触发 Azure/K8s 部署。
- 模块化单体阶段只有一个部署产物(一个 Deployment);如果后续拆分微服务,每个 domain 各自补一份
Dockerfile和 CI job,工程结构上已经按模块划好边界(见 01-project-structure.md),拆分成本较低——本质上是把bootstrap依赖的某个domains/xxx模块摘出来,单独套一层@SpringBootApplication入口和自己的Dockerfile。
附录:为什么坚持"一个镜像走所有环境"
一种常见但有风险的做法是:给每个环境单独打包(比如构建时注入 application-uat.yml 到镜像里),这样看起来"环境隔离更彻底",但实际引入了一个更严重的问题——UAT 验证通过的镜像和 Prod 部署的镜像,字节级别就不是同一个东西,即使代码版本号一样,构建过程中的依赖解析、基础镜像 layer 缓存状态都可能有细微差异,理论上会出现"UAT 测过没问题,Prod 部署后行为不一致"的情况,而且事后很难证明"两次构建到底有没有差异"。
"一个镜像走所有环境"(Build once, deploy many)反过来保证:镜像本身在所有环境完全一致,环境差异只体现在外部注入的配置(ConfigMap/Secret/环境变量)上。这也是 The Twelve-Factor App 里"严格分离构建和运行"这条原则的直接应用。
待补充
- 具体 CI pipeline 的完整 stage 定义和镜像版本/tag 策略。
- 灰度发布/回滚方案。
- Gradle 构建缓存/并行构建的 CI 加速配置。