CI-CD 最佳实践与安全
概述
工具是手段,实践才是核心。本文覆盖 CI/CD 落地中最关键的五个维度:分支策略、测试金字塔、安全左移、制品管理和发布策略。
一、分支策略
Git Flow(经典,适合版本化发布)
main ────●────────●────────●──── (生产)
\ / \ /
release ──●────●── ●────●──── (发布分支)
\ / \ /
develop ────●────────●──────── (开发主线)
\ /
feature ───────●────● (功能分支)
| 适用 | 不适用 |
|---|
| 移动端 App / 客户端软件 | Web 服务(发布周期太长) |
| 多版本并行维护 | 小团队、高频部署 |
GitHub Flow(简单,适合持续部署)
main ──────────────────●──●── (始终可部署)
\ \ /
feature ──●──● ──●──● (特性分支 → PR → 合并即部署)
| 适用 | 不适用 |
|---|
| Web 服务 / SaaS | 需要多版本并行维护 |
| 小到中型团队 | 严格的发布审批流程 |
Trunk-Based Development(推荐,CI/CD 最佳搭档)
main ──●─●─●─●─●─●─●─●── (每天合并,短生命周期分支)
\/
feature ──● (分支存活 < 1 天)
核心原则:
- 分支存活时间 < 24 小时
- Feature Flag 控制未完成功能的可见性
- 高频合并 → 小批量变更 → 冲突少、回滚容易
- 需要成熟的自动化测试 + 代码审查能力
选型建议
版本化发布(App/客户端):Git Flow
SaaS/Web 服务: Trunk-Based Development
开源项目(外部贡献者):GitHub Flow
二、测试金字塔
╱ E2E 测试 ╲ (少量,慢,覆盖关键用户场景)
╱──────────────╲
╱ 集成测试 ╲ (中等,API/DB/外部依赖)
╱──────────────────╲
╱ 单元测试 ╲ (大量,快,覆盖核心逻辑)
╱────────────────────────╲
| 层级 | 比例 | 速度 | 工具(Go 项目) | 目的 |
|---|
| 单元测试 | 70% | 毫秒 | go test | 验证函数/方法逻辑 |
| 集成测试 | 20% | 秒 | go test -tags=integration + testcontainers | 验证组件交互 |
| E2E | 10% | 分钟 | Cypress / Playwright | 验证端到端流程 |
CI Pipeline 中的测试策略
# 快速失败原则:Lint → 单元测试 → 集成测试 → E2E
# 前置步骤失败自动跳过后续(节约资源)
lint: # 1 分钟内 ← 最便宜的检查
if: always()
unit-test: # 2 分钟内
needs: lint
integration: # 5 分钟内
needs: unit-test
e2e: # 10 分钟内 ← 仅在重要 PR 或 main 分支运行
needs: integration
if: github.ref == 'refs/heads/main' || contains(github.event.pull_request.labels.*.name, 'e2e')
三、安全左移(Shift-Left Security)
CI 流水线安全扫描点
Code Commit
├── SAST (静态应用安全测试) → SonarQube, CodeQL
├── SCA (软件组成分析) → Dependency Track, Snyk
├── Secret 扫描 → truffleHog, Gitleaks
└── IaC 扫描 → tfsec, Checkov
Dockerfile Commit
└── Dockerfile Lint → hadolint
Image Build
├── 镜像扫描 → Trivy, Grype
└── SBOM 生成 → Syft, CycloneDX
Registry Push
└── 准入控制 → OPA/Gatekeeper, Kyverno
Trivy 扫描集成示例
# GitHub Actions
- name: Scan Dockerfile
uses: aquasecurity/trivy-action@master
with:
scan-type: 'config' # 扫描 Dockerfile/Helm/K8s YAML
severity: 'HIGH,CRITICAL'
exit-code: 1 # 高危则失败
- name: Scan image
uses: aquasecurity/trivy-action@master
with:
image-ref: '${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ github.sha }}'
format: 'sarif'
output: 'trivy-results.sarif'
severity: 'CRITICAL,HIGH'
Secret 扫描
# 防止密钥/Token/AK-SK 提交到仓库
- name: Secret scan
uses: gitleaks/gitleaks-action@v2
with:
config-path: .gitleaks.toml
完整安全集成样例(生产级 GitHub Actions)
name: Security Pipeline
on:
push:
branches: [main]
pull_request:
branches: [main]
schedule:
- cron: '0 6 * * 1' # 每周一全量扫描
jobs:
# ── 1. SAST(静态代码分析)──
sast:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: SonarQube Scan
uses: SonarSource/sonarqube-scan-action@v3
env:
SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
SONAR_HOST_URL: ${{ secrets.SONAR_URL }}
with:
args: >
-Dsonar.projectKey=my-project
-Dsonar.qualitygate.wait=true
# ── 2. SCA(依赖漏洞扫描)──
sca:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- run: |
go mod download
- name: Run Trivy vulnerability scanner
uses: aquasecurity/trivy-action@master
with:
scan-type: 'fs'
scan-ref: '.'
format: 'sarif'
output: 'trivy-fs-results.sarif'
severity: 'CRITICAL,HIGH'
- name: Upload Trivy scan results
uses: github/codeql-action/upload-sarif@v3
with:
sarif_file: 'trivy-fs-results.sarif'
# ── 3. Secret 检测 ──
secret-scan:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0 # 全量历史(检测历史提交)
- name: Gitleaks
uses: gitleaks/gitleaks-action@v2
with:
config-path: .gitleaks.toml
# ── 4. IaC 扫描 ──
iac-scan:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Checkov scan
uses: bridgecrewio/checkov-action@master
with:
directory: .
framework: terraform,kubernetes,helm
soft_fail: false # 硬失败
# ── 5. 镜像构建 + 扫描 + 签名 ──
image-security:
needs: [sast, sca, secret-scan, iac-scan]
runs-on: ubuntu-latest
permissions:
contents: read
packages: write
id-token: write # Cosign keyless signing 所需
steps:
- uses: actions/checkout@v4
- name: Build image
uses: docker/build-push-action@v6
with:
context: .
push: false
load: true
tags: my-app:scan
- name: Trivy image scan
uses: aquasecurity/trivy-action@master
with:
image-ref: 'my-app:scan'
format: 'sarif'
output: 'trivy-image-results.sarif'
severity: 'CRITICAL,HIGH'
exit-code: 1
- name: Generate SBOM
uses: anchore/sbom-action@v0
with:
image: 'my-app:scan'
format: 'spdx-json'
output-file: 'sbom.spdx.json'
- name: Upload SBOM as artifact
uses: actions/upload-artifact@v4
with:
name: sbom
path: sbom.spdx.json
- name: Push & Cosign sign
run: |
docker push ${{ env.REGISTRY }}/my-app:${{ github.sha }}
# Cosign keyless signing(OIDC + Sigstore)
cosign sign --yes \
${{ env.REGISTRY }}/my-app:${{ github.sha }}
供应链安全(Software Supply Chain)
SLSA 框架简介
SLSA (Supply-chain Levels for Software Artifacts):
Level 0: 无任何保证
Level 1: 构建有文档记录(出处 provenance)
Level 2: 使用版本控制和托管构建服务
Level 3: 审计和防篡改构建(隔离环境、签名 attestation)
Level 4: 最高级别:需要两位独立人员审查 + 密封构建
目前主流工具链可达到 Level 2-3
SBOM(软件物料清单)
# SBOM = 镜像/二进制包含的所有组件的"成分清单"
# 用途:
# 1. 漏洞影响面分析:Log4Shell 出现 → 查 SBOM 确认哪些镜像受到影响
# 2. 合规审计:开源许可证识别与合规检查
# 3. 供应链可视化
# 生成 SPDX / CycloneDX 两种标准格式
syft my-app:scan -o spdx-json > sbom.spdx.json
syft my-app:scan -o cyclonedx-json > sbom.cyclonedx.json
# 扫描 SBOM 中的漏洞
grype sbom:sbom.spdx.json
Cosign 镜像签名验证
# 签名流程(构建时)
cosign sign --key cosign.key ${IMAGE}:${TAG}
# Keyless 签名(OIDC + GitHub Actions identity)
cosign sign --yes ${IMAGE}:${TAG}
# 部署时验证(K8s Admission Webhook 或 ArgoCD)
cosign verify \
--certificate-identity "https://github.com/my-org/my-repo/.github/workflows/ci.yml@refs/heads/main" \
--certificate-oidc-issuer "https://token.actions.githubusercontent.com" \
${IMAGE}:${TAG}
# 跨集群准入控制(OPA Gatekeeper 策略)
# 拒绝未签名或签名无效的镜像
Harbor 安全策略
# Harbor 仓库端安全策略(在镜像推送到仓库后自动执行)
# 1. 漏洞扫描(Trivy/Clair 后台自动扫描)
# 2. 策略引擎:
# - 禁止有 HIGH/CRITICAL 漏洞的镜像被拉取
# - 禁止未签名的镜像部署到生产集群
# - 限制特定项目的镜像来源
# 3. 代理缓存(缓存 Docker Hub/外部镜像,防止拉取限流)
三、安全左移(Shift-Left Security)
CI 流水线安全扫描点
Code Commit
├── SAST (静态应用安全测试) → SonarQube, CodeQL
├── SCA (软件组成分析) → Dependency Track, Snyk
├── Secret 扫描 → truffleHog, Gitleaks
└── IaC 扫描 → tfsec, Checkov
Dockerfile Commit
└── Dockerfile Lint → hadolint
Image Build
├── 镜像扫描 → Trivy, Grype
└── SBOM 生成 → Syft, CycloneDX
Registry Push
└── 准入控制 → OPA/Gatekeeper, Kyverno
Trivy 扫描集成示例
# GitHub Actions
- name: Scan Dockerfile
uses: aquasecurity/trivy-action@master
with:
scan-type: 'config' # 扫描 Dockerfile/Helm/K8s YAML
severity: 'HIGH,CRITICAL'
exit-code: 1 # 高危则失败
- name: Scan image
uses: aquasecurity/trivy-action@master
with:
image-ref: '${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ github.sha }}'
format: 'sarif'
output: 'trivy-results.sarif'
severity: 'CRITICAL,HIGH'
Secret 扫描
# 防止密钥/Token/AK-SK 提交到仓库
- name: Secret scan
uses: gitleaks/gitleaks-action@v2
with:
config-path: .gitleaks.toml
四、制品管理
Docker 镜像最佳实践
# ── 多阶段构建 ──
FROM golang:1.23-alpine AS builder
WORKDIR /app
COPY go.mod go.sum ./
RUN go mod download
COPY . .
RUN CGO_ENABLED=0 go build -ldflags="-s -w" -o /app/server .
FROM alpine:3.20
RUN apk --no-cache add ca-certificates tzdata
COPY --from=builder /app/server /app/server
USER 1000:1000 # 非 root 运行
EXPOSE 8080
ENTRYPOINT ["/app/server"]
镜像标签策略
# 推荐多标签策略
harbor.example.com/my-app:sha-abc1234 # 精确追溯到 Commit
harbor.example.com/my-app:main # 当前 main 最新
harbor.example.com/my-app:v1.2.3 # SemVer 版本(Release)
# 避免
harbor.example.com/my-app:latest # 不明确,禁止
制品仓库选型
| 注册表 | 适用 | 特点 |
|---|
| Harbor | 企业自建 | 镜像扫描、RBAC、代理缓存、Helm Chart |
| Docker Hub | 个人/小团队 | 免费有限制、公开/私有 |
| GHCR | GitHub 项目 | 与 Actions 集成天然、Packages 统一管理 |
| ECR/ACR/GCR | 云平台项目 | 免运维、与云服务深度集成 |
五、发布策略
四种策略对比
| 策略 | 风险 | 回滚速度 | 资源成本 | 适用 |
|---|
| 滚动更新 | 中 | 分钟级 | 低 | 默认选择 |
| 蓝绿部署 | 低 | 秒级 | 2x 资源 | 核心服务 |
| 金丝雀发布 | 最低 | 分钟级 | 1.2x | 流量大、验证需求 |
| A/B 测试 | — | — | 1.5x | 产品实验 |
金丝雀发布完整流程
# Argo Rollouts 金丝雀配置
strategy:
canary:
steps:
- setWeight: 10 # 10% 流量
- pause: { duration: 10m }
- analysis: # 自动分析(Prometheus)
templates:
- templateName: error-rate-check
- setWeight: 50 # 50% 流量
- pause: { duration: 30m }
- setWeight: 100
回滚策略
# ArgoCD 一键回滚
argocd app rollback my-app <revision-number>
# Helm 回滚
helm rollback my-release <revision>
# K8s 原生回滚
kubectl rollout undo deployment/my-app
kubectl rollout undo deployment/my-app --to-revision=3
综合 CI/CD Pipeline 参考
┌────────────────────────────────────────────────────────────────┐
│ CI Phase │
├────────────────────────────────────────────────────────────────┤
│ Push/PR → Lint → Unit Test → Security Scan → Build → Push │
│ ↓ (Pass) │
│ Integration Test → E2E Test → Artifact Push │
├────────────────────────────────────────────────────────────────┤
│ CD Phase (GitOps) │
├────────────────────────────────────────────────────────────────┤
│ Git Image Tag Update → PR Review → Merge │
│ → ArgoCD Auto Sync → Health Check │
│ ↓ 失败 │
│ Auto Rollback │
└────────────────────────────────────────────────────────────────┘
常见问题 / 坑点
| 问题 | 原因 | 解决方案 |
|---|
| CI 越来越慢 | 构建环境膨胀、依赖无缓存 | 缓存优化 + 并行 Job + 增量构建 |
| 测试不稳定(Flaky tests) | 时间依赖/竞态/外部服务 | 重试机制 + Retry + Mock 外部依赖 |
| 凭据泄露到 Git 历史 | 误提交 Secret 文件 | git filter-branch 清理 + Secret 扫描 |
| 镜像体积膨胀 | 未清理构建缓存 | 多阶段构建 + .dockerignore |
| 部署后服务不可用 | 无就绪探针/冒烟测试 | Readiness Probe + 部署后自动冒烟测试 |
| 跨环境配置不一致 | 手动修改环境差异 | 统一用 Kustomize overlays 或 Helm values |
关联知识
参考资源
学习时间
| 阶段 | 时间 | 备注 |
|---|
| 初次学习 | 2026-07-14 | 分支策略 + 测试 + 安全 + 制品 + 发布 |
状态: 📖 已掌握
下次复习日期: 2026-08-14