Một dịch vụ đang chạy bỗng nhiên biến mất không rõ lý do, container Docker tự động khởi động lại, hoặc website bất ngờ trả mã lỗi 502 trong vài phút rồi tự hoạt động trở lại. Đây là những dấu hiệu kinh điển cho thấy nhân Linux vừa kích hoạt cơ chế OOM Killer (Out of Memory Killer) để cưỡng chế tắt một tiến trình nhằm cứu máy chủ khỏi bị treo cứng.
Một điểm dễ gây nhầm lẫn là khi chạy lệnh free -h sau sự cố, lượng RAM trống (available) thường vẫn hiển thị dồi dào. Nguyên nhân rất đơn giản: ngay khi OOM Killer can thiệp, toàn bộ bộ nhớ của tiến trình bị khai tử đã được thu hồi và trả lại cho hệ điều hành.
Để điều tra chính xác, bạn không thể dựa vào trạng thái RAM tức thời mà phải lật lại nhật ký sự kiện của kernel quanh đúng mốc thời gian xảy ra lỗi.
Dòng “Killed process” nằm gần cuối chuỗi sự kiện; cần quay lại tìm áp lực bộ nhớ xuất hiện trước đó.
OOM Killer là gì và cơ chế hoạt động ra sao?
Linux có cơ chế tối ưu hóa bộ nhớ gọi là Overcommit: cho phép các ứng dụng yêu cầu cấp phát bộ nhớ ảo nhiều hơn dung lượng RAM vật lý thực tế, với giả định rằng không phải tiến trình nào cũng sử dụng hết 100% lượng RAM đã đăng ký cùng một lúc.
Tuy nhiên, khi tổng lượng RAM thực tế và phân vùng Swap cạn kiệt, hệ thống không còn bất kỳ trang nhớ nào để cấp phát tiếp. Để bảo vệ toàn bộ máy chủ không bị sập nguồn (kernel panic), nhân Linux buộc phải kích hoạt thuật toán OOM Killer.
Thuật toán này sẽ chấm điểm toàn bộ các tiến trình đang chạy (oom_score) dựa trên:
- Tỷ lệ phần trăm RAM mà tiến trình đang chiếm dụng.
- Thời gian tiến trình đã hoạt động (tiến trình mới sinh ra thường dễ bị chọn hơn tiến trình nền tảng).
- Điểm ưu tiên điều chỉnh của quản trị viên (
oom_score_adj).
Tiến trình có điểm số cao nhất sẽ bị gửi tín hiệu SIGKILL (tín hiệu dừng cưỡng chế không thể chặn) để thu hồi toàn bộ bộ nhớ ngay lập tức. Thông thường, các tiến trình ứng dụng lớn như MySQL, Java, Node.js, PHP-FPM worker chính là những nạn nhân đầu tiên bị chọn.
Bước 1: Tìm bằng chứng OOM Killer trong nhật ký Kernel
Bằng chứng xác thực nhất về việc OOM Killer vừa can thiệp luôn nằm trong thông điệp của kernel (dmesg hoặc journalctl).
Chạy lệnh trích xuất các dòng log liên quan:
# Kiểm tra log kernel của lần boot hiện tại
sudo dmesg -T | grep -i -E 'out of memory|oom|killed process'
# Hoặc dùng journalctl nếu sự cố vừa xảy ra trong ngày
sudo journalctl -k --since "today" | grep -i -E 'oom|killed process'Nếu máy chủ vừa bị reboot sau sự cố, hãy xem log của lần boot trước đó bằng cờ -b -1:
sudo journalctl -k -b -1 | grep -i -E 'oom|killed process'Một thông báo OOM Killer tiêu chuẩn sẽ có định dạng:
Out of memory: Killed process 14205 (mysqld) total-vm:2847520kB, anon-rss:1450120kB, file-rss:0kB, shmem-rss:0kB, UID:1001 pgtables:3120kB oom_score_adj:0Dòng log này xác nhận không thể chối cãi: tiến trình mysqld (PID 14205) đã bị kernel khai tử vào đúng thời điểm đó vì hệ thống cạn kiệt bộ nhớ.
Ngoài kernel OOM, trên các bản Ubuntu mới có tích hợp dịch vụ systemd-oomd giám sát áp lực bộ nhớ (PSI) ở tầng cgroup. Hãy kiểm tra thêm trạng thái của dịch vụ này nếu log kernel không thấy:
sudo journalctl -u systemd-oomd --since "today"Bước 2: Phân biệt Global OOM (toàn VPS) và Container OOM
Đây là điểm khác biệt kỹ thuật rất quan trọng nhưng thường bị bỏ qua trong môi trường container:
1. Global OOM (Hết RAM toàn hệ thống)
Xảy ra khi tổng dung lượng RAM vật lý của toàn bộ VPS bị cạn kiệt. Kernel sẽ rà soát toàn bộ cây tiến trình của cả máy chủ và chọn tiến trình nặng nhất để kill. Trong trường hợp này, free -h trước đó sẽ thấy MemAvailable giảm về 0 và Swap bị sử dụng tối đa.
2. Container OOM (Chạm giới hạn tài nguyên của Container)
Khi bạn chạy Docker hoặc Docker Compose có thiết lập giới hạn bộ nhớ (ví dụ mem_limit: 1g hoặc deploy.resources.limits.memory: 1G):
- Toàn bộ VPS có thể có 16GB RAM và vẫn còn trống tới 10GB RAM.
- Nhưng bản thân container chỉ được cấp tối đa 1GB. Khi ứng dụng bên trong container dùng vượt quá 1GB, hệ thống cgroup sẽ lập tức kill tiến trình bên trong container đó mà không làm ảnh hưởng tới các dịch vụ khác trên VPS.
Kiểm tra xem container có vừa bị OOM hay không:
docker inspect <container_name> --format '{{json .State}}'Nếu trường "OOMKilled": true và mã thoát "ExitCode": 137 (128 + 9 tức là bị kill bởi SIGKILL), bạn khẳng định 100% container bị kill do chạm giới hạn bộ nhớ được cấp phát, chứ không phải do VPS thiếu RAM.
Bước 3: Đọc hiểu các chỉ số bộ nhớ đúng cách
Khi kiểm tra bộ nhớ trên Linux, bạn cần nắm rõ ý nghĩa thực sự của từng cột:
free -h total used free shared buff/cache available
Mem: 3.8Gi 1.2Gi 210Mi 45Mi 2.4Gi 2.3Gi
Swap: 2.0Gi 180Mi 1.8Gifree(210Mi): Lượng RAM hoàn toàn trống chưa được gán cho bất kỳ mục đích nào. Con số này thấp là hoàn toàn bình thường vì Linux luôn tận dụng RAM nhàn rỗi để làm bộ nhớ đệm đĩa cứng (buff/cache).buff/cache(2.4Gi): Bộ nhớ đệm tệp tin. Khi ứng dụng cần thêm RAM, Linux có thể giải phóng phần bộ nhớ này ngay lập tức để cấp cho ứng dụng mà không cần phải hoán đổi sang swap.available(2.3Gi): Chỉ số quan trọng nhất. Đây là lượng RAM thực tế mà hệ thống có thể cung cấp ngay lập tức cho các tiến trình mới mà không gây nghẽn swap. Khiavailablegiảm xuống dưới 10% tổng dung lượng, máy chủ đang bước vào vùng nguy hiểm.
Theo dõi áp lực Swap bằng vmstat
Dung lượng Swap đã dùng (used) không phải là vấn đề nếu đó là dữ liệu của các tiến trình không hoạt động (idle). Điều nguy hiểm là hiện tượng hoán đổi dữ liệu liên tục ra vào đĩa cứng (Swap Thrashing).
Chạy lệnh lấy mẫu:
vmstat 2 5Quan sát hai cột:
si(Swap-In): Dung lượng dữ liệu đang phải đọc từ Swap ngược lại vào RAM mỗi giây.so(Swap-Out): Dung lượng dữ liệu đang phải đẩy từ RAM ra Swap mỗi giây.
Nếu si và so liên tục duy trì ở mức hàng chục megabyte/giây, ổ đĩa sẽ bị nghẽn đọc ghi I/O nghiêm trọng, website sẽ phản hồi chậm chạp và OOM Killer sắp sửa được kích hoạt.
Bước 4: Tìm thủ phạm thực sự làm phình to bộ nhớ
Tiến trình bị OOM Killer khai tử (ví dụ mysqld) chỉ là nạn nhân bị chọn vì nó có dung lượng lớn nhất tại thời điểm đó, chưa chắc nó là thủ phạm làm tràn bộ nhớ.
Thủ phạm thực sự thường rơi vào các trường hợp sau:
- Lỗi rò rỉ bộ nhớ (Memory Leak): Một tiến trình Node.js, Python worker hoặc PHP daemon chạy ngầm không giải phóng các biến toàn cục sau mỗi request. Bộ nhớ của nó phình to từ 200MB lên 2GB sau 3 ngày hoạt động và nuốt trọn toàn bộ RAM của máy chủ.
- Tác vụ xử lý dữ liệu đột biến: Một nhân viên bấm nút xuất báo cáo Excel chứa 500.000 dòng dữ liệu khiến ứng dụng nạp toàn bộ dữ liệu vào RAM cùng một lúc.
- Cấu hình worker pool quá cao: Cấu hình PHP-FPM cho phép tới 50 worker (
pm.max_children = 50), trong khi mỗi worker tiêu tốn trung bình 80MB RAM. Khi có 50 kết nối đồng thời, PHP-FPM sẽ đòi tới 4GB RAM — vượt quá dung lượng 2GB của toàn bộ VPS.
Khi hệ thống vừa phục hồi, hãy chụp lại danh sách các tiến trình chiếm dụng nhiều RAM nhất:
ps -eo pid,ppid,user,rss,%mem,comm --sort=-rss | head -n 15(Cột rss biểu thị dung lượng RAM vật lý thực tế mà tiến trình đang chiếm dụng tính bằng Kilobyte).
Bước 5: Các giải pháp phòng ngừa OOM tái diễn
Để chấm dứt tình trạng OOM Killer ghé thăm máy chủ, bạn cần áp dụng các biện pháp phòng vệ theo từng tầng:
1. Luôn thiết lập phân vùng Swap hợp lý
Nhiều bản cài đặt VPS mặc định không tạo phân vùng Swap (SwapTotal = 0). Khi không có Swap, chỉ cần một biến động nhỏ về lưu lượng truy cập cũng có thể kích hoạt OOM Killer ngay lập tức vì hệ điều hành không có bất kỳ khoảng đệm an toàn nào.
Hãy luôn tạo một file Swap từ 2GB đến 4GB trên ổ SSD:
sudo fallocate -l 2G /swapfile
sudo chmod 600 /swapfile
sudo mkswap /swapfile
sudo swapon /swapfile
echo '/swapfile none swap sw 0 0' | sudo tee -a /etc/fstabĐiều chỉnh tham số vm.swappiness về mức 10 hoặc 20 để Linux ưu tiên dùng RAM và chỉ đẩy dữ liệu sang Swap khi thực sự cần thiết:
sudo sysctl vm.swappiness=10
echo 'vm.swappiness=10' | sudo tee -a /etc/sysctl.conf2. Giới hạn tài nguyên ở tầng ứng dụng và Container
- Với Docker Compose: Luôn khai báo giới hạn bộ nhớ an toàn cho từng service để ngăn một container lỗi đánh sập toàn bộ các container khác:
services:
backend:
deploy:
resources:
limits:
memory: 1024M- Với PHP-FPM: Giới hạn
pm.max_childrendựa trên công thức thực tế:(Tổng RAM khả dụng - RAM cho MySQL và Nginx) / Dung lượng RAM trung bình của 1 worker. - Với MySQL/MariaDB: Cấu hình
innodb_buffer_pool_sizeở mức phù hợp (thường từ 50% đến 60% tổng RAM của VPS nếu chạy chung với web server).
3. Tự động khởi động lại các worker có nguy cơ rò rỉ bộ nhớ
Với các tiến trình dễ bị memory leak (như PHP-FPM), hãy cấu hình tự động giải phóng tiến trình sau khi phục vụ một số lượng request nhất định:
; Tự động khởi động lại worker sau mỗi 1000 request để xả sạch RAM rò rỉ
pm.max_requests = 10004. Nâng cấp cấu hình VPS khi tải tăng trưởng bền vững
Nếu bạn đã tối ưu hóa mã nguồn, giới hạn worker pool và tạo Swap nhưng dung lượng MemAvailable trong các giờ cao điểm vẫn liên tục chạm đáy do lượng khách hàng thực tế tăng trưởng, đã đến lúc nâng cấp gói RAM lớn hơn theo hướng dẫn tại bài Khi nào nên nâng cấp VPS, khi nào nên tối ưu ứng dụng trước?.
Checklist kiểm tra và xử lý sự cố OOM
- ☐ Đã tra cứu log kernel bằng
dmesg -Thoặcjournalctl -kvà tìm thấy dòngOut of memory: Killed process. - ☐ Đã xác định đích danh tiến trình bị khai tử (PID, tên dịch vụ) và thời điểm chính xác xảy ra sự cố.
- ☐ Đã kiểm tra xem đây là Global OOM (toàn VPS) hay Container OOM (
ExitCode: 137trong Docker). - ☐ Đã kiểm tra chỉ số
availabletrongfree -hthay vì chỉ nhìn vào chỉ sốfree. - ☐ Máy chủ đã được cấu hình phân vùng Swap tối thiểu 2GB–4GB làm khoảng đệm an toàn.
- ☐ Đã rà soát cấu hình worker pool (
pm.max_children) và bộ đệm database (innodb_buffer_pool_size) xem có bị cấp vượt quá dung lượng RAM thực tế không. - ☐ Các container Docker đã được thiết lập
limits.memoryđể cô lập rủi ro. - ☐ Đã thiết lập cảnh báo khi
MemAvailablegiảm xuống dưới 15%–20% trên hệ thống giám sát.
OOM Killer không phải là lỗi của hệ điều hành, mà là cơ chế phòng vệ cuối cùng của Linux để bảo vệ tính toàn vẹn của máy chủ. Khi OOM làm sập ứng dụng trong một sự cố production đang diễn ra, bạn cần phối hợp xử lý theo Quy trình 30 phút đầu khi website gặp sự cố để vừa giữ được bằng chứng kernel vừa đưa dịch vụ trở lại hoạt động an toàn. Bằng cách đọc đúng nhật ký sự kiện và chủ động phân bổ ngân sách bộ nhớ cho từng dịch vụ, bạn sẽ giữ cho hệ thống luôn vận hành ổn định mà không lo bị ngắt quãng dịch vụ đột ngột.



