Bối cảnh & Lý do cô lập tài nguyên lại quan trọng
Hãy hình dung kịch bản này: một Celery worker chưa được tối ưu hoặc một tác vụ xử lý ảnh hàng loạt đột ngột bị rò rỉ 6 GB RAM trên máy chủ có 8 GB. Khi không có ranh giới tài nguyên được áp đặt, Linux Out-Of-Memory (OOM) killer sẽ can thiệp đột ngột. Thay vì cô lập worker vi phạm, nó có thể tắt ngay PostgreSQL hoặc sshd để giải phóng bộ nhớ tức thì. Đây chính là vấn đề “hàng xóm ồn ào” (noisy-neighbor) kinh điển.
Control Groups (cgroups) giải quyết vấn đề này bằng cách đo lường, giới hạn tốc độ (throttling) và cô lập tài nguyên phần cứng. Trong khi Cgroups v1 phân tách các controller trên các cây thư mục phân mảnh, Cgroups v2 giới thiệu một cấu trúc phân cấp thống nhất duy nhất (unified hierarchy). Mỗi tiến trình chỉ thuộc về đúng một đường dẫn cgroup. Cấu trúc này mang lại khả năng phân bổ tài nguyên có thể dự đoán được, phân quyền rõ ràng hơn và thống kê chính xác cho cả bộ nhớ ẩn danh (anonymous memory) lẫn filesystem page cache.
Bạn không cần phải viết các script cấp thấp can thiệp trực tiếp vào /sys/fs/cgroup/. Các bản phân phối Linux hiện đại ủy quyền việc này cho systemd thông qua các unit gọi là Slice. Slice tổ chức các dịch vụ thành các nhóm logic, cho phép bạn áp dụng trọng số CPU (CPU weights), mức trần bộ nhớ và giới hạn I/O đĩa trên toàn bộ các tầng tải công việc chỉ với vài chỉ thị khai báo.
Cài đặt & Kích hoạt Cgroups v2
Các bản phân phối hiện đại—bao gồm Ubuntu 22.04+, Debian 11+, Fedora 31+ và RHEL 9+—đều bật Cgroups v2 theo mặc định. Trước tiên, hãy bắt đầu bằng việc kiểm tra phiên bản mà máy chủ của bạn đang chạy.
Bước 1: Kiểm tra phiên bản Cgroup hiện tại
Kiểm tra loại filesystem của cgroup bằng lệnh stat:
stat -fc %T /sys/fs/cgroup/
Nếu đầu ra trả về cgroup2fs, kernel của bạn đã đang chạy Cgroups v2 thống nhất. Nếu trả về tmpfs, hệ thống đang sử dụng Cgroups v1 cũ.
Bước 2: Kích hoạt Unified Hierarchy (Nếu đang ở Cgroups v1)
Để kích hoạt cấu trúc phân cấp thống nhất trên các bản phân phối cũ hơn, hãy truyền tham số kernel tương ứng qua GRUB:
sudo nano /etc/default/grub
Thêm systemd.unified_cgroup_hierarchy=1 vào chuỗi GRUB_CMDLINE_LINUX:
GRUB_CMDLINE_LINUX="systemd.unified_cgroup_hierarchy=1"
Tạo lại cấu hình GRUB và khởi động lại máy:
# Trên Ubuntu/Debian
sudo update-grub
sudo reboot
# Trên RHEL/AlmaLinux/Rocky
sudo grub2-mkconfig -o /boot/grub2/grub.cfg
sudo reboot
Bước 3: Xác minh các Subsystem Controller khả dụng
Kiểm tra các controller đang hoạt động có sẵn trong root cgroup:
cat /sys/fs/cgroup/cgroup.controllers
Xác nhận rằng cpu, memory, io và pids xuất hiện trong kết quả trả về.
Cấu hình: Systemd Slice và các ràng buộc tài nguyên
Systemd tự động phân chia khối lượng công việc thành ba root slice mặc định:
system.slice: Các daemon chạy ngầm và dịch vụ hệ thống.user.slice: Các phiên đăng nhập tương tác và tiến trình của từng người dùng.machine.slice: Máy ảo và khối lượng công việc của container.
Tạo các slice tùy chỉnh cho phép bạn cô lập các tác vụ ngầm, công cụ phân tích dữ liệu hoặc microservices trong sandbox mà không làm cạn kiệt tài nguyên của các dịch vụ hệ thống thiết yếu.
Bước 1: Định nghĩa một Custom Slice Unit
Tạo một slice chuyên dụng cho các backend worker tại /etc/systemd/system/workload-workers.slice:
sudo nano /etc/systemd/system/workload-workers.slice
Thêm cấu hình sau để áp dụng hạn ngạch CPU, giới hạn bộ nhớ và giới hạn ghi đĩa:
[Unit]
Description=Slice giới hạn tài nguyên cho các dịch vụ Worker
Before=slices.target
[Slice]
# --- Phân bổ CPU ---
# Giới hạn toàn bộ slice ở mức 200% CPU (tương đương 2 core đầy đủ)
CPUQuota=200%
# CPUWeight (1-10000) thiết lập độ ưu tiên tương đối khi xảy ra tranh chấp CPU
CPUWeight=500
# --- Ràng buộc bộ nhớ ---
# MemoryHigh sẽ bóp hiệu năng tiến trình và thu hồi mạnh page cache
MemoryHigh=2G
# MemoryMax là giới hạn cứng: kích hoạt OOM giới hạn trong slice nếu vượt quá
MemoryMax=2.5G
# Giới hạn dung lượng swap để tránh gây nghẽn đĩa (disk thrashing)
MemorySwapMax=512M
# --- Giới hạn I/O ---
# Giới hạn băng thông đọc và ghi tối đa trên ổ đĩa chính
IOWriteBandwidthMax=/dev/sda 25M
IOReadBandwidthMax=/dev/sda 50M
# --- Giới hạn tiến trình ---
TasksMax=1024
Bước 2: Gán dịch vụ vào Custom Slice
Thay vì chỉnh sửa trực tiếp các file unit trong thư mục của nhà cung cấp, hãy gán dịch vụ bằng cơ chế ghi đè drop-in của systemd. Ví dụ: để gắn queue-worker.service vào slice mới của bạn:
sudo systemctl edit queue-worker.service
Thêm chỉ thị gán slice vào bên trong file override:
[Service]
Slice=workload-workers.slice
Lưu và thoát trình soạn thảo. Tải lại cấu hình trình quản lý systemd và khởi động lại worker:
sudo systemctl daemon-reload
sudo systemctl restart queue-worker.service
Bước 3: Chỉ thị tài nguyên trực tiếp trong Service (Cách thay thế)
Nếu bạn chỉ cần giới hạn một dịch vụ độc lập duy nhất thay vì toàn bộ cụm dịch vụ, hãy xác định các giới hạn trực tiếp trong phần [Service] của unit đó:
[Service]
Type=simple
ExecStart=/usr/bin/python3 /opt/app/worker.py
Restart=on-failure
# Giới hạn CPU và Bộ nhớ ở cấp độ dịch vụ
CPUQuota=80%
MemoryHigh=800M
MemoryMax=1G
OOMPolicy=stop
Xác minh & Giám sát theo thời gian thực
Sau khi thiết lập các giới hạn, hãy kiểm tra cách hệ thống phản ứng dưới tải nặng.
1. Kiểm tra thành viên Slice đang hoạt động
Xác minh dịch vụ của bạn đang chạy trong đúng slice bằng lệnh systemctl status:
systemctl status queue-worker.service
Kiểm tra dòng CGroup: trong đầu ra. Nó phải trỏ trực tiếp đến /workload-workers.slice/queue-worker.service.
2. Giám sát việc sử dụng tài nguyên trực tiếp với systemd-cgtop
Systemd đi kèm công cụ systemd-cgtop, một trình giám sát tương tự như top được tổ chức theo cấu trúc phân cấp cgroup:
systemd-cgtop -m
Sử dụng các phím tắt tương tác để sắp xếp các nhóm đang chạy:
- Nhấn c để sắp xếp theo mức sử dụng CPU.
- Nhấn m để sắp xếp theo mức tiêu thụ bộ nhớ.
- Nhấn i để sắp xếp theo các thao tác I/O đĩa.
3. Kiểm tra trực tiếp các giới hạn Kernel đã áp dụng
Truy vấn hệ thống tệp ảo cgroup để kiểm tra các giá trị thô do kernel áp dụng:
# Xem dung lượng bộ nhớ đang sử dụng tính bằng byte
cat /sys/fs/cgroup/workload-workers.slice/memory.current
# Xem giới hạn bộ nhớ cứng đã cấu hình (tính bằng byte hoặc 'max')
cat /sys/fs/cgroup/workload-workers.slice/memory.max
# Xem hạn ngạch băng thông CPU (quota và chu kỳ tính bằng microsecond)
cat /sys/fs/cgroup/workload-workers.slice/cpu.max
4. Kiểm thử áp lực giới hạn tài nguyên (Stress Testing)
Mô phỏng áp lực bộ nhớ bên trong slice bằng stress-ng để xác minh cơ chế thực thi hoạt động đúng như mong đợi:
systemd-run --slice=workload-workers.slice --unit=stress-test \
--wait stress-ng --vm 1 --vm-bytes 3G --timeout 20s
Kiểm tra nhật ký kernel bằng journalctl -u stress-test.service hoặc dmesg -T. Bạn sẽ thấy kernel bóp dung lượng phân bổ bộ nhớ ở mức 2 GB (MemoryHigh) và kích hoạt OOM kill độc lập tại 2.5 GB (MemoryMax)—giúp các dịch vụ cơ sở dữ liệu, web và SSH hoàn toàn không bị ảnh hưởng.

