Jenkins Vue 流水线构建 OOM(exit 137)复发的问题
记录时间:2026-08-21
环境:Jenkins 2.568.1(K8s 动态 Slave,devops 命名空间)/ 共享库vueHelmDevopsByk8s流水线 / node 容器gzeport-node-pnpm:20(node 20.19.5 + pnpm 10.22.0)/ 项目sccg-vue-gzeport-market-vue(Vue3 + TS + vite 7.3.6,5531 模块)
关联笔记:[Jenkins Vue流水线构建异常排查-pnpm版本-husky-构建脚本拦截-OOM](./Jenkins Vue流水线构建异常排查-pnpm版本-husky-构建脚本拦截-OOM.md)(2026-07-16,track-ui-xc 同因 OOM 首次爆发)
一、问题现象
sccg-vue-gzeport-market-vue #19 构建(分支 origin/dev)在前端构建阶段失败,构建耗时 5min41s:
✓ 5531 modules transformed.
rendering chunks...
Killed
ELIFECYCLE Command failed with exit code 137.
- exit 137 = 128 + 9(SIGKILL),node 容器
limits.memory: 6Gi被 cgroup OOM Kill - 失败点在
rendering chunks...(vite/rollup 打包最后阶段,内存峰值区) - 同日早些时候的 #18 构建成功。峰值内存本就在 6Gi 临界区波动,业务提交带来的模块数量差异即可跨过红线
二、排查过程
2.1 确认 137 的来源
Jenkins 日志无 JS 异常堆栈、无磁盘满,Killed 出现在 shell 层,排除应用报错,指向内核杀进程。node 容器 limits 6Gi,判定 cgroup 内存超限。
2.2 定位内存峰值构成:业务 build 脚本并行双进程
日志回显业务 package.json 的 build:uat 脚本:
> market@3.0.0 build:uat /home/jenkins/agent/workspace/sccg-vue-gzeport-market-vue
> vue-tsc --noEmit & vite build --mode uat
单个 &(后台并行)让两个 Node 进程同时跑:
vue-tsc --noEmit:TS 全量类型检查,大项目吃 1~2Gvite build:5531 模块转换 + chunk 渲染,4~5G 以上
流水线的 NODE_OPTIONS=--max-old-space-size=4096 是环境变量,对容器内所有 Node 进程生效,两个进程各自最多 4G 堆,叠加冲破 6Gi 容器上限。与 2026-07-16 track-ui-xc 的 OOM 同因(见关联笔记)。
2.3 发现 vue-tsc 在 CI 里纯浪费
构建日志里 vue-tsc 输出了约 200 个 TS 类型错误(TS2304/TS2694/TS2300/TS2322……),但构建照样往下走直到 vite OOM。机制上:& 把 vue-tsc 放进后台后,shell 不会等待它,其退出状态没有被显式 wait 收集,失败不影响脚本最终退出码——npm script 实际反映的是前台 vite build 的结果。类型检查在 CI 里既不拦截质量,又每次白占 1~2G 内存和几分钟时间。
三、根因分析
| 层面 | 事实 |
|---|---|
| 直接原因 | 业务 build:uat 用 & 并行跑 vue-tsc 与 vite,双进程内存峰值叠加超容器 limit |
| 放大器 | NODE_OPTIONS 无法按进程区分,两进程各享堆上限 |
| 恶化趋势 | 模块数增长到 5531,峰值逐步跨过 6Gi 红线 |
| 根本问题 | vue-tsc 在 CI 无拦截作用(退出码被 & 丢弃),纯属无效内存开销 |
历史脉络(同一根因两轮爆发):
| 时间 | 项目 | 模块数 | 内存动作 | 结果 |
|---|---|---|---|---|
| 2026-07-16 | track-ui-xc | 4353 | 4Gi → 6Gi | 仍 OOM,后靠 SKIP_TYPE_CHECK 跳过 vue-tsc 通过 |
| 2026-08-21 | market-vue | 5531 | 6Gi → 8Gi | 重跑构建通过 |
SKIP_TYPE_CHECK(流水线绕过 npm script 直接 pnpm exec vite build)后来按「流水线统一走 package.json 的 build 脚本」约定移除(track-ui-xc Jenkinsfile 留有注释),新流水线不再实现该开关。因此本轮的约束是:
- 不改业务仓库的 build 脚本(归属业务团队)
- 不绕过 package.json build 约定(红线)
- 内存上限 8Gi(集群可用上限)
三条约束同时成立时,CI 侧只剩资源调整一条路;根治必须业务团队改脚本。
四、解决方案
4.1 流水线侧(已实施并验证):内存扩到集群上限
vars/vueHelmDevopsByk8s.groovy:
| 参数 | 旧值 | 新值 | 说明 |
|---|---|---|---|
| node 容器 limits.memory | 6Gi | 8Gi | 集群可用上限,无法再高 |
| node 容器 requests.memory | 2Gi | 4Gi | 提高调度保留值,降低被调度到剩余内存不足节点的概率;limit > request 本身仍是超卖设计 |
| –max-old-space-size | 4096 | 6144 | 每个 Node 进程各自的 V8 old space 上限(NODE_OPTIONS 无法按进程区分),为堆外原生内存、Buffer、线程栈等预留总体 cgroup 空间;实际余量取决于两进程内存峰值是否重叠 |
注意:这是过渡垫片不是根治。项目模块继续增长后仍会顶到 8Gi,届时没有再加内存的空间。
4.2 业务侧(根本解,待推动)
package.json 的 build:* 脚本按推进顺序:
- 短期:改为
vite build --mode uat(CI 跳过无拦截作用的类型检查,本地由 IDE 承担) - 中长期(推荐架构):类型检查独立成脚本,PR/MR 阶段跑
pnpm typecheck拦截质量,发布构建只跑vite build。类型检查与生产构建是两个 CI 职责,分阶段比塞进一个 build 脚本清晰:{ "typecheck": "vue-tsc --noEmit", "build:uat": "vite build --mode uat" } - 若必须保留在一个脚本里:修完存量 TS 错误后改串行
vue-tsc --noEmit && vite build --mode uat
注意:不能直接把
&改成&&了事——当前代码有约 200 个 TS 错误,串行后 CI 会立刻全红,必须先修错误或先去掉 vue-tsc。
五、验证
共享库推送 + Jenkins 重载后重跑 market-vue:构建通过,vite 越过 rendering chunks 阶段完成打包,不再被 OOM Kill。
# 后续观测(可选):构建期间容器内存峰值,确认与 8Gi 上限的余量
kubectl top pod <market-vue 构建 Pod> -n devops
六、注意事项
exit 137 = SIGKILL,不能只凭退出码定性——容器场景最常见原因是 cgroup OOM Kill,但也可能是人为
kill -9、节点内存压力、编排系统强杀(本例耗时 5min41s 未超 15min 流水线超时,可排除超时强杀)。Killed出现在 shell 层且无 JS 堆栈时优先怀疑内存超限,再用以下命令确认OOMKilled:kubectl get pod <pod> -n devops -o jsonpath='{.status.containerStatuses[*].state.terminated.reason}'Jenkins 动态 Slave Pod 构建结束即删除,取证需提前配置
podRetention保留失败 Pod。区别于heap out of memory(JS 异常,V8 堆内不足),两者处理方向不同。
&的退出码陷阱——后台进程的退出状态不被 shell 等待和收集,脚本退出码只反映前台最后一条命令;CI 里放任何检查命令在&后面(后台)都等于没放。
- CI 资源类故障(OOM)先分析进程行为再考虑加资源;本例加内存三轮(4→6→8Gi)都是在为同一个无效进程买单,8Gi 已到头
- 禁止流水线绕过 package.json 的 build 脚本直接调 vite(SKIP_TYPE_CHECK 路线已按约定移除,勿再引入)
NODE_OPTIONS对容器内所有 Node 进程生效,无法按进程区分堆上限- 大项目(4000+ 模块)接入时留意 build 脚本是否用
&并行类型检查,接入前就该让业务侧改掉
七、参考资料
- 关联笔记:[Jenkins Vue流水线构建异常排查-pnpm版本-husky-构建脚本拦截-OOM](./Jenkins Vue流水线构建异常排查-pnpm版本-husky-构建脚本拦截-OOM.md)
- vite 官方 Troubleshooting(构建内存)