CI/CD:让发布变成"流水线"
这是基础设施系列的第8篇文章。前面我们聊了网关、配置中心、消息队列、服务发现、监控告警、日志系统等话题,今天来聊聊一个将开发与运维紧密连接的关键环节——CI/CD。
一、CI/CD的价值
你有没有经历过这样的发布场景:
- 发布一个版本需要协调三个部门,耗时一周
- 手动复制文件到服务器,偶尔会复制错
- 发布后发现问题,回滚需要半小时
- 每次发布都是一场"战役",团队集体加班待命
- 凌晨三点被叫起来回滚,因为测试没覆盖边界情况
如果你的答案是肯定的,那么你的团队迫切需要 CI/CD。
什么是 CI/CD?
CI/CD 是两个概念的组合:
两者结合,就形成了一条从代码提交到生产发布的完整流水线。
CI/CD 解决了什么问题?
CI/CD 的核心指标
怎么衡量 CI/CD 做得好不好?有几个关键指标:
| 指标 | 说明 | 目标值 |
|---|---|---|
| 部署频率 | 多久发布一次 | 每天/每周多次 |
| 变更前置时间 | 代码提交到上线多久 | 小于1小时 |
| 服务恢复时间 | 出问题多久能恢复 | 小于1小时 |
| 变更失败率 | 发布失败的比例 | 小于15% |
这四个指标被称为 DORA 指标,是业界衡量研发效能的黄金标准。
二、持续集成(CI)设计
CI 的核心流程
一个完整的 CI 流程应该包含以下步骤:
代码提交 → 代码检查 → 单元测试 → 编译构建 → 集成测试 → 产物归档
静态代码分析,检查代码风格、潜在问题、安全漏洞等。这一步是最快的,通常几秒钟就能完成。
# ESLint 配置示例(前端)
{
"extends": ["eslint:recommended", "plugin:react/recommended"],
"rules": {
"no-unused-vars": "error",
"no-console": "warn",
"indent": ["error", 2],
"semi": ["error", "always"]
}
}
// golangci-lint 配置示例(后端)
linters:
enable:
- gofmt
- goimports
- govet
- errcheck
- staticcheck
- ineffassign
测试代码的最小单元是否按预期工作。单元测试应该快速、独立、可重复。
单元测试的黄金法则:不依赖外部环境。如果一个测试需要连数据库、调接口,那它就不是单元测试,而是集成测试。
// Go 单元测试示例
func TestCalculateDiscount(t *testing.T) {
tests := []struct {
name string
price float64
level int
expected float64
}{
{"普通会员", 100.0, 1, 100.0},
{"银卡会员", 100.0, 2, 95.0},
{"金卡会员", 100.0, 3, 90.0},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
result := CalculateDiscount(tt.price, tt.level)
assert.Equal(t, tt.expected, result)
})
}
}
将源代码编译成可执行的产物。构建过程应该是确定性的——同样的代码产生同样的产物。
这一步的关键是:构建结果可重现。不能因为构建时间不同、构建机器不同,就产生不同的结果。
# Dockerfile 示例:多阶段构建
# 阶段1:构建
FROM golang:1.21-alpine AS builder
WORKDIR /app
COPY go.mod go.sum ./
RUN go mod download
COPY . .
RUN CGO_ENABLED=0 GOOS=linux go build -o myapp .
# 阶段2:运行
FROM alpine:3.19
WORKDIR /app
COPY --from=builder /app/myapp .
EXPOSE 8080
CMD ["./myapp"]
测试多个模块协同工作是否正常。相比单元测试,集成测试更接近真实场景,但执行时间更长。
# docker-compose.yml 用于集成测试
version: '3.8'
services:
app:
build: .
depends_on:
- db
- redis
environment:
- DB_HOST=db
- REDIS_HOST=redis
db:
image: mysql:8.0
environment:
- MYSQL_ROOT_PASSWORD=test
- MYSQL_DATABASE=testdb
redis:
image: redis:7-alpine
触发策略
什么时候触发 CI?有几种常见策略:
| 策略 | 说明 | 优点 | 缺点 |
|---|---|---|---|
| 每次提交触发 | 每次 push 都跑 CI | 反馈最快 | 资源消耗大 |
| 定时触发 | 每隔一段时间跑一次 | 资源可控 | 反馈延迟 |
| 合并前触发 | PR/MR 时触发 | 平衡质量和效率 | 可能阻塞合并 |
| 手动触发 | 需要手动点击 | 最省资源 | 容易被遗忘 |
最常见的是合并前触发 + 定时全量测试的组合:
- 每次 Pull Request 触发快速测试(单元测试 + 代码检查)
- 每天晚上触发全量测试(集成测试 + E2E 测试)
核心原则是:反馈要快,问题要早发现。
CI 与代码审查的关系
有了 CI,还需要代码审查吗?两者缺一不可。
| 检查方式 | 能发现什么 | 不能发现什么 |
|---|---|---|
| CI | 语法错误、测试失败、构建失败、安全漏洞、性能回归 | 设计合理性、代码可读性、业务逻辑正确性、命名规范 |
| 代码审查 | 设计问题、可读性问题、业务逻辑问题、知识共享 | 编译错误、测试失败、性能回归 |
CI 是代码审查的前提。在 CI 通过之前,代码审查是浪费时间——代码连编译都过不了,还审查什么?
最佳实践:CI 不通过,禁止代码审查。
CI 配置实战
以 GitHub Actions 为例,一个完整的 CI 配置:
# .github/workflows/ci.yml
name: CI
on:
push:
branches: [ main, develop ]
pull_request:
branches: [ main ]
jobs:
lint:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-go@v5
with:
go-version: '1.21'
- name: Run linters
uses: golangci/golangci-lint-action@v3
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-go@v5
with:
go-version: '1.21'
- name: Run unit tests
run: go test -v -race -coverprofile=coverage.out ./...
- name: Upload coverage
uses: codecov/codecov-action@v3
build:
needs: [lint, test]
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Build Docker image
run: docker build -t myapp:${{ github.sha }} .
- name: Push to registry
run: |
docker tag myapp:${{ github.sha }} registry.example.com/myapp:${{ github.sha }}
docker push registry.example.com/myapp:${{ github.sha }}
这个配置实现了:
- 代码检查和单元测试并行执行
- 两者都通过后才构建 Docker 镜像
- 测试覆盖率自动上传到 Codecov
- 镜像使用 Git commit SHA 作为标签,保证可追溯
三、持续部署(CD)设计
多环境部署
一个成熟的 CD 系统,应该支持多环境部署:
开发环境 → 测试环境 → 预发布环境 → 生产环境
| 环境 | 用途 | 部署频率 | 数据 | 要求 |
|---|---|---|---|---|
| 开发环境 | 开发自测 | 最频繁(每天多次) | 模拟数据 | 部署速度快 |
| 测试环境 | 测试团队验证 | 每日/每周 | 测试数据 | 接近生产 |
| 预发布环境 | 发布前验证 | 每次发布前 | 生产数据副本 | 与生产一致 |
| 生产环境 | 真实用户 | 按需 | 真实数据 | 稳定可靠 |
解决方案:基础设施即代码(IaC)。用代码描述环境配置,所有环境用同一套代码创建。
# Kubernetes 部署配置示例
apiVersion: apps/v1
kind: Deployment
metadata:
name: myapp
spec:
replicas: 3
selector:
matchLabels:
app: myapp
template:
metadata:
labels:
app: myapp
spec:
containers:
- name: myapp
image: registry.example.com/myapp:v1.2.3
ports:
- containerPort: 8080
resources:
requests:
cpu: 100m
memory: 128Mi
limits:
cpu: 500m
memory: 512Mi
env:
- name: DB_HOST
valueFrom:
configMapKeyRef:
name: app-config
key: db_host
部署策略
部署不是简单的"覆盖旧版本",而是一门艺术。不同的场景需要不同的策略:
1. 蓝绿部署(Blue-Green Deployment)
准备两套完全相同的生产环境,一套在线服务(蓝),一套待命(绿)。发布时,先部署到待命环境,验证通过后切换流量。如果有问题,快速切回。
┌─────────────┐
用户 ──→│ 负载均衡 │
└──────┬──────┘
│
┌───────┴───────┐
↓ ↓
┌─────────┐ ┌─────────┐
│ 蓝环境 │ │ 绿环境 │
│ v1.0 │ │ v1.1 │
│ (在线) │ │ (待命) │
└─────────┘ └─────────┘
2. 金丝雀发布(Canary Release)
先让一小部分用户使用新版本,观察没有问题后,逐步扩大范围。这是一种渐进式的发布策略,风险可控。
第一阶段:5% 流量 → 新版本
第二阶段:25% 流量 → 新版本
第三阶段:50% 流量 → 新版本
第四阶段:100% 流量 → 新版本
每个阶段都要观察关键指标:
- 错误率是否上升?
- 响应时间是否变慢?
- 业务指标是否正常?
# Istio 金丝雀发布配置
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
name: myapp
spec:
hosts:
- myapp.example.com
http:
- route:
- destination:
host: myapp
subset: v1
weight: 90
- destination:
host: myapp
subset: v2
weight: 10
3. 滚动发布(Rolling Update)
逐台更新服务器,保证始终有服务器在提供服务。这种方式资源利用率高,但回滚相对麻烦。
初始状态:[v1, v1, v1, v1, v1]
步骤1:[v2, v1, v1, v1, v1] # 更新第1台
步骤2:[v2, v2, v1, v1, v1] # 更新第2台
步骤3:[v2, v2, v2, v1, v1] # 更新第3台
步骤4:[v2, v2, v2, v2, v1] # 更新第4台
步骤5:[v2, v2, v2, v2, v2] # 更新第5台
# Kubernetes 滚动更新配置
apiVersion: apps/v1
kind: Deployment
spec:
strategy:
type: RollingUpdate
rollingUpdate:
maxSurge: 1 # 最多多1个Pod
maxUnavailable: 0 # 不允许不可用
策略对比
| 策略 | 回滚速度 | 资源消耗 | 发布速度 | 适用场景 |
|---|---|---|---|---|
| 蓝绿部署 | 秒级 | 高(2倍) | 快 | 关键服务、需要快速回滚 |
| 金丝雀发布 | 分钟级 | 低 | 慢 | 大规模服务、风险敏感 |
| 滚动发布 | 分钟级 | 低 | 中 | 资源有限、一般服务 |
回滚机制
无论准备多么充分,发布后都可能发现问题。回滚机制是最后的保险。
回滚应该是一个按钮的事情。蓝绿部署的回滚是秒级的——切换流量即可。滚动发布的回滚需要时间——要逐台替换回去。
# Kubernetes 回滚命令
kubectl rollout undo deployment/myapp
# 回滚到指定版本
kubectl rollout undo deployment/myapp --to-revision=3
每个版本都应该被妥善保存:
- 代码:Git tag 标记每个发布版本
- 镜像:Docker 镜像打上版本标签,保留最近 N 个版本
- 配置:配置变更记录在配置中心
- 数据库:迁移脚本要支持回滚
# Git tag 规范
v1.0.0-20260228-abc123 # 版本号-日期-commit
什么时候该回滚?需要明确的判断标准:
# 自动回滚规则示例
rollback_rules:
- metric: error_rate
threshold: 1%
action: auto_rollback
- metric: p99_latency
threshold: 2000ms
action: alert_and_confirm
- metric: success_rate
threshold: 99%
action: auto_rollback
配置与代码分离
CD 中一个常见的坑是:配置和代码混在一起。
问题场景:
- 同一份代码,开发环境连测试库,生产环境连生产库
- 改个配置,要重新编译、重新打包、重新发布
- 配置改错了,导致生产事故
最佳实践是:配置与代码分离。
┌─────────────────────────────────────┐
│ 配置中心 │
│ ┌─────────┐ ┌─────────┐ ┌─────────┐│
│ │ 开发配置 │ │ 测试配置 │ │ 生产配置 ││
│ └────┬────┘ └────┬────┘ └────┬────┘│
└───────┼───────────┼───────────┼─────┘
│ │ │
↓ ↓ ↓
┌────────────────────────────────┐
│ 同一份代码 │
│ 同一个镜像 │
└────────────────────────────────┘
配置存储在配置中心(如 Nacos、Apollo、Consul),独立于代码版本。发布新版本时,配置可以保持不变;需要改配置时,不需要重新发布代码。
# Kubernetes ConfigMap 示例
apiVersion: v1
kind: ConfigMap
metadata:
name: app-config
data:
DB_HOST: "mysql.prod.svc.cluster.local"
REDIS_HOST: "redis.prod.svc.cluster.local"
LOG_LEVEL: "info"
四、微服务场景下的 CI/CD
微服务让 CI/CD 变得更复杂,也更重要。
微服务的 CI/CD 挑战
单体应用只有一个仓库、一个构建、一个部署。微服务可能有几十上百个服务,每个服务都有自己的 CI/CD 流程。
服务 A 依赖服务 B 的接口。B 改了接口,A 可能就挂了。如何在发布前发现这种问题?
每个服务有自己的数据库。发布时,数据库迁移要考虑数据一致性。
一个请求经过 10 个服务。出问题时,如何快速定位是哪个服务的问题?
微服务 CI/CD 最佳实践
每个微服务有自己的代码仓库、CI 配置、部署流程。服务之间互不干扰,可以独立发布。
服务A ─→ CI/CD A ─→ 部署 A
服务B ─→ CI/CD B ─→ 部署 B
服务C ─→ CI/CD C ─→ 部署 C
在 CI 中加入契约测试,确保服务间接口变更不会破坏依赖方。
# 契约测试示例
contract_tests:
- provider: user-service
consumer: order-service
contract: user-service-order-service.json
发布顺序很重要:
- 先发布接口提供方(Provider)
- 再发布接口消费方(Consumer)
如果反过来,消费方调用了新接口,但提供方还没发布,就会报错。
使用 Istio、Linkerd 等服务网格工具,实现:
- 流量管理(金丝雀发布、流量切换)
- 故障注入(混沌工程)
- 可观测性(链路追踪、指标收集)
# Istio 流量管理示例
apiVersion: networking.istio.io/v1alpha3
kind: DestinationRule
metadata:
name: myapp
spec:
host: myapp
subsets:
- name: v1
labels:
version: v1
- name: v2
labels:
version: v2
微服务的数据库迁移遵循"向前兼容"原则:
阶段1:新代码兼容旧数据库
阶段2:发布新代码
阶段3:执行数据库迁移
阶段4:(可选)旧代码兼容新数据库(为回滚做准备)
-- 向前兼容的数据库迁移示例
-- 步骤1:添加新列(允许为空)
ALTER TABLE orders ADD COLUMN discount DECIMAL(10,2) NULL;
-- 步骤2:发布新代码(使用新列)
-- 步骤3:回填历史数据
UPDATE orders SET discount = 0 WHERE discount IS NULL;
-- 步骤4:添加非空约束
ALTER TABLE orders MODIFY COLUMN discount DECIMAL(10,2) NOT NULL DEFAULT 0;
五、CI/CD 工具对比
市面上有很多 CI/CD 工具,如何选择?
主流工具对比
| 工具 | 类型 | 优点 | 缺点 | 适用场景 |
|---|---|---|---|---|
| Jenkins | 自托管 | 插件丰富、高度可定制 | 维护成本高、配置复杂 | 传统企业、复杂流程 |
| GitHub Actions | 云服务 | 与 GitHub 深度集成、配置简单 | 锁定 GitHub | GitHub 用户 |
| GitLab CI | 自托管/云 | 与 GitLab 集成、功能全面 | 学习曲线 | GitLab 用户 |
| CircleCI | 云服务 | 速度快、配置灵活 | 价格较高 | 中小团队 |
| ArgoCD | 自托管 | GitOps 原生、Kubernetes 友好 | 仅限 CD | Kubernetes 环境 |
| Tekton | 自托管 | 云原生、Kubernetes 原生 | 学习曲线陡峭 | Kubernetes 环境 |
Jenkins:老牌霸主
// Jenkinsfile 示例
pipeline {
agent any
stages {
stage('Build') {
steps {
sh 'go build -o myapp'
}
}
stage('Test') {
steps {
sh 'go test -v ./...'
}
}
stage('Deploy') {
when {
branch 'main'
}
steps {
sh 'kubectl apply -f k8s/'
}
}
}
post {
failure {
mail to: 'team@example.com',
subject: "Build Failed: ${env.JOB_NAME}",
body: "Check console output at ${env.BUILD_URL}"
}
}
}
- 插件生态丰富,几乎无所不能
- 社区活跃,文档完善
- 完全自托管,数据安全
- 维护成本高(Java 应用,内存占用大)
- 配置复杂,学习曲线陡峭
- UI 界面相对老旧
GitHub Actions:云原生新秀
# GitHub Actions 完整示例
name: Build and Deploy
on:
push:
branches: [ main ]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Set up Go
uses: actions/setup-go@v5
with:
go-version: '1.21'
- name: Build
run: go build -v ./...
- name: Test
run: go test -v ./...
deploy:
needs: build
runs-on: ubuntu-latest
if: github.ref == 'refs/heads/main'
steps:
- name: Deploy to production
run: |
echo "Deploying to production..."
- 与 GitHub 深度集成,配置简单
- 云服务,无需维护服务器
- Marketplace 提供大量现成的 Action
- 锁定 GitHub 生态
- 自托管 Runner 配置相对复杂
- 免费额度有限制
GitLab CI:一体化方案
# .gitlab-ci.yml
stages:
- build
- test
- deploy
build:
stage: build
image: golang:1.21
script:
- go build -o myapp
artifacts:
paths:
- myapp
test:
stage: test
image: golang:1.21
script:
- go test -v ./...
deploy_production:
stage: deploy
image: bitnami/kubectl
script:
- kubectl apply -f k8s/
only:
- main
when: manual
- 代码仓库 + CI/CD + 容器注册表一体化
- 功能全面(包括安全扫描、代码质量等)
- 自托管版本免费
- 功能太多,学习曲线陡峭
- 自托管版本资源消耗较大
ArgoCD:GitOps 的最佳实践
# ArgoCD Application 配置
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: myapp
namespace: argocd
spec:
project: default
source:
repoURL: https://github.com/myorg/myapp-config.git
targetRevision: HEAD
path: overlays/production
destination:
server: https://kubernetes.default.svc
namespace: myapp
syncPolicy:
automated:
prune: true
selfHeal: true
- GitOps 原生,配置即代码
- 声明式,自动同步
- 可视化界面直观
- 完美支持 Kubernetes
- 仅限 CD,需要配合其他 CI 工具
- 学习曲线(需要理解 GitOps)
如何选择?
选择 CI/CD 工具的决策树:
是否使用 Kubernetes?
├─ 是 → 考虑 ArgoCD + GitHub Actions/GitLab CI
└─ 否 → 使用什么代码托管平台?
├─ GitHub → GitHub Actions
├─ GitLab → GitLab CI
└─ 其他/自建 → Jenkins 或 CircleCI
六、流水线设计原则
CI 和 CD 结合起来,就形成了完整的流水线。如何设计好这条流水线?
原则一:快速反馈
流水线的首要目标是快速反馈。开发人员提交代码后,应该在几分钟内知道结果。
实现方法:
第一层:单元测试(快,< 5分钟)
第二层:集成测试(中,< 30分钟)
第三层:E2E 测试(慢,< 2小时)
先跑快的,快速发现问题;再跑慢的,全面验证质量。
# GitHub Actions 并行示例
jobs:
test-unit:
runs-on: ubuntu-latest
steps:
- run: go test -v ./...
test-integration:
runs-on: ubuntu-latest
steps:
- run: go test -v -tags=integration ./...
build:
needs: [test-unit, test-integration] # 两者并行执行
runs-on: ubuntu-latest
只测试受影响的部分。比如,改了用户服务,就不用跑订单服务的测试。
原则二:幂等性
流水线的每次执行都应该是幂等的——同样的输入,产生同样的输出。
这意味着:
- 构建过程不能依赖外部状态(比如当前时间)
- 测试不能依赖执行顺序
- 部署不能假设目标环境的当前状态
# 不幂等的构建(依赖当前时间)
docker build -t myapp:$(date +%Y%m%d) .
# 幂等的构建(使用 Git commit SHA)
docker build -t myapp:${GIT_COMMIT_SHA} .
原则三:可追溯性
每次流水线执行的结果都应该被记录下来:
- 触发原因:谁提交的代码?提交信息是什么?
- 执行时间:什么时候开始?什么时候结束?
- 执行结果:成功还是失败?失败在哪个阶段?
- 产物信息:构建出什么产物?在哪里可以找到?
# 记录构建元数据
build_info:
commit_sha: abc123def456
commit_message: "feat: add user login"
author: zhangsan@example.com
build_number: 1234
build_time: 2026-02-28T10:30:00Z
artifact_url: registry.example.com/myapp:abc123def456
原则四:失败隔离
流水线中某个阶段失败了,不应该影响其他阶段的执行。这样可以看到所有的问题,而不是修一个问题、跑一次流水线、发现下一个问题。
# 失败隔离示例
jobs:
lint:
# 即使后续步骤失败,这个也会执行完
continue-on-error: false
test-unit:
# 即使 lint 失败,单元测试也会执行
test-integration:
# 即使 test-unit 失败,集成测试也会执行
原则五:渐进式交付
设计多级"门禁":
代码提交
↓
【第一级门禁】代码风格 + 单元测试(< 5分钟)
↓ 通过
【第二级门禁】集成测试 + 安全扫描(< 30分钟)
↓ 通过
【第三级门禁】性能测试 + E2E 测试(< 2小时)
↓ 通过
部署到生产环境
每一级门禁都是一个过滤网,把问题拦截在合适的阶段。
七、常见问题与解决方案
问题一:CI 经常失败
- 测试不稳定(Flaky Test)
- 检查规则太严格
- 环境问题(网络不稳定、依赖服务不可用)
- 区分真失败和假失败
- 记录失败原因,分析是真问题还是环境问题
- 调整检查规则
- 分阶段实施:先警告,再强制
- 投资测试基础设施
- Mock 外部依赖,减少网络调用
# 失败重试配置
jobs:
test:
runs-on: ubuntu-latest
continue-on-error: false
steps:
- name: Run tests
uses: nick-fields/retry@v2
with:
max_attempts: 3
retry_wait_seconds: 10
command: go test -v ./...
问题二:发布时间过长
- 流水线太长(串行执行)
- 测试太慢(大量 E2E 测试)
- 人工审批环节多
- 并行化
- 使用分布式测试框架
- 分层测试
- 慢速测试(E2E)只跑关键路径
- 减少人工审批
- 关键变更才需要人工确认
- 异步部署
- 不阻塞主流程
问题三:回滚困难
- 数据库迁移不可逆
- 配置变更没记录
- 缺少回滚演练
- 数据库迁移向前兼容
- 数据库迁移分步执行
- 配置版本化
- 支持一键回滚到历史版本
- 定期回滚演练
- 记录回滚时间,持续优化
问题四:环境不一致
- 各环境配置不同
- 数据不同
- 版本不同
- 基础设施即代码
- 所有环境用同一套代码创建
- 容器化
- 环境差异通过环境变量注入
- 数据同步
- 注意脱敏处理
问题五:流水线维护成本高
- 流水线缺乏模块化
- 缺少文档
- 缺少测试
- 模块化设计
- 新项目复用现有配置
- 流水线测试
- 使用 lint 工具检查配置语法
- 保持简单
- 定期清理不再使用的步骤
八、CI/CD 成熟度模型
怎么评估团队的 CI/CD 水平?可以参考以下成熟度模型:
| 级别 | 描述 | 特征 |
|---|---|---|
| Level 0 | 手动部署 | 没有自动化,全靠人工操作 |
| Level 1 | 脚本化部署 | 有部署脚本,但仍需手动触发 |
| Level 2 | 基础 CI/CD | 代码提交自动触发构建和测试 |
| Level 3 | 完整 CI/CD | 自动化测试覆盖全面,自动部署到各环境 |
| Level 4 | 高级 CI/CD | 金丝雀发布、自动化回滚、完善监控 |
| Level 5 | 精英级 | 每天多次发布,问题自动发现和修复 |
大多数团队在 Level 2-3 之间。Level 4-5 需要大量的基础设施投入和文化建设。
九、总结
CI/CD 是现代软件开发的基石。它将开发、测试、运维串联起来,形成一条高效、可靠的交付流水线。
- 完整的检查流程(代码检查 → 单元测试 → 构建 → 集成测试)
- 快速的反馈机制(分层测试、并行执行)
- 失败后的正确处理(失败隔离、自动重试)
- 多环境部署(开发 → 测试 → 预发布 → 生产)
- 合理的部署策略(蓝绿、金丝雀、滚动发布)
- 可靠的回滚机制(快速回滚、版本管理、回滚决策)
- 配置与代码分离(配置中心、环境变量注入)
- 每个服务独立 CI/CD
- 契约测试保证接口兼容
- 服务网格实现流量管理
- 数据库迁移向前兼容
CI/CD 不是工具的问题,而是工程文化的问题。它需要团队对质量的承诺、对自动化的信任、对持续改进的追求。
- 系列七-1:API 网关:流量的"交通枢纽"
- 系列七-2:配置中心:系统的"控制面板"
- 系列七-3:消息队列:异步的"传送带"
- 系列七-4:服务发现:微服务的"通讯录"
- 系列七-5:监控告警:系统的"健康体检"
- 系列七-6:HTTP 服务:通信的"通用语言"
- 系列七-7:日志系统:系统的"黑匣子"
- 系列七-8:CI/CD:让发布变成"流水线"(本文)
💬 评论 (0)