Thử nghiệm nhanh: Giới hạn 500MB RAM trong 3 phút
Đang vội và muốn thấy ngay kết quả? Hãy thử giới hạn một tiến trình chỉ được phép dùng tối đa 500MB RAM. Trên các bản Linux hiện đại như Ubuntu 22.04+, Fedora hay AlmaLinux 9, mọi quyền năng của cgroups v2 đều nằm gọn trong thư mục /sys/fs/cgroup.
Trước hết, hãy tạo một nhóm quản lý mới (ví dụ: han-che-app):
sudo mkdir /sys/fs/cgroup/han-che-app
Thiết lập ngưỡng RAM 500MB cho nhóm này cực kỳ đơn giản:
echo "500M" | sudo tee /sys/fs/cgroup/han-che-app/memory.max
Để áp dụng, bạn chỉ cần ghi PID của tiến trình vào file cgroup.procs. Một cách chuyên nghiệp hơn là dùng systemd-run để khởi chạy script Python với giới hạn có sẵn:
systemd-run --user --scope -p MemoryMax=500M python3 crawler_data.py
Giờ thì con script của bạn có thể thoải mái chạy mà không lo nó bỗng dưng “ăn sạch” RAM của máy chủ.
Cgroups v2: Bộ khung quản lý tài nguyên thế hệ mới
Cgroups (Control Groups) là tính năng cốt lõi của nhân Linux giúp phân bổ tài nguyên như CPU, RAM, băng thông hay I/O cho các nhóm tiến trình. Khác với sự rắc rối của phiên bản v1, cgroups v2 gom tất cả về một mối. Nó hoạt động theo cấu trúc cây thư mục (hierarchy) cực kỳ tường minh.
Thực tế tại các dự án cũ chạy CentOS 7, việc tối ưu cgroups v1 thường khiến anh em sysadmin đau đầu vì cấu trúc phân tán. Khi chuyển sang các bản kernel mới, việc quản lý trở nên dễ thở hơn nhiều. Mỗi folder bạn tạo trong /sys/fs/cgroup đóng vai trò như một “vùng xanh” an toàn, giữ cho các tiến trình ngốn tài nguyên không gây ảnh hưởng đến toàn cục.
Tại sao phải dùng cgroups khi đã có Docker?
Nhiều người thường nghĩ Docker với các tham số --memory hay --cpus là đủ. Tuy nhiên, trong môi trường production, không phải lúc nào chúng ta cũng đóng gói mọi thứ vào container. Những ứng dụng legacy, script cronjob chạy ngầm hoặc chính các database core thường chạy trực tiếp trên VM để tận dụng tối đa hiệu năng.
Sử dụng trực tiếp cgroups v2 mang lại những lợi ích sát sườn:
- Loại bỏ hoàn toàn chi phí overhead (dù nhỏ) của Docker.
- Kiểm soát chặt chẽ các service hệ thống (Nginx, MySQL) chạy trực tiếp trên OS.
- Nắm vững cơ chế cốt lõi mà chính Docker hay Kubernetes đang vận hành bên dưới.
Cấu hình các thông số quan trọng cho ứng dụng
1. Siết chặt CPU (CPU Quota)
Trong thế giới cgroups v2, CPU được điều phối qua file cpu.max. File này nhận hai giá trị: giới hạn thời gian sử dụng và chu kỳ (mặc định 100ms).
Giả sử bạn muốn một tiến trình chỉ được dùng tối đa 20% sức mạnh của một nhân CPU:
# Sử dụng 20ms trong mỗi chu kỳ 100ms
echo "20000 100000" | sudo tee /sys/fs/cgroup/han-che-app/cpu.max
2. Quản lý RAM (Memory Limit)
Có hai ngưỡng bạn cần đặc biệt lưu ý để tránh treo máy:
memory.high: Ngưỡng cảnh báo. Khi chạm mốc này, hệ thống sẽ ép tiến trình dọn dẹp bộ nhớ (reclaim) nhưng không giết nó ngay.memory.max: Ngưỡng tử thần. Vượt qua con số này, kernel sẽ kích hoạt OOM Killer để chấm dứt tiến trình ngay lập tức.
echo "1G" | sudo tee /sys/fs/cgroup/han-che-app/memory.high
echo "1.5G" | sudo tee /sys/fs/cgroup/han-che-app/memory.max
3. Giới hạn tốc độ đọc ghi ổ đĩa (I/O)
Tính năng này cực kỳ hữu ích cho các script backup hoặc nén file dung lượng lớn. Bạn sẽ không muốn một tiến trình backup làm nghẽn toàn bộ băng thông ổ cứng, khiến website không thể truy xuất dữ liệu.
Đầu tiên, hãy dùng lsblk để xác định ID ổ đĩa (major:minor). Nếu ổ đĩa là 8:0 và bạn muốn giới hạn tốc độ đọc ở mức 10MB/s:
echo "8:0 rbps=10485760" | sudo tee /sys/fs/cgroup/han-che-app/io.max
Triển khai chuyên nghiệp với Systemd
Việc thao tác thủ công với file trong /sys/fs/cgroup chỉ phù hợp khi debug nhanh. Đối với môi trường thực tế, cách chuẩn mực nhất là cấu hình trực tiếp vào file service của Systemd.
Hãy mở file service của bạn (ví dụ: /etc/systemd/system/my-app.service) và thêm các dòng sau vào mục [Service]:
[Service]
ExecStart=/usr/bin/python3 /opt/my-app/main.py
# Giới hạn CPU 50%
CPUQuota=50%
# Giới hạn RAM cứng 1GB, mềm 800MB
MemoryMax=1G
MemoryHigh=800M
# Giới hạn tốc độ đọc ghi IO 10MB/s
IOReadBandwidthMax=/dev/sda 10M
Cuối cùng, chỉ cần chạy systemctl daemon-reload và khởi động lại service. Hệ thống sẽ tự động lo phần còn lại một cách sạch sẽ.
Kinh nghiệm thực chiến: Đừng để con số đánh lừa
Sau nhiều lần xử lý sự cố, mình rút ra vài bài học đắt giá khi làm việc với cgroups v2:
- Luôn có khoảng đệm (Buffer): Nếu ứng dụng Java cần 1GB RAM để khởi động, đừng bao giờ set
memory.maxđúng 1GB. Hãy cộng thêm khoảng 20-30% cho các chi phí phát sinh đột xuất. - Ưu tiên
memory.high: Hãy để ứng dụng có cơ hội tự giải phóng bộ nhớ thay vì bị kernel “trảm” thẳng tay bằng lệnh OOM Killer. - Quan sát
memory.events: Đây là nơi chứa những thông tin vô giá. Nó sẽ cho bạn biết chính xác số lần ứng dụng suýt chết hụt vì thiếu RAM.
Mình từng gặp ca khó với một ứng dụng Java chạy trong cgroup. Dù đã set Heap Size 1GB nhưng app vẫn chết liên tục. Hóa ra, JVM còn tiêu tốn thêm khoảng 200MB cho Off-heap và Stack. Sau khi kiểm tra memory.events và thấy counter của oom_kill tăng vọt, mình mới vỡ lẽ và điều chỉnh lại con số cho hợp lý.
Cgroups v2 không hề khó nếu bạn nắm vững đặc thù của ứng dụng. Hy vọng bài viết này giúp anh em có thêm công cụ đắc lực để trị những tiến trình cứng đầu trên server!

