CI/CD 最佳实践:构建高效的持续集成与交付流水线
什么是 CI/CD?
CI(Continuous Integration,持续集成) 和 CD(Continuous Delivery/Deployment,持续交付/部署) 是现代软件开发的核心实践:
- 持续集成(CI):频繁地将代码变更合并到主干分支,通过自动化构建和测试验证代码质量
- 持续交付(CD):自动将通过测试的代码发布到预生产环境,随时可以部署到生产环境
- 持续部署(CD):自动将通过测试的代码部署到生产环境
CI/CD 核心原则
1. 频繁集成
传统方式: CI 方式:
━━━━━━━━━ ━━━━━━━━━
开发一周 每天多次提交
↓ ↓
合并冲突 小步快跑
↓ ↓
解决冲突 快速反馈
↓ ↓
测试失败 问题早发现2. 自动化一切
| 阶段 | 自动化内容 |
|---|---|
| 代码提交 | 自动触发构建 |
| 构建 | 编译、打包、镜像构建 |
| 测试 | 单元测试、集成测试、E2E 测试 |
| 代码质量 | 静态分析、安全扫描、覆盖率检查 |
| 部署 | 环境配置、服务发布、健康检查 |
| 监控 | 日志收集、指标监控、告警通知 |
3. 快速反馈
yaml
# 流水线阶段设计 - 快速失败
stages:
- lint # 1-2 分钟:代码规范检查
- build # 2-5 分钟:编译构建
- unit-test # 5-10 分钟:单元测试
- integration # 10-20 分钟:集成测试
- deploy-staging # 自动部署到预生产
- e2e-test # 20-30 分钟:端到端测试
- deploy-prod # 手动触发部署到生产CI/CD 流水线设计
典型流水线架构
┌─────────────────────────────────────────────────────────────┐
│ 代码提交 │
│ (Git Push / PR) │
└───────────────────────────┬─────────────────────────────────┘
↓
┌─────────────────────────────────────────────────────────────┐
│ 触发 CI/CD 流水线 │
└───────────────────────────┬─────────────────────────────────┘
↓
┌───────────────────┼───────────────────┐
↓ ↓ ↓
┌───────────────┐ ┌───────────────┐ ┌───────────────┐
│ 代码检查 │ │ 构建打包 │ │ 安全扫描 │
│ - Lint │ │ - 编译 │ │ - 依赖漏洞 │
│ - Format │ │ - 单元测试 │ │ - 密钥检测 │
│ - 代码规范 │ │ - 覆盖率 │ │ - SAST │
└───────┬───────┘ └───────┬───────┘ └───────┬───────┘
│ │ │
└───────────────────┼───────────────────┘
↓
┌─────────────────────────────────────────────────────────────┐
│ 镜像构建与推送 │
│ (Docker Build & Push to Registry) │
└───────────────────────────┬─────────────────────────────────┘
↓
┌─────────────────────────────────────────────────────────────┐
│ 部署到预生产环境 │
│ (Deploy to Staging) │
└───────────────────────────┬─────────────────────────────────┘
↓
┌─────────────────────────────────────────────────────────────┐
│ 集成测试与验收 │
│ (Integration Tests / E2E Tests) │
└───────────────────────────┬─────────────────────────────────┘
↓
┌─────────────────────────────────────────────────────────────┐
│ 手动审批(可选) │
│ (Manual Approval) │
└───────────────────────────┬─────────────────────────────────┘
↓
┌─────────────────────────────────────────────────────────────┐
│ 部署到生产环境 │
│ (Deploy to Production) │
└───────────────────────────┬─────────────────────────────────┘
↓
┌─────────────────────────────────────────────────────────────┐
│ 生产环境验证 │
│ (Smoke Tests / Health Checks) │
└───────────────────────────┬─────────────────────────────────┘
↓
┌─────────────────────────────────────────────────────────────┐
│ 监控与告警 │
│ (Monitoring / Alerting / Logging) │
└─────────────────────────────────────────────────────────────┘GitHub Actions 实战
基础配置
yaml
# .github/workflows/ci.yml
name: CI/CD Pipeline
on:
push:
branches: [ main, develop ]
pull_request:
branches: [ main ]
env:
GO_VERSION: '1.22'
REGISTRY: ghcr.io
IMAGE_NAME: ${{ github.repository }}
jobs:
# 代码检查
lint:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Set up Go
uses: actions/setup-go@v5
with:
go-version: ${{ env.GO_VERSION }}
- name: golangci-lint
uses: golangci/golangci-lint-action@v6
with:
version: latest
args: --timeout=5m
# 构建与测试
build:
runs-on: ubuntu-latest
needs: lint
steps:
- uses: actions/checkout@v4
- name: Set up Go
uses: actions/setup-go@v5
with:
go-version: ${{ env.GO_VERSION }}
- name: Cache Go modules
uses: actions/cache@v4
with:
path: ~/go/pkg/mod
key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }}
restore-keys: |
${{ runner.os }}-go-
- name: Download dependencies
run: go mod download
- name: Run tests
run: go test -v -race -coverprofile=coverage.out ./...
- name: Upload coverage
uses: codecov/codecov-action@v4
with:
file: ./coverage.out
# 安全扫描
security:
runs-on: ubuntu-latest
needs: build
steps:
- uses: actions/checkout@v4
- name: Run Gosec Security Scanner
uses: securego/gosec@master
with:
args: '-no-fail -fmt sarif -out results.sarif ./...'
- name: Upload SARIF file
uses: github/codeql-action/upload-sarif@v3
with:
sarif_file: results.sarif
# 镜像构建
docker:
runs-on: ubuntu-latest
needs: [build, security]
permissions:
contents: read
packages: write
steps:
- uses: actions/checkout@v4
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Log in to Container Registry
uses: docker/login-action@v3
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Extract metadata
id: meta
uses: docker/metadata-action@v5
with:
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
tags: |
type=ref,event=branch
type=ref,event=pr
type=sha,prefix={{branch}}-
type=raw,value=latest,enable={{is_default_branch}}
- name: Build and push
uses: docker/build-push-action@v5
with:
context: .
push: true
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
cache-from: type=gha
cache-to: type=gha,mode=max
# 部署到预生产
deploy-staging:
runs-on: ubuntu-latest
needs: docker
if: github.ref == 'refs/heads/develop'
environment:
name: staging
url: https://staging.example.com
steps:
- uses: actions/checkout@v4
- name: Deploy to Staging
run: |
echo "Deploying to staging..."
# kubectl set image deployment/app app=${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ github.sha }}
# 部署到生产
deploy-production:
runs-on: ubuntu-latest
needs: docker
if: github.ref == 'refs/heads/main'
environment:
name: production
url: https://example.com
steps:
- uses: actions/checkout@v4
- name: Deploy to Production
run: |
echo "Deploying to production..."
# kubectl set image deployment/app app=${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ github.sha }}多环境部署策略
yaml
# .github/workflows/deploy.yml
name: Deploy
on:
workflow_dispatch:
inputs:
environment:
description: 'Environment to deploy'
required: true
default: 'staging'
type: choice
options:
- staging
- production
version:
description: 'Version to deploy'
required: true
type: string
jobs:
deploy:
runs-on: ubuntu-latest
environment:
name: ${{ github.event.inputs.environment }}
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Configure kubectl
uses: azure/setup-kubectl@v3
- name: Set up Helm
uses: azure/setup-helm@v3
- name: Deploy
run: |
helm upgrade --install app ./helm-chart \
--namespace ${{ github.event.inputs.environment }} \
--set image.tag=${{ github.event.inputs.version }} \
--wait --timeout 5m部署策略
1. 滚动更新(Rolling Update)
yaml
# Kubernetes Deployment
apiVersion: apps/v1
kind: Deployment
metadata:
name: my-app
spec:
replicas: 5
strategy:
type: RollingUpdate
rollingUpdate:
maxSurge: 1 # 最多多启动 1 个 Pod
maxUnavailable: 1 # 最多 1 个 Pod 不可用
template:
spec:
containers:
- name: app
image: my-app:v1.2.3
readinessProbe:
httpGet:
path: /health
port: 8080
initialDelaySeconds: 10
periodSeconds: 52. 蓝绿部署(Blue-Green Deployment)
yaml
# GitHub Actions - Blue-Green Deploy
- name: Blue-Green Deploy
run: |
# 获取当前活跃版本(蓝色)
CURRENT_COLOR=$(kubectl get service app -o jsonpath='{.spec.selector.color}')
# 确定新版本颜色(绿色)
if [ "$CURRENT_COLOR" = "blue" ]; then
NEW_COLOR="green"
else
NEW_COLOR="blue"
fi
# 部署新版本到 $NEW_COLOR
kubectl set image deployment/app-$NEW_COLOR app=my-app:${{ github.sha }}
kubectl rollout status deployment/app-$NEW_COLOR
# 切换流量
kubectl patch service app -p '{"spec":{"selector":{"color":"'$NEW_COLOR'"}}}'
# 等待验证
sleep 30
# 健康检查
if ! curl -f https://example.com/health; then
# 回滚
kubectl patch service app -p '{"spec":{"selector":{"color":"'$CURRENT_COLOR'"}}}'
exit 1
fi3. 金丝雀发布(Canary Deployment)
yaml
# Argo Rollout - Canary
apiVersion: argoproj.io/v1alpha1
kind: Rollout
metadata:
name: my-app
spec:
replicas: 10
strategy:
canary:
steps:
- setWeight: 10 # 10% 流量到新版本
- pause: {duration: 10m} # 观察 10 分钟
- setWeight: 25
- pause: {duration: 10m}
- setWeight: 50
- pause: {duration: 10m}
- setWeight: 100
analysis:
templates:
- templateName: success-rate测试策略
测试金字塔
/\
/ \
/ E2E \ # 端到端测试(少量)
/────────\ # 慢、不稳定、成本高
/ \
/ Integration \ # 集成测试(中等)
/────────────────\ # 测试组件交互
/ \
/ Unit Tests \ # 单元测试(大量)
/────────────────────────\ # 快、稳定、成本低自动化测试流水线
yaml
# .github/workflows/test.yml
name: Test Suite
on: [push, pull_request]
jobs:
unit-test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-go@v5
- name: Unit Tests
run: go test -short ./...
integration-test:
runs-on: ubuntu-latest
services:
postgres:
image: postgres:15
env:
POSTGRES_PASSWORD: postgres
options: >-
--health-cmd pg_isready
--health-interval 10s
--health-timeout 5s
--health-retries 5
ports:
- 5432:5432
steps:
- uses: actions/checkout@v4
- uses: actions/setup-go@v5
- name: Integration Tests
env:
DATABASE_URL: postgres://postgres:postgres@localhost:5432/test?sslmode=disable
run: go test -run Integration ./...
e2e-test:
runs-on: ubuntu-latest
needs: [unit-test, integration-test]
steps:
- uses: actions/checkout@v4
- name: Start Services
run: docker-compose -f docker-compose.test.yml up -d
- name: Run E2E Tests
run: |
npx playwright test
- name: Stop Services
if: always()
run: docker-compose -f docker-compose.test.yml down监控与可观测性
健康检查
go
package main
import (
"net/http"
"github.com/gin-gonic/gin"
)
func main() {
r := gin.Default()
// 存活检查
r.GET("/health/live", func(c *gin.Context) {
c.JSON(http.StatusOK, gin.H{"status": "alive"})
})
// 就绪检查
r.GET("/health/ready", func(c *gin.Context) {
// 检查数据库连接
if err := db.Ping(); err != nil {
c.JSON(http.StatusServiceUnavailable, gin.H{
"status": "not ready",
"error": err.Error(),
})
return
}
c.JSON(http.StatusOK, gin.H{"status": "ready"})
})
r.Run()
}部署监控
yaml
# .github/workflows/monitor.yml
- name: Monitor Deployment
run: |
for i in {1..30}; do
STATUS=$(curl -s -o /dev/null -w "%{http_code}" https://example.com/health)
if [ "$STATUS" = "200" ]; then
echo "Deployment successful!"
exit 0
fi
echo "Waiting for deployment... ($i/30)"
sleep 10
done
echo "Deployment failed!"
exit 1安全最佳实践
密钥管理
yaml
# 使用 GitHub Secrets
- name: Deploy
env:
DATABASE_URL: ${{ secrets.DATABASE_URL }}
API_KEY: ${{ secrets.API_KEY }}
run: |
kubectl create secret generic app-secrets \
--from-literal=database-url="$DATABASE_URL" \
--from-literal=api-key="$API_KEY"SAST/DAST 扫描
yaml
- name: SAST Scan
uses: returntocorp/semgrep-action@v1
with:
config: >-
p/security-audit
p/owasp-top-ten
- name: Container Scan
uses: aquasecurity/trivy-action@master
with:
image-ref: my-app:${{ github.sha }}
format: 'sarif'
output: 'trivy-results.sarif'总结
构建高效的 CI/CD 流水线需要关注以下关键点:
| 维度 | 最佳实践 |
|---|---|
| 速度 | 并行执行、缓存优化、快速失败 |
| 质量 | 自动化测试、代码审查、安全扫描 |
| 可靠性 | 健康检查、自动回滚、灰度发布 |
| 可观测性 | 日志收集、指标监控、链路追踪 |
| 安全 | 密钥管理、漏洞扫描、最小权限 |
关键指标:
- 部署频率:每天部署次数
- 变更前置时间:代码提交到生产部署的时间
- 变更失败率:导致故障的部署比例
- 服务恢复时间:故障发生到恢复的时间
相关文章推荐:
- GitHub Actions 实战 - GitHub Actions 详细教程
- Docker 基础入门 - 容器化基础
- Kubernetes 基础入门 - 容器编排入门

