CI/CD 与 DevOps
Java 架构师 · 可观测与工程化
CI/CD 把"代码到上线"自动化,DevOps 把研发与运维的壁垒打通。目标是快速、安全、可重复地交付——架构师需设计流水线、质量门禁与发布策略,让变更既快又稳。本章深入 CI(持续集成)、CD(持续交付/部署)、不可变制品、Git 工作流,再到 Jenkins Pipeline、GitLab CI、Argo CD GitOps,以及蓝绿/金丝雀发布与回滚的工程细节。
一、核心概念:CI / CD / 制品
- CI(持续集成):频繁合并 + 自动构建测试,尽早暴露冲突。关键是"主干上频繁小步提交",而非长期分支。
- CD(持续交付 / 持续部署):构建产物自动经测试/预发到生产。持续交付需人工审批点;持续部署全自动(无人工卡点,靠强质量门禁兜底)。
- 制品(Artifact):不可变镜像/包(用 commit SHA 打标签),一次构建多处部署,保证环境一致(开发/测试/生产跑同一个镜像,差异只来自配置注入)。
- Git 工作流:GitFlow(分支多、适合发布节奏慢)、Trunk-Based(主干开发,推荐高成熟度团队,缩短反馈环)。
二、质量门禁:流水线的"该停就停"
流水线不是越快越好,要有阻断条件。左移(Shift-Left):把安全与质量检查尽早放入 CI:
- 编译 + 单元测试(覆盖率门禁,如 < 60% 阻断)。
- 静态扫描(SAST,如 SonarQube)、依赖漏洞扫描(如 Trivy)。
- 镜像扫描(CVE)、契约测试(Provider/Consumer)。
- 集成测试 + 性能基线(可选,耗时长放独立阶段)。
门禁失败必须阻断后续阶段并通知负责人,而非"带着失败继续跑"。
三、工具选型对比
| 工具 | 定位 | 特点 | 适用 |
|---|---|---|---|
| Jenkins | 通用 CI 引擎 | 插件多、需自运维、Pipeline as Code | 复杂定制、已有存量 |
| GitLab CI | 一体化 | 与 Git 紧耦合、YAML 定义、Runner 弹性 | GitLab 体系、一体化 |
| Argo CD | GitOps 部署 | 声明式、K8s 原生、自动同步、可回滚 | K8s 环境 GitOps |
四、GitLab CI 流水线示例
stages: [build, test, image, deploy]
build:
stage: build
script: ./mvnw package -DskipTests
test:
stage: test
script: ./mvnw test
coverage: '/Total.*?([0-9]+)%/' # 覆盖率提取
image:
stage: image
script:
- docker build -t registry/order:$CI_COMMIT_SHA . # 用 SHA 打标签(不可变)
- trivy image registry/order:$CI_COMMIT_SHA # 镜像漏洞扫描(门禁)
deploy:
stage: deploy
script: kubectl set image deploy/order order=registry/order:$CI_COMMIT_SHA
only: [main]五、Jenkins Pipeline(声明式)
pipeline {
agent any
stages {
stage('Build') { steps { sh './mvnw package -DskipTests' } }
stage('Test') { steps { sh './mvnw test' } }
stage('Image') { steps { sh 'docker build -t registry/order:${GIT_COMMIT} .' } }
stage('Deploy') { steps { sh 'kubectl apply -f k8s/' } }
}
post { failure { slackSend message: '构建失败: ${BUILD_URL}' } }
}流水线的致命陷阱
- 制品不可变被破坏:同一版本重复构建 → 环境漂移(测试过的镜像 ≠ 上线的镜像)。必须用 commit SHA 打标签,一次构建、多环境复用。
- 质量门禁缺失:跳过单测/扫描直接上线,技术债累积;门禁失败应阻断。
- 手动发布无回滚:发布即"祈祷"。需蓝绿/金丝雀 + 一键回滚。
- 密钥进仓库:凭据硬编码进镜像/清单 → 泄露风险。用 Secret 管理(Vault/K8s Secret/CI 变量),绝不入库。
- 长分支少集成:分支存活数周 → 合并地狱。提倡 Trunk-Based + 小步提交。
六、GitOps:Git 为唯一真实源(Argo CD)
GitOps 思想:Git 仓库里的声明(K8s 清单/Helm values)就是集群期望状态,Argo CD 持续比对"Git 声明"与"集群实际",不一致就自动同步(或告警等待)。变更 = 提 PR,可审计、可回滚(回滚 = 回退 commit)。
优势:
- 可审计:谁、何时、改了什么,Git 历史即审计日志。
- 可回滚:回退一个 commit 即回滚(秒级,比手动 kubectl 可靠)。
- 一致性:集群状态永远向 Git 对齐,杜绝"手动改了集群但没人知道"。
- 漂移检测:有人手动 kubectl edit,Argo CD 发现与 Git 不符 → 自动纠正或告警。
七、发布策略:蓝绿 / 金丝雀
| 策略 | 机制 | 回滚 | 适用 |
|---|---|---|---|
| 蓝绿(Blue/Green) | 两套环境,流量整体切到绿,异常切回蓝 | 切流量即可(瞬时) | 无状态、可整体切换 |
| 金丝雀(Canary) | 1%→10%→100% 渐进放量 | 回滚灰度部分 | 需观察真实流量 |
金丝雀决策依据(必须可观测):错误率、P99 延迟、业务指标(下单成功率)。任一超阈值 → 暂停放量/回滚。配合 Argo Rollouts 可做自动化分析(自动推进或回滚)。
发布即风险:回滚能力是底气
没有回滚能力的发布 = 赌博。回滚必须"一键/秒级":蓝绿靠切流量、金丝雀靠缩灰度、GitOps 靠回退 commit。发布前确认:回滚路径、回滚触发条件、回滚验证(回滚后是否真恢复)。大促/核心变更要"冻结期"(如双11前禁止非必要发布)。
八、最佳实践
- 一次构建、多环境复用同一镜像(配置由环境注入,不进镜像)。
- 发布策略:金丝雀(小流量验证)或蓝绿(瞬时切换),配合可观测验证再放量。
- 用 GitOps 管理 K8s 清单,变更可审计、回滚 = 回退 commit。
- 流水线即代码,纳入版本控制与评审;门禁失败阻断。
- 密钥走 Secret 管理,绝不进仓库;长任务放独立 stage 避免拖慢反馈。
本节小结
CI/CD 把交付变成可重复的工程动作:CI 频繁集成尽早暴露冲突,CD 自动推进到生产,不可变制品(commit SHA 标签)保证环境一致。关键是质量门禁左移且能阻断——把安全/测试挡在合并前而非事后。发布策略上,金丝雀用渐进放量+可观测决策、蓝绿用瞬时切换,二者都要求"一键回滚"作为底气。GitOps(Argo CD)让 Git 成为唯一真实源:变更即 PR、可审计、回滚 = 回退 commit、漂移可检测。架构师要设计的是"又快又稳"的发布机制——用自动化替代人工判断,用回滚能力换发布勇气。
📚 官方文档与参考资料
- Jenkins 文档:https://www.jenkins.io/doc/
- GitLab CI 文档:https://docs.gitlab.com/ee/ci/
- Argo CD 文档(GitOps):https://argo-cd.readthedocs.io/
- Kubernetes 文档(部署/回滚):https://kubernetes.io/docs/
- Helm 文档(清单模板化):https://helm.sh/docs/
✏️ 练习与自测
- 设计题:设计一条 Java 服务的 CI/CD 流水线,列出每个阶段(build/test/image/deploy)的质量门禁与阻断条件(含覆盖率、镜像扫描、契约测试)。
- 设计题:用 Argo CD 实现"回滚到上一个稳定版本",说明 GitOps 下如何操作、为什么比手动 kubectl 回滚更可靠。若有人手动改了集群,会发生什么?
- 设计题:金丝雀发布中,用哪些可观测指标(错误率/P99/业务指标)决定"继续放量还是回滚"?给出自动推进与自动回滚的规则阈值。
- 辨析题:为什么"制品不可变"是 CI/CD 的基石?如果测试通过的镜像在上线前被重新构建,会带来什么风险?如何保证"测试过的就是上线的"?