CI/CD(Continuous Integration / Continuous Delivery / Continuous Deployment) 不是单一工具,而是一套从代码变更到生产运行的自动化工程体系。它由多个阶段、多种工具、多层策略组合而成,核心目标是:在保障质量的前提下,缩短从"代码提交"到"用户可用"的周期。
下面从理论模型、流水线阶段、工具实现、K8s 集成、高级策略五个维度系统拆解。
一、CI/CD 的三层模型
业界通常将 CI/CD 划分为三个递进的层次,理解它们的边界至关重要:
| 层次 | 全称 | 自动化范围 | 人工干预点 | 目标 |
|---|---|---|---|---|
| CI | Continuous Integration | 代码提交 → 构建 → 测试 | 无(自动触发) | 快速发现集成错误 |
| CDelivery | Continuous Delivery | CI 产物 → 打包 → 部署到预发布/生产(具备发布能力) | 发布到生产需人工审批 | 随时可发布,发布决策权在人 |
| CDeployment | Continuous Deployment | CDelivery 基础上,自动发布到生产 | 无(完全自动) | 变更即时上线,依赖自动化测试与监控 |
关键差异: Continuous Delivery 与 Continuous Deployment 的唯一区别是"生产部署是否自动"。
二、CI/CD 流水线的核心阶段
一个完整的 CI/CD 流水线通常包含 8 个阶段:
┌─────────┐ ┌─────────┐ ┌─────────┐ ┌─────────┐ ┌─────────┐ ┌─────────┐ ┌─────────┐ ┌─────────┐
│ Source │ → │ Build │ → │ Test │ → │ Security│ → │ Package │ → │ Deploy │ → │ Verify │ → │ Monitor │
│ 代码触发│ │ 编译构建│ │ 测试 │ │ 安全扫描│ │ 制品打包│ │ 部署 │ │ 验证 │ │ 监控 │
└─────────┘ └─────────┘ └─────────┘ └─────────┘ └─────────┘ └─────────┘ └─────────┘ └─────────┘
│ │ │ │ │ │ │ │
Git Push Maven/Gradle Unit Test SAST/DAST Docker Build Helm Upgrade Smoke Test Prometheus
Webhook npm/Go Integration Dependency Image Push K8s Apply E2E Test Alert
镜像构建 Test Scan Sign/Scan ArgoCD Sync Chaos Test Rollback
阶段 1:Source(代码触发)
触发源:
- 代码提交:
git push到main/develop分支 - Merge Request:代码审查通过后自动触发
- 定时触发: nightly build(每日构建)
- 上游触发:A 服务构建成功后触发 B 服务构建
关键实践:
# GitLab CI 触发配置
stages:
- build
- test
build-job:
stage: build
# 只有 main 分支和 tag 才触发构建
only:
- main
- tags
# 排除文档修改触发构建
except:
changes:
- "*.md"
- "docs/**/*"
阶段 2:Build(编译构建)
目标:将源代码转换为可运行产物。
典型任务:
- 编译 Java(Maven/Gradle)、Go(go build)、前端(npm run build)
- 生成代码(Protobuf、OpenAPI Generator)
- 静态代码分析(SonarQube)
与 K8s 的衔接:
# ============================================
# Dockerfile:多阶段构建,减小最终镜像体积
# ============================================
# 阶段 1:构建阶段(使用完整 JDK + Maven)
FROM maven:3.9-eclipse-temurin-17 AS builder
WORKDIR /app
# 先复制 pom.xml 下载依赖(利用 Docker 缓存层)
COPY pom.xml .
RUN mvn dependency:go-offline
# 再复制源码编译
COPY src ./src
RUN mvn clean package -DskipTests
# 阶段 2:运行阶段(仅使用 JRE,无构建工具)
FROM eclipse-temurin:17-jre-alpine
WORKDIR /app
# 从构建阶段复制产物
COPY --from=builder /app/target/order-service-*.jar app.jar
EXPOSE 8080
ENTRYPOINT ["java", "-jar", "app.jar"]
阶段 3:Test(测试)
测试金字塔模型:
/\
/ \ E2E 测试(少而精,验证完整链路)
/____\ ↑ 慢、贵、脆弱
/ \ 集成测试(服务间交互)
/________\ ↑ 中等速度
/ \ 单元测试(多而快,覆盖核心业务逻辑)
/____________\ ↓ 快、便宜、稳定
流水线中的测试策略:
| 测试类型 | 工具示例 | 触发时机 | 失败处理 |
|---|---|---|---|
| 单元测试 | JUnit、pytest、Jest | Build 后 | 阻断流水线 |
| 集成测试 | TestContainers、Spring Boot Test | 容器启动后 | 阻断流水线 |
| 契约测试 | Pact、Spring Cloud Contract | 服务间 API 变更时 | 阻断流水线 |
| E2E 测试 | Cypress、Selenium、Playwright | 部署到预发布环境后 | 阻断生产发布 |
| 性能测试 | JMeter、k6、Gatling | 夜间定时或发布前 | 人工判断是否发布 |
K8s 集成测试示例:
# GitLab CI 中使用 TestContainers 进行集成测试
integration-test:
stage: test
image: maven:3.9-eclipse-temurin-17
services:
- name: docker:24-dind
alias: docker
variables:
DOCKER_HOST: tcp://docker:2376
script:
- mvn test -P integration-test
# TestContainers 会自动在 DinD 中启动 MySQL/Redis 容器进行测试
阶段 4:Security(安全扫描)
DevSecOps 理念:安全左移(Shift Left),在开发早期发现漏洞。
| 扫描类型 | 工具 | 作用 | 阶段 |
|---|---|---|---|
| SAST | SonarQube、Checkmarx、Semgrep | 静态代码分析,发现 SQL 注入、XSS 等 | Build 后 |
| DAST | OWASP ZAP、Burp Suite | 动态应用测试,模拟攻击运行中的应用 | 部署后 |
| SCA | Snyk、Trivy、Dependency-Check | 依赖组件漏洞扫描(Log4j 等) | Build 后 |
| Container Scan | Trivy、Clair、Snyk | 容器镜像 OS 漏洞和软件包漏洞 | Package 后 |
| Secret Scan | GitLeaks、TruffleHog | 检测代码中硬编码的密码、Token | Source 阶段 |
Trivy 镜像扫描集成:
container-scan:
stage: security
image: aquasec/trivy:latest
script:
# 扫描 Harbor 中的镜像
- trivy image --severity HIGH,CRITICAL \
--exit-code 1 \
harbor.company.com/ecommerce/order-service:$CI_COMMIT_SHA
# 发现 HIGH/CRITICAL 漏洞时阻断流水线
allow_failure: false
阶段 5:Package(制品打包)
目标:将构建产物打包为可部署的制品。
制品类型:
- Docker 镜像:推送到 Harbor / Docker Hub / ECR
- Helm Chart:打包为
.tgz,推送到 Harbor OCI / ChartMuseum - 二进制包:JAR、WAR、静态文件(存放到 S3 / Nexus / Artifactory)
版本管理策略:
# 语义化版本 + Git 元信息
IMAGE_TAG="${VERSION}-${GIT_COMMIT_SHORT_SHA}-${BUILD_NUMBER}"
# 示例:v2.1.0-a1b2c3d-156
# 多环境标签策略
docker tag order-service:v2.1.0-a1b2c3d harbor.company.com/ecommerce/order-service:staging
docker tag order-service:v2.1.0-a1b2c3d harbor.company.com/ecommerce/order-service:prod
阶段 6:Deploy(部署)
这是 CI/CD 与 K8s 结合最紧密的阶段,分为**推送式(Push)和拉取式(Pull)**两种模型。
模型 A:Push 式部署(传统 CI/CD)
CI 工具直接调用 K8s API 或 Helm 进行部署:
# GitLab CI / Jenkins 中执行
deploy-staging:
stage: deploy
image: alpine/helm:latest
script:
- helm upgrade --install ecommerce-staging ./ecommerce-chart \
-n ecommerce-staging \
--set global.imageRegistry=harbor.company.com \
--set orderService.image.tag=$CI_COMMIT_SHA \
--set userService.image.tag=$CI_COMMIT_SHA \
--wait --timeout 10m
environment:
name: staging
url: https://api-staging.ecommerce.com
缺点:
- CI 工具需要 K8s 集群的 高权限凭据(kubeconfig),安全风险大
- CI 工具成为部署的单一故障点
- 难以实现回滚审计和漂移检测
模型 B:Pull 式部署(GitOps,现代推荐)
CI 只负责构建镜像并更新 Git 仓库中的配置;由 GitOps 控制器(如 ArgoCD、Flux)自动同步到 K8s。
开发者 push 代码
↓
CI 构建镜像 → 推送到 Harbor
↓
CI 修改 Git 配置仓库(image tag: v2.1.0)
↓
ArgoCD 检测到 Git 变更
↓
ArgoCD 自动 apply 到 K8s
↓
ArgoCD 持续监控 Git 与 K8s 状态一致性
ArgoCD Application 定义:
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: ecommerce
namespace: argocd
spec:
# 源:Git 仓库中的 Helm Chart
source:
repoURL: https://gitlab.company.com/ecommerce/helm-charts.git
targetRevision: HEAD
path: ecommerce-chart
helm:
valueFiles:
- values-prod.yaml
# 目标集群
destination:
server: https://kubernetes.default.svc
namespace: ecommerce
# 同步策略:自动同步 + 自动修复漂移
syncPolicy:
automated:
prune: true # 删除 Git 中不存在的资源
selfHeal: true # 自动修复手动修改导致的漂移
syncOptions:
- CreateNamespace=true
GitOps 的优势:
- 单一事实源(Single Source of Truth):Git 仓库定义了期望状态
- 安全隔离:CI 不需要 K8s 写权限,ArgoCD 在集群内运行
- 完整审计:所有变更通过 Git 提交记录可追溯
- 回滚即 Revert:
git revert后 ArgoCD 自动回滚
阶段 7:Verify(验证)
部署后必须验证应用健康,否则可能引入"静默故障"。
| 验证类型 | 工具/方法 | 作用 |
|---|---|---|
| Smoke Test | curl / HTTP 探针 | 验证核心接口返回 200 |
| Health Check | K8s Readiness/Liveness Probe | 验证 Pod 就绪 |
| Synthetic Monitoring | Prometheus Blackbox Exporter | 模拟用户请求验证链路 |
| Chaos Engineering | Chaos Mesh、Litmus | 注入故障验证系统韧性 |
| A/B 测试验证 | Flagger、Argo Rollouts | 对比新旧版本业务指标 |
Jenkins 中集成 Smoke Test:
stage('Smoke Test') {
steps {
script {
// 等待服务就绪
sleep 30
// 验证订单创建接口
def response = sh(
script: "curl -s -o /dev/null -w '%{http_code}' \
-X POST https://api.ecommerce.com/api/order/health",
returnStdout: true
).trim()
if (response != "200") {
error("Smoke test failed with status ${response}")
}
}
}
}
阶段 8:Monitor & Feedback(监控与反馈)
部署不是终点,而是持续验证的起点。
关键反馈回路:
Prometheus 采集指标
↓
Grafana 展示错误率 / 延迟 / 吞吐量
↓
错误率超过阈值 → Alertmanager 触发告警
↓
PagerDuty / 钉钉通知运维
↓
人工判断或自动触发 ArgoCD Rollback(Git Revert)
↓
Jenkins / GitLab CI 记录此次发布为失败
↓
DORA 度量平台统计部署失败率
三、两种主流 CI/CD 架构实现
架构 A:GitLab CI + K8s(一体化)
GitLab Server(代码 + CI 调度)
│
├── GitLab Runner(K8s Executor)
│ └── 每个 Job 启动一个 K8s Pod
│ ├── build-pod(Maven 编译)
│ ├── test-pod(运行测试)
│ └── deploy-pod(Helm 部署)
│
└── GitLab Container Registry(镜像仓库)
└── 镜像推送到 Harbor
GitLab Runner 配置(K8s Executor):
# config.toml
[[runners]]
name = "k8s-runner"
url = "https://gitlab.company.com"
token = "TOKEN"
executor = "kubernetes"
[runners.kubernetes]
namespace = "gitlab-runner"
image = "ubuntu:22.04"
# 使用特权容器运行 Docker in Docker
privileged = true
# 资源限制
[runners.kubernetes.resources]
[runners.kubernetes.resources.requests]
cpu = "500m"
memory = "512Mi"
[runners.kubernetes.resources.limits]
cpu = "2000m"
memory = "2Gi"
架构 B:Jenkins + K8s(可编程编排)
Jenkins Master(调度中心)
│
├── Jenkins Agent(K8s Pod Template)
│ └── JNLP 连接后执行 Pipeline
│ ├── 容器 maven:编译
│ ├── 容器 docker:构建镜像
│ └── 容器 helm:部署
│
└── Harbor(镜像仓库)
Jenkins Kubernetes Plugin 配置:
// Jenkinsfile 中声明 Pod 模板
podTemplate(
yaml: '''
apiVersion: v1
kind: Pod
spec:
containers:
- name: maven
image: maven:3.9-eclipse-temurin-17
command: ['cat']
tty: true
- name: docker
image: docker:24-dind
securityContext:
privileged: true
- name: helm
image: alpine/helm:latest
command: ['cat']
tty: true
''',
// 工作空间卷:多个容器共享构建产物
volumes: [
emptyDirVolume(mountPath: '/workspace', memory: false)
]
) {
node(POD_LABEL) {
stage('Build') {
container('maven') {
sh 'mvn clean package'
}
}
stage('Docker Build') {
container('docker') {
sh 'docker build -t order-service:v1.0 .'
}
}
stage('Deploy') {
container('helm') {
sh 'helm upgrade --install ecommerce ./chart'
}
}
}
}
四、高级部署策略(CD 核心)
1. 蓝绿部署(Blue-Green Deployment)
原理:同时维护两套生产环境(Blue 当前、Green 新版本),切换流量。
Ingress / Service
│
├──► Blue(当前 v1.0,3 个 Pod)← 100% 流量
│
└──► Green(新版本 v2.0,3 个 Pod)← 0% 流量
验证 Green 健康后,Ingress 切换 100% 流量到 Green
保留 Blue 一段时间,确认无问题后删除
Helm 实现:
# values.yaml
blueGreen:
enabled: true
active: blue # 当前活跃版本
# 通过 Helm 切换
helm upgrade ecommerce ./chart --set blueGreen.active=green
2. 金丝雀发布(Canary Release)
原理:新版本只接收少量真实流量,逐步扩大比例,观察指标。
v1.0(95% 流量) v2.0(5% 流量)
┌─────────┐ ┌─────────┐
│ Pod x 3 │ │ Pod x 1 │
└─────────┘ └─────────┘
▲ ▲
└──────────┬─────────────┘
│
Ingress 权重路由
观察 10 分钟:错误率 < 0.1%,延迟 < 100ms
→ 扩大 v2.0 到 50% → 100% → 删除 v1.0
Argo Rollouts 实现:
apiVersion: argoproj.io/v1alpha1
kind: Rollout
metadata:
name: order-service
spec:
replicas: 5
strategy:
canary:
steps:
- setWeight: 10 # 10% 流量给新版本
- pause: {duration: 10m} # 观察 10 分钟
- setWeight: 50 # 50% 流量
- pause: {duration: 10m}
- setWeight: 100 # 100% 流量
analysis:
templates:
- templateName: success-rate
args:
- name: service-name
value: order-service
3. 灰度发布(Feature Flag)
原理:代码已部署到生产,但功能通过开关控制,仅对特定用户开放。
// Java 代码中使用 Feature Flag
if (featureFlags.isEnabled("seckill-v2", userId)) {
// 新秒杀逻辑
return seckillServiceV2.createOrder(request);
} else {
// 旧逻辑
return seckillService.createOrder(request);
}
优势:发布与上线解耦,出现问题无需回滚代码,只需关闭开关。
五、度量:DORA 指标
Google DevOps 研究与评估(DORA)团队提出的四个核心指标,用于衡量 CI/CD 成熟度:
| 指标 | 定义 | 精英团队表现 | 度量方式 |
|---|---|---|---|
| 部署频率 | 单位时间内生产部署次数 | 按需部署,每日多次 | GitLab/Jenkins 部署日志 |
| 变更前置时间 | 从代码提交到生产运行的时间 | < 1 小时 | CI Pipeline 持续时间 |
| 恢复服务时间 | 生产故障发生后恢复的时间 | < 1 小时 | 监控告警到修复的时长 |
| 变更失败率 | 导致故障或需要回滚的部署占比 | < 5% | 失败部署数 / 总部署数 |
六、完整电商 CI/CD 流水线示例
# ============================================
# .gitlab-ci.yml:电商微服务完整 CI/CD
# ============================================
variables:
DOCKER_REGISTRY: "harbor.company.com"
KUBE_NAMESPACE: "ecommerce"
stages:
- build
- test
- security
- package
- deploy-staging
- verify-staging
- deploy-production
# ============================================
# 1. 构建
# ============================================
build:
stage: build
image: maven:3.9-eclipse-temurin-17
script:
- mvn -B clean package -DskipTests
artifacts:
paths:
- target/*.jar
expire_in: 1 hour
# ============================================
# 2. 测试
# ============================================
unit-test:
stage: test
image: maven:3.9-eclipse-temurin-17
script:
- mvn test
coverage: '/Total.*?(\d+\%)/'
integration-test:
stage: test
image: maven:3.9-eclipse-temurin-17
services:
- docker:24-dind
variables:
DOCKER_HOST: tcp://docker:2376
script:
- mvn test -P integration-test
# ============================================
# 3. 安全扫描
# ============================================
sast:
stage: security
image: returntocorp/semgrep:latest
script:
- semgrep --config=auto --error --json .
artifacts:
reports:
sast: gl-sast-report.json
container-scan:
stage: security
image: aquasec/trivy:latest
script:
- trivy image --severity HIGH,CRITICAL --exit-code 1 \
$DOCKER_REGISTRY/ecommerce/order-service:$CI_COMMIT_SHA
# ============================================
# 4. 打包镜像
# ============================================
docker-build:
stage: package
image: docker:24-dind
services:
- docker:24-dind
script:
- docker build -t $DOCKER_REGISTRY/ecommerce/order-service:$CI_COMMIT_SHA .
- docker push $DOCKER_REGISTRY/ecommerce/order-service:$CI_COMMIT_SHA
# ============================================
# 5. 部署到预发布环境
# ============================================
deploy-staging:
stage: deploy-staging
image: alpine/helm:latest
script:
- helm upgrade --install ecommerce-staging ./chart
--namespace $KUBE_NAMESPACE-staging
--set orderService.image.tag=$CI_COMMIT_SHA
--wait
environment:
name: staging
url: https://api-staging.ecommerce.com
# ============================================
# 6. 预发布环境验证
# ============================================
smoke-test:
stage: verify-staging
image: curlimages/curl:latest
script:
- curl -f https://api-staging.ecommerce.com/api/order/health
- curl -f https://api-staging.ecommerce.com/api/product/health
# ============================================
# 7. 部署到生产(需人工审批)
# ============================================
deploy-production:
stage: deploy-production
image: alpine/helm:latest
script:
- helm upgrade --install ecommerce ./chart
--namespace $KUBE_NAMESPACE
--set orderService.image.tag=$CI_COMMIT_SHA
--wait
environment:
name: production
url: https://api.ecommerce.com
when: manual # 手动触发
only:
- main # 只有 main 分支可部署生产
七、总结
| 维度 | 核心要点 |
|---|---|
| CI | 代码提交 → 自动构建 → 自动测试,目标是快速反馈集成错误 |
| CDelivery | CI 产物 → 自动打包 → 自动部署到类生产环境,具备随时发布能力 |
| CDeployment | 自动发布到生产,完全无人干预,依赖强大的测试与监控 |
| Push 部署 | CI 工具直接调用 Helm/kubectl,简单但安全风险高 |
| Pull 部署(GitOps) | CI 只更新 Git,ArgoCD 自动同步,推荐生产使用 |
| 高级策略 | 蓝绿(快速切换)、金丝雀(渐进放量)、Feature Flag(代码与发布解耦) |
| 度量 | DORA 四指标:部署频率、前置时间、恢复时间、失败率 |
CI/CD 不是"一个工具",而是一套"从代码到用户的自动化工程体系"。它的终极形态是:开发者提交代码后,系统自动完成测试、打包、部署、验证,并在出现问题时自动回滚——人只负责写代码和业务决策。