全部文章

Writing

把发布从 2 天压到 2 小时:CI/CD 流水线的三段改造

发布慢很少是某一步慢,而是等待、人工确认与责任不清叠加的结果。分享一次以流水线换确定性的改造。

先量化「慢」在哪里

改造前,一次前端发布平均耗时 2 天。但打开时间线会发现,真正的构建只占 20 分钟,其余时间分布在:等测试环境空闲、等某个人手动打包、等运维在群里确认、发现问题后回滚重来。

所以这次改造的目标不是「让 Webpack 快一点」,而是把等待和人为判断从流程里移除。

第一段:构建产物标准化

第一步是把「在我机器上是好的」变成「在任何地方都一样」。做法是用 Docker 固定构建环境:

FROM node:22-slim AS build
WORKDIR /app
COPY pnpm-lock.yaml package.json ./
RUN corepack enable && pnpm install --frozen-lockfile
COPY . .
RUN pnpm build

FROM nginx:alpine
COPY --from=build /app/dist /usr/share/nginx/html
COPY nginx.conf /etc/nginx/conf.d/default.conf

关键在 --frozen-lockfile:依赖版本必须来自 lockfile,禁止构建时解析新版本。这一条消除了「上次发布正常、这次莫名失败」的多数情况。

第二段:流水线分阶段,失败要快

流水线按「越便宜越靠前」排序:

阶段 内容 失败后
verify lint + 类型检查 立即终止,不占构建资源
test 单元测试 + 关键路径快照 立即终止
build 生产构建,产物带 commit 指纹 保留产物便于排查
deploy:staging 自动部署测试环境 自动回滚上一版本
smoke 冒烟用例(登录、下单、报表) 阻断生产发布
deploy:prod 人工一键确认 支持按 tag 回滚
stages: [verify, test, build, staging, smoke, production]

verify:
  stage: verify
  script:
    - pnpm lint
    - pnpm exec vue-tsc --noEmit

deploy:prod:
  stage: production
  when: manual
  script:
    - ./scripts/deploy.sh --env prod --tag $CI_COMMIT_SHORT_SHA

when: manual 保留了人作为最后一道闸门,但把「等运维排期」变成了「点一下按钮」。

第三段:回滚必须比发布更快

发布系统如果没有回滚,团队就会倾向于「不敢发」。我们的做法是:每次部署都保留最近 5 个版本的静态产物与镜像 tag,回滚是一条命令:

./scripts/deploy.sh --env prod --tag "$(git describe --tags --abbrev=0 HEAD^)"

配合 PM2 或 Nginx 的平滑重载,回滚耗时在 1 分钟内。发布频率的提升,来自于失败成本的下降。

结果

  • 发布周期从 2 天缩短至 2 小时,其中人工操作只剩一次确认。
  • 部署成功率提升至 99%,失败多数在 verify 与 smoke 阶段被拦截。
  • 回滚从「重新打包」变为「切 tag」,事故恢复时间显著下降。

经验

  1. 缓存要按 lockfile 哈希,而不是按分支,否则跨分支互相污染。
  2. 构建产物带 commit 指纹,线上问题可以立刻定位到代码版本。
  3. 环境配置外置,同一份产物在测试与生产只改运行时配置,不再重复构建。
  4. 把冒烟用例当资产,它比任何发布规范都更能阻止事故。

CI/CD 的价值不在于「自动化」,而在于把发布变成一个可重复、可回滚、可度量的工程动作。当发布不再令人紧张,团队才敢做小步快跑。