摘要(Auto)

  • 生成时间:2025-12-21
  • 关注点:流水线阶段拆解、质量与安全门禁、Docker 分层与缓存命中、蓝绿/金丝雀/滚动发布、可追溯(commit→ 构建 → 镜像 → 发布)、回滚与降级

建议追问(Auto)

  • 如何做到“本地过/CI 不过”不会发生?门禁如何渐进收敛?
  • 镜像为什么能缓存?Dockerfile 怎么写才命中?
  • 灰度怎么观测?回滚怎么一键?

关联卡片

在我项目中的角色与使用场景

  • 典型约束:
    • 多团队并行交付,发布频繁,必须保证“质量可控 + 可追溯 + 可回滚”。
    • 构建/测试慢,CI 资源有限,需要靠缓存与增量把时延压下去。
    • 安全与合规:需要扫描与审计(SAST/依赖漏洞/权限最小化)。

原理简述

把 CI/CD 拆成“流水线阶段 + 产物与版本 + 发布策略 + 观测与回滚”四块:

  • 流水线阶段:build → test → scan → package → deploy
  • 产物与版本
    • buildMeta(commit hash/buildId)写入产物/镜像标签
    • artifact 与镜像仓库统一管理,记录谁在何时发布了哪个版本到哪个环境
  • 发布策略:蓝绿 / 金丝雀 / 滚动 + 健康检查 + 自动回滚
  • 观测与回滚:错误率/性能/关键接口耗时作为“是否继续放量”的依据;异常触发回滚或降级开关

对比表格

1)发布策略:蓝绿 vs 金丝雀 vs 滚动

维度蓝绿(Blue/Green)金丝雀(Canary)滚动(Rolling)
风险控制(切流即可回退)(小流量观测)中(逐批替换)
资源成本高(两套环境)
复杂度高(观测/放量)
适用场景关键系统、回滚必须快频繁发布、需要观测资源紧张、常规服务

2)门禁:质量 vs 安全(常见组合)

门禁项目的典型落地
lint/typecheck代码质量/类型安全PR 阶段必跑,max-warnings=0 渐进收敛
unit test回归保护关键模块必跑,按风险设覆盖率阈值
SAST代码安全高危必阻断,中低危输出清单
deps scan依赖漏洞高危阻断,提供升级建议与豁免期限

3)Docker 缓存命中:写法差异

写法缓存命中说明
先复制 lockfile 再装依赖,最后复制源码业务代码变动不影响依赖层
先复制全部源码再装依赖任何改动都让依赖层失效

模拟问答

  • [ ] Q1:你怎么保证“可追溯”(commit→ 构建 → 镜像 → 发布)?

    • buildMeta:commit hash/buildId 写进镜像 tag 与应用内版本信息。
    • 发布记录:记录发布人/时间/环境/版本/变更单,支持一键回滚到上一稳定版本。
  • [ ] Q2:Docker 分层缓存为什么能提速?Dockerfile 怎么写才命中?

    • 原理:层(layer)复用;依赖层稳定就能命中缓存。
    • 写法:先复制 package.json/lockfile 安装依赖,再复制业务代码;配合 CI 缓存 pnpm/yarn store。
  • [ ] Q3:质量门禁怎么做“渐进收敛”而不是一刀切?

    • 新代码强制:只对改动文件/目录严格(lint-staged + PR gate)。
    • 存量治理:豁免清单 + 期限 + 趋势看板,逐步把 warning 清零再升为 error。
  • [ ] Q4:金丝雀怎么观测?用哪些指标决定继续放量还是回滚?

    • 指标:错误率、核心接口耗时、关键页面 Web Vitals(LCP/INP/CLS)、业务转化指标(按场景)。
    • 策略:分阶段放量(1%→5%→20%→100%),每阶段设阈值与观察窗口;异常自动停止并回滚。
  • [ ] Q5:配置与密钥怎么治理?怎么避免“错配导致事故”?

    • 分离:代码配置 vs 环境配置(env/secret)分离;按环境/按服务最小权限。
    • 校验:发布前校验必填变量/格式;审批流 + 审计;异常快速回滚与熔断。

手写代码区

一个“buildMeta 注入”的最小思路(伪代码):

1) CI 读取当前 commit hash → 生成 BUILD_ID
2) 构建时写入环境变量:BUILD_ID、GIT_SHA
3) 应用启动时暴露 /version:返回 BUILD_ID、GIT_SHA
4) 日志/错误上报都带 BUILD_ID,保证可追溯
1
2
3
4

我的补充(Manual)

(不会被脚本覆盖:真实约束、组织因素、踩坑)

上次更新:
(adsbygoogle = window.adsbygoogle || []).push({});