Ngăn chặn Linux OOM Killer làm sập các ứng dụng Production

Linux tutorial - IT technology blog
Linux tutorial - IT technology blog

Khi các tiến trình biến mất: Cơn ác mộng PagerDuty lúc 2 giờ sáng

Hãy tưởng tượng bạn đang giám sát một môi trường production. Mọi thứ trông có vẻ ổn định cho đến khi một dịch vụ quan trọng—có lẽ là cơ sở dữ liệu PostgreSQL chính hoặc một instance Nginx lưu lượng cao—đột ngột biến mất. Bạn kiểm tra trạng thái, nhưng tiến trình đã không còn. Không có log crash ứng dụng và không có dấu hiệu cảnh báo trên dashboard Grafana. Đây chính là dấu hiệu điển hình của Linux Out of Memory (OOM) Killer.

Linux quản lý bộ nhớ theo triết lý “dùng hoặc mất”. Khi RAM cạn kiệt hoàn toàn, kernel phải đưa ra một lựa chọn tàn khốc: khai tử một tiến trình để ngăn toàn bộ hệ điều hành bị treo. Thật không may, kernel thường nhắm vào tiến trình nặng nhất, vốn thường là ứng dụng bạn cần nhất. Làm chủ cơ chế bộ nhớ này là điều thiết yếu đối với bất kỳ ai vận hành các hệ thống có tính sẵn sàng cao (high-availability).

Gần đây, tôi đã quản lý một loạt các node Ubuntu 22.04 chỉ có 4GB RAM. Bằng cách hướng dẫn kernel biết tiến trình nào có thể “hy sinh”, tôi đã giảm được khoảng 85% các vụ gián đoạn dịch vụ ngoài ý muốn mà không cần nâng cấp phần cứng.

Cơ chế của Overcommit

Để xử lý OOM Killer, trước tiên chúng ta cần hiểu tại sao nó được kích hoạt. Theo mặc định, Linux sử dụng một chiến lược gọi là “Overcommit”. Kernel cho phép các ứng dụng yêu cầu nhiều bộ nhớ hơn mức vật lý hiện có, đặt cược rằng hầu hết các tiến trình sẽ không sử dụng hết mức phân bổ cùng một lúc. Nó giống như việc một hãng hàng không bán quá số ghế trên một chuyến bay, giả định rằng một vài hành khách sẽ không đến.

Nhưng khi mọi tiến trình đồng loạt đòi hỏi RAM, hệ thống sẽ chạm giới hạn. Lúc này, hàm oom_killer() sẽ tính toán một oom_score cho mọi tiến trình. Nó ưu tiên các mục tiêu dựa trên ba yếu tố chính:

  • Mức sử dụng RAM: Những tiến trình tiêu thụ nhiều bộ nhớ là mục tiêu đầu tiên.
  • Tuổi thọ tiến trình: Kernel ưu tiên khai tử các tiến trình mới hơn là các daemon hệ thống đã chạy lâu dài.
  • User ID: Các tiến trình do root sở hữu được ưu tiên bảo vệ hơn một chút so với các tác vụ cấp người dùng.

Bước 1: Truy tìm bằng chứng

Đừng bắt đầu thay đổi cấu hình cho đến khi bạn có bằng chứng. Kernel ghi lại mọi hoạt động thực thi trong system log. Nếu cơ sở dữ liệu của bạn bị sập lúc 3:15 sáng, đó là nơi đầu tiên bạn nên kiểm tra.

# Kiểm tra bộ đệm kernel để tìm các sự kiện OOM gần đây
dmesg | grep -i oom

# Tìm kiếm trong log lịch sử trên Debian hoặc Ubuntu
grep -i 'killed process' /var/log/syslog

# Tìm kiếm log trên RHEL, CentOS, hoặc AlmaLinux
grep -i 'killed process' /var/log/messages

Hãy tìm một dòng như Out of memory: Kill process 1234 (mysqld) score 500. Điều này xác nhận OOM Killer chính là thủ phạm. Log cũng sẽ hiển thị một bản dump bộ nhớ đầy đủ, tiết lộ chính xác lượng RAM và Swap còn lại khi sự việc xảy ra.

Bước 2: Bảo vệ các dịch vụ quan trọng

Mặc dù bạn không nên tắt hoàn toàn OOM Killer, bạn có thể tác động đến danh sách mục tiêu của nó. Mỗi tiến trình có một file điều chỉnh điểm số nằm tại /proc/[PID]/oom_score_adj. Giá trị này dao động từ -1000 (không bao giờ giết) đến 1000 (ưu tiên giết trước).

Giả sử bạn có một daemon SSH quan trọng và một script xử lý log chạy ngầm. Bạn muốn đảm bảo SSH luôn sống để có thể đăng nhập và xử lý sự cố. Đây là cách bảo vệ một tiến trình thủ công:

# Lấy PID của dịch vụ của bạn
pidof sshd

# Làm cho nó gần như không thể bị khai tử
echo -1000 > /proc/$(pidof sshd)/oom_score_adj

Các thay đổi thủ công sẽ biến mất sau khi khởi động lại. Để khắc phục vĩnh viễn trong systemd, hãy thêm phần điều chỉnh trực tiếp vào file service của bạn. Đây là cách sạch nhất để quản lý các mức ưu tiên.

[Service]
# Chỉnh sửa qua: systemctl edit mysqld
OOMScoreAdjust=-500

Bước 3: Thắt chặt chính sách Overcommit

Nếu máy chủ của bạn thường xuyên bị “hụt hơi”, hãy cân nhắc thay đổi cách kernel xử lý các yêu cầu bộ nhớ. Bạn có thể tinh chỉnh điều này thông qua tham số vm.overcommit_memory. Nó hỗ trợ ba chế độ:

  • 0 (Heuristic): Chế độ mặc định nơi kernel “đoán” xem nó có đủ RAM hay không.
  • 1 (Always): Kernel luôn chấp nhận các yêu cầu cấp phát bộ nhớ. Điều này hữu ích cho các ứng dụng khoa học chuyên dụng nhưng nguy hiểm cho các máy chủ thông thường.
  • 2 (Strict): Kernel chỉ cấp phát bộ nhớ đến một giới hạn cụ thể (Swap + một tỷ lệ phần trăm của RAM).

Tôi khuyên dùng Chế độ 2 cho các cơ sở dữ liệu production nơi tính ổn định là ưu tiên hàng đầu. Nó khiến các lệnh gọi malloc thất bại thay vì để kernel khai tử các tiến trình sau đó. Sử dụng các lệnh sau để áp dụng:

# Áp dụng ngay lập tức
sudo sysctl -w vm.overcommit_memory=2

# Duy trì sau khi khởi động lại
echo "vm.overcommit_memory = 2" | sudo tee -a /etc/sysctl.conf
sudo sysctl -p

Bước 4: Lưới an toàn Swap

Đôi khi giải pháp đơn giản nhất lại hiệu quả nhất. Nếu máy chủ 4GB RAM của bạn đang ở mức báo động, việc thêm một file swap 2GB sẽ cung cấp một vùng đệm quan trọng. Swap dựa trên ổ đĩa chậm hơn đáng kể so với RAM, nhưng nó hoạt động như một “van xả áp” ngăn OOM Killer kích hoạt trong các đợt tăng đột biến nhỏ.

# Tạo và kích hoạt file swap 2GB
sudo fallocate -l 2G /swapfile
sudo chmod 600 /swapfile
sudo mkswap /swapfile
sudo swapon /swapfile

# Thêm vào fstab để duy trì sau khi khởi động lại
echo '/swapfile none swap sw 0 0' | sudo tee -a /etc/fstab

Khi swap đã hoạt động, hãy kiểm tra giá trị swappiness của bạn. Giá trị này (0-100) cho kernel biết mức độ ưu tiên chuyển dữ liệu sang ổ đĩa. Đối với hầu hết các máy chủ hiện đại, giá trị thấp như 10 là hoàn hảo.

# Đặt swappiness thành 10 để có hiệu suất tốt hơn
sudo sysctl -w vm.swappiness=10

Tinh chỉnh VFS Cache

Thiết lập vm.vfs_cache_pressure là một viên ngọc ẩn khác. Nó kiểm soát mức độ quyết liệt của kernel trong việc thu hồi bộ nhớ được sử dụng để lưu cache metadata của thư mục và file. Giá trị mặc định là 100. Nếu bạn giảm xuống 50, kernel sẽ giữ metadata đó trong RAM lâu hơn. Điều này làm giảm I/O ổ đĩa nhưng đánh đổi bằng việc sử dụng thêm một chút bộ nhớ.

sudo sysctl -w vm.vfs_cache_pressure=50

Lời kết

Quản lý bộ nhớ Linux là việc thiết lập các ranh giới. Bằng cách xác định các sự kiện OOM trong log, bảo vệ các ứng dụng quan trọng bằng oom_score_adj và cấu hình các chính sách overcommit nghiêm ngặt, bạn sẽ xây dựng được một hệ thống xử lý áp lực một cách mượt mà.

Luôn theo dõi mức sử dụng cơ bản của bạn bằng htop. Nếu máy chủ của bạn liên tục phải dựa vào file swap, không có sự tinh chỉnh kernel nào có thể thay thế nhu cầu về thêm RAM vật lý. Hãy sử dụng những thủ thuật này để tranh thủ thời gian và sự ổn định, nhưng hãy lắng nghe khi phần cứng thông báo rằng nó đã quá tải.

Share: