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」,事故恢复时间显著下降。
经验
- 缓存要按 lockfile 哈希,而不是按分支,否则跨分支互相污染。
- 构建产物带 commit 指纹,线上问题可以立刻定位到代码版本。
- 环境配置外置,同一份产物在测试与生产只改运行时配置,不再重复构建。
- 把冒烟用例当资产,它比任何发布规范都更能阻止事故。
CI/CD 的价值不在于「自动化」,而在于把发布变成一个可重复、可回滚、可度量的工程动作。当发布不再令人紧张,团队才敢做小步快跑。