CI/CD 持续集成与持续交付¶
一、为什么需要 CI/CD?¶
没有 CI/CD 的痛点¶
| 问题 | 具体表现 |
|---|---|
| 集成地狱 | 多人开发,代码合并时大量冲突,"最后一天合代码"噩梦 |
| 手动部署不可靠 | 人工执行命令,步骤遗漏、环境差异导致线上故障 |
| 发布周期长 | 测试→修复→测试循环,一个版本发布需要数周 |
| 回滚困难 | 出问题不知道回滚到哪个版本,回滚步骤复杂 |
| 环境不一致 | "在我机器上能跑",开发/测试/生产环境差异导致 bug |
CI/CD 解决的核心问题¶
flowchart LR
A[频繁提交\n小步快跑] -->|CI 自动验证| B[快速发现问题\n降低集成成本]
B -->|CD 自动部署| C[快速交付价值\n缩短反馈周期]
C -->|监控+回滚| D[降低发布风险\n快速恢复]
二、CI/CD 流水线全流程¶
flowchart LR
DEV[开发者\n提交代码] --> GIT[Git 仓库\n代码托管]
GIT -->|触发 Webhook| CI[CI 流水线\nJenkins/GitLab CI]
CI --> BUILD[编译构建\nmvn package]
BUILD --> TEST[自动化测试\n单元+集成测试]
TEST --> SCAN[代码扫描\nSonarQube]
SCAN --> DOCKER[构建镜像\nDocker Build & Push]
DOCKER --> STAGING[部署测试环境\n自动]
STAGING --> REVIEW[人工验收\nQA 测试]
REVIEW -->|审批通过| PROD[部署生产环境\n蓝绿/金丝雀]
PROD --> MONITOR[监控告警\nPrometheus/Grafana]
MONITOR -->|异常| ROLLBACK[自动/手动回滚]
各阶段说明¶
| 阶段 | 工具 | 目的 |
|---|---|---|
| 代码托管 | GitLab / GitHub | 版本管理,触发流水线 |
| 编译构建 | Maven / Gradle | 验证代码可编译 |
| 单元测试 | JUnit / Mockito | 验证核心逻辑正确性 |
| 代码扫描 | SonarQube | 检测代码质量、安全漏洞 |
| 镜像构建 | Docker | 打包为可部署的镜像 |
| 镜像仓库 | Harbor / ECR | 存储和管理镜像版本 |
| 部署编排 | Kubernetes / Helm | 自动化部署到集群 |
| 监控告警 | Prometheus + Grafana | 实时监控,异常告警 |
三、常见部署策略¶
3.1 蓝绿部署(Blue-Green Deployment)¶
flowchart TD
LB[负载均衡器] -->|当前流量 100%| BLUE[蓝环境\n旧版本 v1]
LB -.->|切换后 100%| GREEN[绿环境\n新版本 v2]
subgraph 部署步骤
S1[1. 部署新版本到绿环境]
S2[2. 测试绿环境]
S3[3. 切换负载均衡器流量到绿]
S4[4. 观察一段时间]
S5[5a. 正常:下线蓝环境]
S6[5b. 异常:切回蓝环境]
S1 --> S2 --> S3 --> S4
S4 --> S5
S4 --> S6
end
| 维度 | 说明 |
|---|---|
| 优点 | 回滚极快(秒级切换),零停机发布 |
| 缺点 | 资源成本翻倍,需要维护两套环境 |
| 适用场景 | 对回滚速度要求极高的核心系统 |
3.2 金丝雀发布(Canary Release)¶
flowchart LR
USER[用户流量] --> LB[负载均衡器]
LB -->|95% 流量| OLD[旧版本集群\nv1]
LB -->|5% 流量| NEW[金丝雀节点\nv2]
NEW -->|观察指标正常| EXPAND[逐步扩大\n5%→20%→50%→100%]
NEW -->|发现问题| ROLLBACK[立即回滚\n将金丝雀节点切回 v1]
| 维度 | 说明 |
|---|---|
| 优点 | 风险可控,真实流量验证,问题影响范围小 |
| 缺点 | 需要流量控制能力,新旧版本共存期间需兼容 |
| 适用场景 | 大型系统,功能变更影响面广,需要灰度验证 |
3.3 滚动更新(Rolling Update)¶
flowchart LR
subgraph 滚动更新过程
A[实例1 v1] -->|替换| A2[实例1 v2]
B[实例2 v1] -->|替换| B2[实例2 v2]
C[实例3 v1] -->|替换| C2[实例3 v2]
A2 -->|完成后| B
B2 -->|完成后| C
end
| 维度 | 说明 |
|---|---|
| 优点 | 资源利用率高,无需额外环境 |
| 缺点 | 回滚慢,更新期间新旧版本共存,需要接口向后兼容 |
| 适用场景 | 资源有限,可接受短暂新旧版本共存 |
| Kubernetes 支持 | kubectl rollout 原生支持,maxSurge 和 maxUnavailable 控制节奏 |
三种策略对比¶
| 策略 | 资源成本 | 回滚速度 | 风险控制 | 复杂度 |
|---|---|---|---|---|
| 蓝绿部署 | 高(2倍) | ⭐⭐⭐⭐⭐ 秒级 | 中(全量切换) | 低 |
| 金丝雀发布 | 低 | ⭐⭐⭐ 需要操作 | 高(小流量验证) | 高 |
| 滚动更新 | 低 | ⭐⭐ 较慢 | 低(全量替换) | 中 |
四、Dockerfile 与镜像构建最佳实践¶
# 多阶段构建:减小最终镜像体积
# 第一阶段:构建
FROM maven:3.9-openjdk-17 AS builder
WORKDIR /app
COPY pom.xml .
# 先复制 pom.xml 并下载依赖(利用 Docker 层缓存)
RUN mvn dependency:go-offline
COPY src ./src
RUN mvn package -DskipTests
# 第二阶段:运行(只包含 JRE,不包含 Maven 和源码)
FROM openjdk:17-jre-slim
WORKDIR /app
COPY --from=builder /app/target/*.jar app.jar
EXPOSE 8080
ENTRYPOINT ["java", "-jar", "app.jar"]
最佳实践: - 使用多阶段构建,最终镜像只包含运行时依赖 - 将不常变化的层(依赖)放在前面,利用 Docker 层缓存加速构建 - 不要在镜像中存储敏感信息(密码、密钥),通过环境变量或 Secret 注入
五、面试高频问题¶
Q1:CI 和 CD 的区别是什么?
- CI(持续集成):开发者频繁提交代码,自动触发构建和测试,快速发现集成问题。目标是"代码随时可构建"。
- CD(持续交付):在 CI 基础上,自动部署到测试环境,人工审批后部署到生产。目标是"代码随时可发布"。
- CD(持续部署):更进一步,通过所有测试后自动部署到生产,无需人工干预。
Q2:蓝绿部署和金丝雀发布如何选择?
- 对回滚速度要求极高(核心支付、交易系统)→ 蓝绿部署
- 需要真实流量验证、风险控制(大型电商、社交平台)→ 金丝雀发布
- 资源有限、变更风险低 → 滚动更新
Q3:如何保证生产环境和测试环境的一致性?
使用 Docker 镜像:同一个镜像在测试和生产环境运行,消除环境差异。配合 Kubernetes 的 ConfigMap 和 Secret 管理环境差异配置,而不是修改镜像。
Q4:流水线中哪个阶段最容易出问题?
实践中最常见的问题: 1. 测试覆盖率不足:单元测试只覆盖 happy path,边界情况漏测 2. 环境配置差异:测试环境数据库版本、配置与生产不一致 3. 数据库变更未纳入流水线:代码部署了但 DDL 没执行,导致启动失败
复习检验标准:能否画出 CI/CD 流水线的完整流程?能否说出三种部署策略的适用场景?能否解释蓝绿部署的回滚原理?