本节摘要:CI/CD 八大原则——自动化、版本控制、频繁集成、构建一次随处部署、尽早失败等——构成流水线设计的检查清单。本节用 Jenkins Declarative Pipeline 把原则映射到 stage,并梳理从 commit 到 rollback 的完整生命周期。
阅读完本节,你应当能够:
动手优先:下面 Jenkinsfile 几乎覆盖全部原则——先通读,再对照表格找对应关系。
pipeline { agent any options { buildDiscarder(logRotator(numToKeepStr: '20')) timeout(time: 30, unit: 'MINUTES') } stages { stage('Checkout') { steps { checkout scm } } // 原则2: 版本控制 stage('Build') { steps { sh 'mvn -B clean package -DskipTests' } } stage('Unit Test') { steps { sh 'mvn test' } } // 原则4: 每次提交触发 stage('Static Scan') { steps { sh 'mvn sonar:sonar' } } // 原则7: 质量内建 stage('Archive') { steps { archiveArtifacts 'target/*.jar' } } // 原则5: 构建一次 stage('Deploy Staging') { steps { sh './deploy.sh staging target/app.jar' } } stage('Smoke Test') { steps { sh 'pytest tests/smoke' } } } post { failure { slackSend color: 'danger', message: "Failed ${env.JOB_NAME} #${env.BUILD_NUMBER}" } } }
| # | 原则 | 流水线体现 | 违反后果 |
|---|---|---|---|
| 1 | 自动化一切可能环节 | 无 SSH 手工步骤 | 人为错误、不可审计 |
| 2 | 版本控制一切 | 代码/配置/IaC 进 Git | 配置漂移 |
| 3 | 频繁提交集成 | 短分支、trunk-based | 合并地狱 |
| 4 | 每次提交触发构建测试 | webhook on push/PR | 主干腐化 |
| 5 | 构建一次,随处部署 | 同一 JAR/镜像晋级 | 「在我机器上能跑」 |
| 6 | 尽早失败,快速反馈 | 单测在打包前 | 缺陷发现成本高 |
| 7 | 质量内建 | SonarQube、SAST 进 PR | 上线前夜补测 |
| 8 | 环境一致性 | Docker/IaC 复现环境 | 预发通过生产挂 |
| 阶段 | 典型时长 | 失败是否阻断 |
|---|---|---|
| Checkout | 10–30s | 是 |
| Build | 2–8min | 是 |
| Unit Test | 1–5min | 是 |
| 制品上传 | 30s–2min | 是 |
| 部署预发 | 2–5min | 是 |
| E2E | 10–30min | 视策略 |
| 生产 | 2–10min | 审批 gate |
⚠️ 常见坑:把 E2E 放在 unit test 之前——反馈从 2 分钟变成 30 分钟,开发者开始 skip CI。
💡 关键直觉:「尽早失败」= 把最便宜的测试放最前。单元测试毫秒级,E2E 分钟级;顺序错了,CI 失去意义。
jobs: ci: steps: - uses: actions/checkout@v4 - run: npm ci && npm test # 阶段 2-3 - run: npm run build # 阶段 4 - uses: actions/upload-artifact@v4 with: { name: dist, path: dist/ } cd-staging: needs: ci steps: - run: ./deploy-staging.sh # 阶段 5-7 cd-prod: needs: cd-staging environment: production # 阶段 8 审批 steps: - run: ./deploy-prod.sh
# 错误:每个环境重新 mvn package deploy-staging.sh → mvn package -Dstaging=true deploy-prod.sh → mvn package -Dprod=true # 正确:晋级同一镜像 docker pull myapp:abc123 && deploy to staging docker pull myapp:abc123 && deploy to prod # 同一 digest
代码提交阶段开发者须在本地跑 lint 与快测——pre-commit hook 可强制 pytest tests/unit -q 通过才允许 commit。这不是 CI 替代,而是把失败左移到秒级。
构建阶段CI Server(Jenkins 2.x Pipeline、GitLab Runner、GitHub-hosted runner)分配 executor。Runner 应不可变:每次 job 用新容器或 clean workspace,避免上次构建残留污染本次。GitHub runs-on: ubuntu-latest 每次是新 VM。
测试阶段分 fast path 与 slow path。Fast:unit + lint + SAST,目标 5 分钟内。Slow:integration + contract test,可并行。Flaky test 须 quarantine——标记 @pytest.mark.flaky 并单独 job,不阻断主干。
制品阶段成功构建产出 immutable artifact。Java 推 Nexus maven-releases,Docker 推 Harbor registry/app@sha256:digest。制品元数据记录:commit SHA、构建号、依赖 SBOM——供审计与 CVE 追溯。
部署测试环境通常自动——merge main 即 deploy dev/staging。环境须可丢弃:Terraform 或 Helm 从零创建,禁止「祖传 staging 服务器」手工调过配置。
验收性能测试k6/JMeter 在 staging 跑,对比上一版本 P95。劣化超 20% 阻断 prod promote——性能回归与功能回归同等重要。
预发验证最接近 prod:数据量可缩但拓扑一致。UAT 可选手动,自动化 smoke 必选。
生产部署CD 需 approve,CDP 自动。无论哪种,deploy 后 5 分钟 error rate 监控是安全网。
监控回滚Prometheus alert → PagerDuty → runbook 或 auto rollback。Postmortem 24h 内 blameless 完成。
# 晋级脚本:同一 digest 从 staging 到 prod DIGEST=$(docker inspect registry/app:abc123 --format='{{index .RepoDigests 0}}') docker pull $DIGEST docker tag $DIGEST registry/app:prod-current kubectl set image deploy/app app=$DIGEST
digest 比 tag 更 immutable——tag 可被覆盖,digest 不能。
下一章我们进入 CI 实践:版本控制如何触发流水线,构建与测试如何设门禁。