你是否遇到过这种离谱情况:
- Jenkins 构建日志里出现一行:
Killed - 但流水线最终显示:
Finished: SUCCESS - 上传到服务器的
dist目录却文件不全(只剩 logo/favicon,JS/CSS chunk 缺失) - 线上访问出现白屏、404、资源加载失败
这篇文章会把这个问题讲清楚,并给出一套"照做就能好"的解决方案。
1. 现象:最典型的日志长什么样?
常见日志片段如下:
text
DEPRECATION WARNING: Sass @import rules are deprecated ...
Killed
adding: dist-prod/ ...
SSH: Transferred 1 file(s)
Finished: SUCCESS
注意关键点:
- Killed 出现了(非常关键)
- 后面仍然继续打包 zip、继续 SCP 上传
- 最后还显示 SUCCESS(非常迷惑)
2. 一句话结论:真正的原因是什么?
✅ 这不是 Jenkins 上传的问题
✅ 也不是 zip 打包的问题
❗ 真正原因是:前端构建进程被 Linux 系统强制 Kill 了(多为 OOM 内存不足触发)
于是构建没完成,
dist是半成品,但 Jenkins 还继续走后续步骤,导致你拿到"不完整产物"。
3. 为什么会被 Kill?Killed 到底是什么意思?
3.1 Killed 通常来自 Linux 的 OOM Killer
当系统(或容器)内存紧张时,Linux 内核会选择"杀掉"某些进程来保护系统继续运行,这就是 OOM Killer。
被杀的进程往往是:
node(前端构建)- 或大内存的
java/mvn子进程
它会直接发 SIGKILL(9),进程"原地消失"。
3.2 为什么你服务器看起来内存很大也会发生?
很多人会说:"我明明 16G 内存啊!"
这里有几个非常常见的误区:
误区 A:Node.js 默认堆内存很小
即使机器 16G,Node 默认最大堆内存也大约只有 1.5~2G 左右(不同版本略有差异)。
大型项目(如 Vue/Vite/Webpack + 大量 SCSS)很容易超过这个阈值,然后崩溃或被 Kill。
误区 B:Jenkins 在 Docker 里跑,容器被限内存
宿主机 16G ≠ Jenkins 容器 16G
容器可能只分配了 2G/4G,Node 在容器里根本"看不到"宿主机内存。
误区 C:内存是"瞬时峰值"问题
面板上看到 64% 内存占用是"平均",但构建时可能瞬间冲到很高,OOM 在一瞬间发生,面板刷新看不到。
4. 为什么 Jenkins 显示 SUCCESS?但产物却不全?
这是本问题最迷惑人的点。
原因通常是:
- Node 进程被 SIGKILL 干掉
- Jenkins 脚本没有"失败即退出"
- Jenkins 继续执行
zip+scp - 最终把"半成品 dist"上传了
- 流水线最后一步返回 0,Jenkins 认为成功
✅ 所以:SUCCESS 只是脚本最后一步成功,并不代表构建过程完整成功。
5. 5 分钟快速定位:确认是不是 OOM
在 Jenkins 服务器上执行(需要 root 或 sudo):
bash
dmesg | tail -n 80
或者直接过滤:
bash
dmesg | grep -i -E "out of memory|killed process|oom"
如果看到类似:
text
Out of memory: Kill process 12345 (node) score 987 or sacrifice child
Killed process 12345 (node)
✅ 恭喜你,问题已经 100% 坐实:就是 OOM/Killed。
6. 解决方案(推荐顺序):按这套做基本必好
方案 1(最推荐、立竿见影):给 Node 扩大内存
在 Jenkins 构建脚本里,在 npm run build 之前加:
bash
export NODE_OPTIONS=--max_old_space_size=4096
npm run build
解释:
4096表示给 Node 堆内存上限 4GB- 16GB 机器完全够用
- 如果机器只有 4GB,可用 2048
方案 2(必须做):让 Jenkins "失败就立刻停止",防止假成功
在脚本开头加:
bash
set -e
或关键命令后面加:
bash
npm run build || exit 1
效果:
- 构建被 kill → 立刻 FAIL
- 不会继续 zip/scp
- 不会把半成品上传到生产
方案 3(Docker Jenkins 必看):检查容器内存限制
如果 Jenkins 在 Docker 里:
bash
docker ps | grep jenkins
查看是否限内存:
bash
docker inspect <容器名或ID> | grep -i memory
如果容器内存限制只有 2G/4G:
✅ 请把 Jenkins 容器内存调到 ≥ 6G(更稳 8G),否则你调 Node 参数也可能不生效。
方案 4(强烈建议):加 Swap 兜底
即使你有 16G 内存,建议也配一个 swap 防止尖峰:
bash
fallocate -l 4G /swapfile
chmod 600 /swapfile
mkswap /swapfile
swapon /swapfile
验证:
bash
free -h
看到 swap 非 0 即成功。
方案 5(优化项):降低构建内存峰值
可选但有效:
- 关闭 sourcemap
- 减少构建分析插件
- 减少并行构建数(同一台机器同时跑多个 job 会爆)
7. 给你一份"可直接复制"的 Jenkins 构建脚本(前端)
适用于大多数 Vue/Vite/Webpack 项目:
bash
#!/usr/bin/env bash
set -e
node -v
npm -v
# 关键:给 Node 足够的堆内存,防止 OOM/Killed
export NODE_OPTIONS="--max_old_space_size=4096"
npm ci
npm run build
# 可选:确保产物存在且完整(简单校验)
test -d dist || (echo "dist not found!" && exit 1)
8. 如果你还有 Java 后端(Maven)构建,也可以顺手稳一下
Maven 构建偶尔也会遇到内存不足或频繁 GC 导致慢:
bash
export MAVEN_OPTS="-Xms1024m -Xmx4096m"
mvn -T 1C clean package -DskipTests
说明:
-T 1C:按 CPU 核心数并行(8 核会更快)-DskipTests:不跑测试(看你需求)
9. 最佳实践:如何避免"产物不全还上线"的事故?
强烈建议你做两件事:
9.1 构建失败就不要上传
用 set -e 保证失败即中断。
9.2 上传前做产物校验
例如判断关键文件是否存在:
bash
test -f dist/index.html || exit 1
ls dist/assets/*.js >/dev/null 2>&1 || exit 1
10. 常见 Q&A
Q1:我机器 16G,为什么 Node 默认还会 OOM?
A:Node 有自己的默认堆上限,并不会自动用满系统内存。需要 NODE_OPTIONS 手动放大。
Q2:为什么 Jenkins 显示 SUCCESS?
A:因为脚本没"失败即退出",后续步骤成功就可能导致 Jenkins 最终成功。
Q3:Sass 的 deprecated warning 是原因吗?
A:不是直接原因,但它提示你 SCSS 编译较多,可能提高内存峰值,从而更容易触发 OOM。
11. 总结(记住这句话就够)
Jenkins 构建日志出现
Killed,产物不完整但显示 SUCCESS
99% 是 Node 构建被 OOM Killer 干掉了✅ 解决方案:
放大 Node 内存(NODE_OPTIONS) + 失败即中断(set -e) + 检查 Docker 限制/加 swap