描述在 grunt 中实现零停机前端部署的关键步骤
解读
面试官想知道你是否能把“前端构建”与“线上发布”打通,用 Grunt 把“本地打包 → 上传 → 流量切换 → 回滚”做成一条零中断的流水线。国内主流场景是 Nginx + 多版本静态目录,因此回答必须围绕“多版本并存 + 原子切换”展开,并体现你对灰度、回滚、缓存、CDN 同步等细节的认知。
知识点
- 多版本目录策略:/data/web/static/v1.2.3/…
- Nginx upstream 或 try_files 原子切换:软链 or
mv指向最新版本 - 文件指纹:Grunt 插件 grunt-filerev 给所有静态资源加 hash,解决缓存击穿
- 非覆盖式上传:rsync --link-dest 或 ossutil cp --update,保证旧文件仍在
- 健康检查与自动回滚:上传后通过 grunt-http 调用接口验证 200,失败则回指旧软链
- 并发与锁:用 grunt-lockfile 防止多人同时发布
- CDN 刷新:grunt-aliyun-cdn 或 grunt-qcloud-cdn 批量提交刷新 API,确保边缘节点同步
- 灰度/分组发布:结合 Nginx map 模块按 Cookie、IP 段分流,Grunt 任务里动态生成灰度配置并热加载
- 回滚速度:保留最近 5 个版本,回滚只需重新指向旧目录,<1 秒完成
- 日志与通知:grunt-slack 或 grunt-dingtalk 在发布成功/失败时实时推送,方便值班
答案
我通常把零停机流程拆成 7 个 Grunt 子任务,全部串在 grunt release 一条命令里:
-
前置检查
grunt.lock文件若存在直接退出,防止并发;接着跑eslint、jest,只有 0 错误才继续。 -
多环境配置注入
用grunt-replace把__API__替换成线上域名,并写入版本号v<%= pkg.version %>-<%= grunt.template.today('yyyymmddHHMM') %>,保证每次构建唯一。 -
构建与指纹
顺序执行clean、webpack:prod、filerev、usemin,产出目录dist/v1.2.3-202406211530/;所有 js/css 带 hash,图片压缩后小于 50 KB。 -
上传(非覆盖)
通过grunt-exec调用rsync -avz --link-dest=../current ./dist/v1.2.3-202406211530/ remote:/data/web/static/v1.2.3-202406211530/;旧文件不删除,上传耗时 20 秒。 -
健康检查
上传后grunt-http请求https://static.xxx.com/v1.2.3-202406211530/js/app.1234.js,返回 200 且内容与本地 MD5 一致才继续;否则自动删除远端新目录并钉钉报警。 -
原子切换
SSH 到服务器执行ln -sfn /data/web/static/v1.2.3-202406211530/ /data/web/static/current,Nginx 的root指到current,切换瞬间完成,用户无感知。 -
CDN 刷新与通知
调用grunt-aliyun-cdn:flush批量刷新https://static.xxx.com/*;成功后钉钉机器人推送“线上已更新至 v1.2.3,回滚命令:ln -sfn v1.2.2 current”。整个流程 3 分钟内结束,回滚 1 秒内完成,真正做到零停机。
拓展思考
- 如果项目日 PV 过亿,可把“蓝绿目录”升级为“三级目录”:预发→金丝雀→全量,Grunt 任务里用
map文件控制流量比例,10%→30%→100% 自动阶梯推进。 - 对 SSR 或微前端场景,静态资源零停机只是第一步,还需把模板文件(html)纳入版本控制,用 Nginx SSI 拼接,确保灰度模板与灰度静态版本一致。
- 为了审计,可在 Gruntfile 里把本次版本号、MD5、操作人、Git commit 写入
/data/web/static/versions.json,前端实时读取,方便运营一键回滚。