DeployEasy
Menu
ProductionTrung bình

Sửa lỗi Killed và exit code 137 khi build Docker, Next.js

Giải thích exit code 137, SIGKILL, Linux OOM Killer và cách kiểm tra RAM, swap, Docker limit khi build Next.js hoặc Node.js trên VPS.

· 9 phút đọc · 1.936 từ
Mục lục bài viết

Bạn chạy:

npm run build

hoặc:

docker build .

Sau một lúc terminal chỉ hiện:

Killed

Docker có thể báo:

process exited with code 137

Exit code 137 thường liên quan tới process nhận SIGKILL. Trên VPS RAM thấp, nguyên nhân rất phổ biến là Linux OOM Killer dừng process để cứu hệ thống khi hết bộ nhớ.

Tuy nhiên, không nên kết luận mọi 137 đều là OOM. Process cũng có thể bị người dùng hoặc hệ thống gửi SIGKILL vì lý do khác.

Exit code 137 có nghĩa gì?

Unix process thoát do signal thường được biểu diễn:

128 + signal number

SIGKILL là signal số 9:

128 + 9 = 137

Vì vậy:

exit code 137

nghĩa là process bị kill bằng SIGKILL.

SIGKILL không thể được process bắt hoặc xử lý graceful shutdown.

OOM là gì?

OOM là viết tắt của:

Out Of Memory

Khi hệ thống không còn đủ RAM và swap cho yêu cầu bộ nhớ, kernel có thể kích hoạt OOM Killer để chọn một hoặc nhiều process dừng.

Mục tiêu là giúp hệ thống còn khả năng hoạt động thay vì treo hoàn toàn.

Vì sao build Next.js dùng nhiều RAM?

Build có thể chạy:

  • TypeScript.
  • Bundler.
  • Minification.
  • Static page generation.
  • Image/source map processing.
  • Worker song song.
  • Dependency compilation.

RAM dùng trong build thường cao hơn RAM khi app chạy.

Một VPS 1 GB có thể chạy app nhỏ sau build nhưng không đủ để tự build source.

Vì sao Docker build bị 137?

Docker build process vẫn dùng tài nguyên của host hoặc giới hạn builder/container.

Các giai đoạn tốn RAM:

RUN npm ci
RUN npm run build

Nếu host chỉ có khoảng 1 GB RAM, Node.js và package manager có thể bị OOM.

Bước 1: Kiểm tra RAM

free -h

Ví dụ:

               total   used   free  shared  buff/cache  available
Mem:           957Mi   700Mi   70Mi    20Mi       187Mi      120Mi
Swap:             0B      0B     0B

Quan tâm available, không chỉ free.

Kiểm tra realtime:

watch -n 1 free -h

Hoặc:

htop

Bước 2: Tìm log OOM Killer

sudo journalctl -k |
grep -i -E 'out of memory|oom|killed process'

Theo thời gian:

sudo journalctl -k \
  --since "30 minutes ago" |
grep -i -E 'out of memory|oom|killed process'

Hoặc:

dmesg --ctime |
grep -i -E 'out of memory|oom|killed process'

Ví dụ:

Out of memory: Killed process 12345 (node)

Đây là bằng chứng mạnh rằng process bị OOM.

Bước 3: Kiểm tra cgroup/Docker

Container có thể bị OOM vì giới hạn riêng dù host còn RAM.

Kiểm tra:

docker inspect CONTAINER_NAME \
  --format '{{.State.OOMKilled}}'

Kết quả:

true

Xem giới hạn:

docker inspect CONTAINER_NAME \
  --format 'Memory={{.HostConfig.Memory}}'

Docker stats:

docker stats

Compose có thể có:

mem_limit: 512m

App vượt 512 MB sẽ bị kill dù host có nhiều hơn.

Bước 4: Xác định process dùng RAM

ps aux --sort=-%mem | head -n 15

Hoặc:

top
htop

Docker:

docker stats --no-stream

Có thể PostgreSQL, PM2, Docker daemon và app cũ đang chiếm RAM trước khi build.

Bước 5: Kiểm tra swap

swapon --show
free -h

Nếu không có swap:

Swap: 0B

VPS RAM thấp dễ OOM khi tải tăng đột ngột.

Tạo swap file

Ví dụ 2 GB:

sudo fallocate -l 2G /swapfile
sudo chmod 600 /swapfile
sudo mkswap /swapfile
sudo swapon /swapfile

Kiểm tra:

swapon --show
free -h

Thêm /etc/fstab:

/swapfile none swap sw 0 0

Test:

sudo mount -a

Swap không thay thế RAM. Build có thể rất chậm khi swap nhiều.

Kiểm tra nhà cung cấp có cho phép swap không.

Swappiness

Xem:

sysctl vm.swappiness

Thay tạm:

sudo sysctl vm.swappiness=10

Cấu hình lâu dài:

/etc/sysctl.d/99-deployeasy.conf
vm.swappiness=10

Không có giá trị tốt nhất cho mọi workload. Cần theo dõi memory pressure.

Giải pháp tốt nhất: Build trong CI

Thay vì build trên VPS:

GitHub Actions
    ↓ build image/artifact
Container Registry
    ↓ pull
VPS

VPS chỉ:

docker compose pull
docker compose up -d

Lợi ích:

  • Không tốn RAM build trên production.
  • Artifact được test.
  • Có version.
  • Dễ rollback.
  • Production không cần source/build tool đầy đủ.

Build artifact ngoài VPS

Next.js standalone:

CI build

.next/standalone
.next/static
public

đóng gói hoặc image

VPS

NestJS:

CI build

dist/
package files

VPS

Phải build cùng architecture/runtime phù hợp, đặc biệt với native dependency.

Tăng RAM VPS

Nếu workload thường xuyên vượt RAM, swap chỉ trì hoãn vấn đề.

Dấu hiệu cần nâng RAM:

  • Swap dùng liên tục.
  • OOM tái diễn.
  • Database bị kill.
  • App latency tăng.
  • Build và runtime cạnh tranh.
  • Memory available thường rất thấp.

VPS chạy Docker + Next.js + NestJS + PostgreSQL thường cần nhiều hơn 1 GB.

Dừng service không cần thiết trước build

Chỉ dùng như biện pháp tạm thời và có kế hoạch downtime.

Ví dụ:

docker compose stop web api
npm run build
docker compose start web api

Rủi ro:

  • Website down.
  • Build fail và service chưa start lại.
  • Database không nên dừng tùy tiện.

CI build vẫn là lựa chọn tốt hơn.

Giảm số worker build

Một số tool hỗ trợ giới hạn concurrency. Cấu hình phụ thuộc framework/version.

Không sử dụng biến hoặc flag không được tài liệu version hiện tại hỗ trợ.

Nguyên tắc:

  • Giảm parallelism.
  • Tắt source map không cần thiết.
  • Giảm plugin nặng.
  • Tách build.
  • Loại dependency không dùng.

Đo trước/sau.

Node.js heap limit

Node có tùy chọn:

NODE_OPTIONS="--max-old-space-size=1024"

Nhưng tăng heap trên VPS chỉ có 1 GB có thể làm OOM nhanh hơn vì process còn cần native memory và hệ thống cũng cần RAM.

Giảm heap:

NODE_OPTIONS="--max-old-space-size=512"

có thể khiến Node báo JavaScript heap out of memory thay vì kernel kill, nhưng build có thể không hoàn thành.

Không coi --max-old-space-size là cách tạo thêm RAM.

JavaScript heap out of memory khác 137

Node có thể tự báo:

FATAL ERROR: Reached heap limit
JavaScript heap out of memory

Đây là Node heap limit.

Exit 137 là SIGKILL ở cấp OS/cgroup.

Cả hai liên quan memory nhưng cách chẩn đoán khác.

Docker BuildKit resource

Docker Desktop có giới hạn VM riêng. Trên Windows/macOS, tăng memory Docker Desktop có thể cần thiết.

Trên Linux VPS, Docker dùng host/cgroup resource.

Kiểm tra builder:

docker buildx ls
docker info

Multi-stage có giảm RAM build không?

Multi-stage chủ yếu:

  • Giảm image runtime.
  • Tách build/runtime.
  • Tối ưu layer.

Nó không mặc nhiên giảm peak RAM của bước npm run build.

Tuy nhiên, Dockerfile tốt giúp tránh build lại dependency không cần thiết.

Build context quá lớn

Kiểm tra .dockerignore.

Không gửi:

node_modules
.next
.git
logs
backup
uploads

Build context lớn chủ yếu tốn I/O/disk nhưng cũng có thể làm build nặng hơn.

Kiểm tra disk

Khi disk đầy, build có thể fail với lỗi khác, nhưng đôi lúc tình trạng hệ thống trở nên khó chẩn đoán.

df -h
df -i
docker system df

Cả RAM và disk đều phải được kiểm tra.

OOM score

Kernel dùng nhiều yếu tố để chọn process kill.

Xem process:

cat /proc/PID/oom_score
cat /proc/PID/oom_score_adj

Không tùy tiện đặt OOM score để bảo vệ mọi process. Nếu bảo vệ app, kernel có thể kill database hoặc service quan trọng khác.

Giải pháp đúng là đủ tài nguyên và limit hợp lý.

systemd memory limit

Unit có thể có:

MemoryMax=600M

Nếu app vượt, systemd/cgroup có thể kill.

Xem:

systemctl show deployeasy-api \
  -p MemoryCurrent \
  -p MemoryMax

Kubernetes/container platform

Container có request/limit memory. Vượt limit thường dẫn tới OOMKilled.

Kiểm tra platform events và metric.

Không chỉ tăng limit; tìm memory leak và sizing đúng.

Memory leak

Nếu app RAM tăng liên tục sau nhiều giờ/ngày:

  • Heap snapshot.
  • Metric process memory.
  • Request pattern.
  • Cache không giới hạn.
  • Listener leak.
  • Large object.
  • Queue backlog.

Restart chỉ giảm triệu chứng tạm thời.

Quy trình chẩn đoán hoàn chỉnh

1. Ghi lại lệnh và thời điểm

npm run build
10:32
exit 137

2. Xem kernel log

sudo journalctl -k \
  --since "10 minutes ago"

3. Xem RAM/swap

free -h
swapon --show

4. Xem process/container

ps aux --sort=-%mem | head
docker stats --no-stream

5. Xem limit

docker inspect
systemctl show

6. Chọn giải pháp

Build CI
Tăng RAM
Thêm swap hỗ trợ
Giảm concurrency
Sửa memory leak
Điều chỉnh limit

Ví dụ VPS 1 GB

Trước build:

Ubuntu + Docker daemon: 250 MB
PostgreSQL:             200 MB
API:                    180 MB
Next.js:                200 MB
Available:              rất thấp

Build thêm có thể cần hàng trăm MB đến hơn 1 GB, dẫn tới OOM.

Giải pháp hợp lý:

CI build image
VPS chỉ chạy image

hoặc tăng RAM/tách database.

Không nên làm

Chạy lại nhiều lần

Nếu tài nguyên không đổi, kết quả có thể vẫn là OOM và gây downtime.

Tạo swap cực lớn rồi xem như đã sửa

Swap chậm và có thể làm server thrashing.

Tắt OOM Killer

Có thể làm toàn hệ thống treo và không SSH được.

Kill database để nhường RAM

Có nguy cơ mất dịch vụ và recovery không mong muốn.

Checklist

[ ] Xác nhận exit 137 = SIGKILL
[ ] Xem kernel OOM log
[ ] Kiểm tra free -h
[ ] Kiểm tra swap
[ ] Kiểm tra Docker OOMKilled
[ ] Kiểm tra cgroup/systemd limit
[ ] Xem process dùng RAM
[ ] Kiểm tra disk/inode
[ ] Build trong CI nếu có thể
[ ] Image/artifact có version
[ ] Tăng RAM nếu pressure kéo dài
[ ] Theo dõi memory runtime
[ ] Điều tra memory leak nếu RAM tăng liên tục

Câu hỏi thường gặp

Exit 137 luôn là hết RAM phải không?

Không. Nó nghĩa process nhận SIGKILL. OOM là nguyên nhân rất phổ biến, cần xác nhận bằng kernel/cgroup log.

Thêm swap có hết lỗi không?

Có thể giúp tải ngắn hạn nhưng không bảo đảm. Build có thể chậm và workload thường xuyên thiếu RAM vẫn cần nâng cấp.

Tăng Node heap có sửa không?

Chỉ hữu ích nếu Node heap limit là nút thắt và host còn RAM. Trên VPS thiếu RAM, tăng heap có thể làm OOM nặng hơn.

Docker build trên VPS hay CI tốt hơn?

Production VPS nhỏ thường nên pull image đã build/test từ CI.

Kết luận

Khi gặp Killed hoặc exit 137, đừng chỉ chạy lại. Hãy xác minh SIGKILL, kiểm tra OOM log, RAM, swap và container limit. Giải pháp bền vững thường là build ngoài production và cấp đủ tài nguyên.

Nguồn tham khảo

Đọc tiếp