giới hạn tài nguyên cgroups v2 với systemd

11 phút đọc - 3 tháng 6, 2026

hero section cover
Mục lục
  • Giới hạn tài nguyên cgroups v2 với systemd
  • Kích hoạt cgroups v2
  • Cách systemd tổ chức các cgroup
  • Giới hạn CPU
  • Giới hạn bộ nhớ với cgroups v2
  • Giới hạn I/O
  • Cách ly đa người thuê với các phân vùng
  • Giám sát bằng systemd-cgtop và PSI
Chia sẻ

Đặt giới hạn CPU, bộ nhớ và I/O bằng cgroups v2 và systemd. Cấu hình thực tế cho các máy chủ Linux đa người dùng, với giám sát PSI và cách ly slice.

Giới hạn tài nguyên cgroups v2 với systemd

cgroups v2 là khung kiểm soát tài nguyên thống nhất của nhân Linux. Nó thay thế hệ thống phân cấp v1 rời rạc bằng một cây duy nhất xử lý CPU, bộ nhớ và I/O một cách nhất quán, đồng thời hỗ trợ việc cách ly container trong Docker, Kubernetes và systemd. Bài viết này trình bày cách kích hoạt cgroups v2, thiết lập giới hạn thông qua systemd và áp dụng nó vào các tình huống lưu trữ đa người dùng thực tế.

Kích hoạt cgroups v2

Các bản phân phối hiện đại được cài đặt sẵn cgroups v2 theo mặc định: Ubuntu 21.10+, Debian 11+, Fedora 31+ và RHEL/Rocky 9+. Các hệ thống cũ hơn có thể chạy một hệ thống phân cấp hỗn hợp hoặc vẫn mặc định là v1. Kiểm tra bằng:

stat -fc %T /sys/fs/cgroup/

Kết quả của cgroup2fs xác nhận v2 đang hoạt động. tmpfs thường có nghĩa là v1.

Để chuyển hệ thống hỗn hợp sang v2 thuần túy, hãy chỉnh sửa /etc/default/grub và thêm đoạn sau vào GRUB_CMDLINE_LINUX_DEFAULT:

systemd.unified_cgroup_hierarchy=1 cgroup_no_v1=all

Sau đó, tạo lại GRUB và khởi động lại:

sudo update-grub
sudo reboot

Đối với môi trường sản xuất, hãy chạy kernel 5.2 trở lên để có được cgroup freezer cho v2, và systemd 244+ để có cpuset . Trên Rocky Linux 8 và RHEL 8, bạn cũng có thể cần kích hoạt tính năng kế toán một cách rõ ràng bằng cách thêm các dòng này vào /etc/systemd/system.conf:

DefaultCPUAccounting=yes
DefaultMemoryAccounting=yes
DefaultIOAccounting=yes

Tải lại với sudo systemctl daemon-reexec. Sau khi khởi động lại, hãy kiểm tra xem có những bộ điều khiển nào khả dụng:

cat /sys/fs/cgroup/cgroup.controllers

Bạn sẽ thấy cpu, memory, io, và pids được liệt kê. Các bộ điều khiển này không được bật cho các cgroup con theo mặc định. Để kích hoạt chúng, hãy ghi vào tệp điều khiển cây con gốc:

echo "+cpu +memory +io" | sudo tee /sys/fs/cgroup/cgroup.subtree_control

Để có cái nhìn tổng quan chi tiết về sự khác biệt giữa v2 và v1 ở cấp độ nội bộ, bài thuyết trình NDC TechTown của Michael Kerrisk là tài liệu tham khảo tốt nhất:

Cách systemd tổ chức các cgroup

systemd tạo một cgroup cho mỗi dịch vụ mà nó khởi động, được đặt tên theo đơn vị. nginx.service được /sys/fs/cgroup/system.slice/nginx.service/, và mọi tiến trình mà nó tạo ra đều tồn tại bên trong cgroup đó. Ba loại đơn vị được ánh xạ trực tiếp vào hệ thống phân cấp:

Loại đơn vịVai tròMô tả
.sliceNút bên trongNhóm các dịch vụ liên quan và xác định các giới hạn chung
.serviceNút cuốiQuản lý các quy trình được khởi chạy bởi systemd
.scopeNút láTheo dõi các quy trình được khởi chạy từ bên ngoài (tải trọng container, phiên đăng nhập)

Bốn phân vùng mặc định được cung cấp sẵn: -.slice (root), system.slice, user.slice, và machine.slice. Bất kỳ giới hạn nào được áp dụng cho một slice sẽ tự động áp dụng cho mọi dịch vụ trong đó.

Một quy tắc v2 đáng nhớ: các tiến trình chỉ có thể tồn tại trong các nút lá. Một cgroup có các cgroup con không thể trực tiếp lưu trữ các tiến trình, đó là lý do tại sao systemd không bao giờ đặt các dịch vụ vào thân của một slice.

Luôn đặt giới hạn thông qua systemd thay vì ghi trực tiếp vào /sys/fs/cgroup/ trực tiếp. Các thao tác ghi thủ công không tồn tại sau khi khởi động lại và xung đột với quyền sở hữu độc quyền của systemd đối với hệ thống phân cấp. Sử dụng systemctl set-property cho các thay đổi một lần và các đơn vị thay thế (systemctl edit nginx.service) cho các thay đổi vĩnh viễn.

Giới hạn CPU

cgroups v2 cung cấp cho bạn hai tùy chọn kiểm soát CPU: giới hạn cứng (cpu.max, được hiển thị dưới dạng CPUQuota trong systemd) và trọng số tỷ lệ (cpu.weight / CPUWeight).

CPUQuota là giới hạn tuyệt đối. CPUQuota=50% cho phép sử dụng một nửa lõi; CPUQuota=200% cho phép thời gian tương đương hai nhân đầy đủ. Dịch vụ sẽ bị giới hạn nếu cố gắng vượt quá mức này, bất kể phần còn lại của CPU đang nhàn rỗi đến mức nào.

CPUWeight chỉ có ý nghĩa khi có xung đột. Phạm vi là từ 1 đến 10.000, mặc định là 100. Ba dịch vụ có trọng số lần lượt là 150, 100 và 50 sẽ nhận được khoảng 50%, 33% và 17% thời gian CPU khi tất cả đều yêu cầu cùng lúc. Khi CPU đang nhàn rỗi, trọng số không hạn chế bất cứ điều gì.

Đối với các tác vụ nhạy cảm với độ trễ, hãy gán các tiến trình vào các lõi cụ thể bằng AllowedCPUs=. Điều này giúp giảm chuyển đổi ngữ cảnh và giữ bộ nhớ cache trên mỗi lõi luôn hoạt động:

[Service]
CPUQuota=200%
CPUWeight=150
AllowedCPUs=0-3

Sử dụng hạn ngạch cứng khi bạn cần chi phí có thể dự đoán được (thanh toán cho nhiều người thuê, cách ly hàng xóm ồn ào). Sử dụng trọng số khi bạn muốn sử dụng phần cứng tối đa và chỉ cần sắp xếp thứ tự ưu tiên trong các đợt tăng đột biến.

Giới hạn bộ nhớ với cgroups v2

Bộ nhớ có hai cấp độ: memory.high (mềm, giới hạn tốc độ) và memory.max (cứng, OOM). Để biết thông tin cơ bản về swap, thu hồi trang và trình giết OOM của kernel, hãy xem bài viết liên quan của chúng tôi về quản lý bộ nhớ Linux.

Đặt memory.high khoảng 10 đến 20% dưới memory.max. Kernel sẽ bắt đầu thu hồi trang và giới hạn phân bổ khi memory.high giá trị này bị vượt qua, điều này thường cho phép khối lượng công việc phục hồi trước khi trình giết OOM kích hoạt. Nếu mức sử dụng đạt memory.max, kernel sẽ giết các tiến trình trong cgroup.

Một cấu hình điển hình:

[Service]
MemoryHigh=400M
MemoryMax=512M
MemorySwapMax=0

MemorySwapMax=0 vô hiệu hóa swap cho cgroup này. Điều này đáng làm đối với các khối lượng công việc nhạy cảm với độ trễ (cơ sở dữ liệu, truyền phát thời gian thực) nơi I/O swap có thể làm tăng độ trễ cuối.

Đối với các nhóm công nhân (worker pools) mà việc bỏ lại các tiến trình mồ côi có thể làm hỏng trạng thái chia sẻ, hãy ghi 1 vào tệp memory.oom.group . Khi một tiến trình bị giết do OOM, nhân hệ thống sẽ giết tất cả các tiến trình trong cgroup cùng lúc.

Kiểm tra memory.events để xem tần suất một dịch vụ bị giới hạn hoặc bị giết do OOM:

cat /sys/fs/cgroup/system.slice/nginx.service/memory.events

Tệp highoom_kill cho bạn biết liệu các giới hạn của bạn có được thiết lập đúng mức hay không. Các giá trị không bằng không liên tục có nghĩa là khối lượng công việc cần thêm dung lượng dự phòng.

Giới hạn I/O

Bộ điều khiển I/O có cùng thiết kế hai chế độ: giới hạn tuyệt đối thông qua io.max và chia sẻ tỷ lệ thông qua io.weight.

Giới hạn được áp dụng cho từng thiết bị khối, được xác định bằng các số major:minor. Tìm chúng bằng lsblk -o NAME,MAJ:MIN. Một cấu hình systemd điển hình:

[Service]
IOReadBandwidthMax=/dev/sda 50M
IOWriteBandwidthMax=/dev/sda 30M
IOReadIOPSMax=/dev/sda 1000
IOWriteIOPSMax=/dev/sda 500

io.weight hoạt động như cpu.weight: phạm vi từ 1 đến 10.000, mặc định là 100. Gán giá trị 500 cho dịch vụ hướng đến khách hàng và 50 cho sao lưu hàng đêm giúp ngăn chặn việc sao lưu làm tắc nghẽn đĩa trong giờ cao điểm, nhưng vẫn cho phép nó sử dụng toàn bộ băng thông khi không có tác vụ nào khác cần đến.

Giới hạn I/O chỉ áp dụng khi bạn nhắm vào thiết bị đúng. Hệ điều hành theo dõi I/O theo thiết bị khối, vì vậy giới hạn trên /dev/sda không có tác dụng đối với I/O đi đến /dev/nvme0n1. Trên các máy chủ có nhiều đĩa, hãy đặt giới hạn cho từng thiết bị.

Cách ly đa người thuê với các phân vùng

Đối với môi trường chia sẻ, hãy định nghĩa một slice cho mỗi tenant. Tạo /etc/systemd/system/tenant-a.slice:

[Slice]
CPUQuota=200%
CPUWeight=150
MemoryHigh=3584M
MemoryMax=4096M
MemorySwapMax=0
IOReadBandwidthMax=/dev/sda 200M
TasksMax=512

TasksMax=512 giới hạn tổng số quy trình và luồng, điều này ngăn chặn một cuộc tấn công fork bomb từ một khách hàng làm sập máy chủ. Đưa các dịch vụ của khách hàng vào slice này (thông qua Slice=tenant-a.slice trong các tệp đơn vị của họ) và chúng sẽ tự động kế thừa mọi thứ.

Mô hình này cũng hiệu quả trong việc tách biệt các tác vụ nền gây nhiễu khỏi các dịch vụ hướng người dùng. Đặt các tác vụ sao lưu, xoay vòng nhật ký và xử lý hàng loạt vào một background.sliceCPUWeightio.weight . Chúng sẽ nhận được toàn bộ tài nguyên khi hệ thống nhàn rỗi và nhường chỗ khi lưu lượng sản xuất đến.

Đối với các môi trường chạy container như DockerPodman, hãy thêm Delegate=yes vào các tệp đơn vị systemd của chúng. Điều này cho phép chúng quản lý các sub-cgroup riêng mà không cần quyền root, và các giới hạn được đặt trên phân vùng cha vẫn áp dụng cho mọi thứ bên dưới.

Giám sát bằng systemd-cgtop và PSI

Để xem trực tiếp kiểu top về CPU, bộ nhớ và I/O theo từng cgroup, hãy chạy:

systemd-cgtop

Đối với hệ thống phân cấp tĩnh và vị trí của các tiến trình, hãy sử dụng systemd-cgls.

Tính năng hữu ích nhất của phiên bản v2 cho giám sát sản xuất là Thông tin Ứng suất (PSI). PSI báo cáo tỷ lệ phần trăm thời gian các tác vụ trong một cgroup bị đình trệ do chờ tài nguyên, được hiển thị trong ba tệp cho mỗi cgroup:

cat /sys/fs/cgroup/tenant-a.slice/cpu.pressure
cat /sys/fs/cgroup/tenant-a.slice/memory.pressure
cat /sys/fs/cgroup/tenant-a.slice/io.pressure

Một CPU có mức sử dụng 100% với áp lực 0% là hoạt động tốt. Mọi tác vụ cần CPU đều được đáp ứng. Cùng một CPU có mức sử dụng 80% nhưng áp lực 30% có nghĩa là các tác vụ đang xếp hàng chờ thời gian chạy. Cảnh báo về PSI, không phải về mức sử dụng: nó phát hiện sự tranh chấp mà các chỉ số mức sử dụng hoàn toàn bỏ sót.

Điều chỉnh giới hạn trực tiếp mà không cần khởi động lại bất kỳ thứ gì:

sudo systemctl set-property tenant-a.slice MemoryMax=6144M

Thay đổi được áp dụng ngay lập tức và duy trì sau khi khởi động lại. Kết hợp với cảnh báo dựa trên PSI, điều này cho phép bạn phản ứng với sự thay đổi tải trước khi chúng dẫn đến việc giết tiến trình do hết bộ nhớ (OOM) hoặc độ trễ tăng vọt.

Nếu bạn đang chạy các khối lượng công việc đa người thuê có mật độ cao và cần một máy chủ có dung lượng trống để áp dụng các chính sách này một cách trơn tru, các máy chủ chuyên dụng của chúng tôi được thiết kế dành riêng cho mục đích đó.

Blog

Nổi bật trong tuần

Các bài viết khác
Tại sao việc sở hữu một VPS mạnh mẽ và không giới hạn băng thông lại quan trọng

Tại sao việc sở hữu một VPS mạnh mẽ và không giới hạn băng thông lại quan trọng

Một VPS không giới hạn băng thông cung cấp băng thông theo gói cố định với tốc độ cổng cố định. Sự khác biệt so với các gói tính theo lưu lượng, khi nào nó mang lại lợi ích và những điều cần kiểm tra trước khi mua.

7 phút đọc - 9 tháng 5, 2025

Quản lý bộ nhớ Linux: Swap, OOM Killer & Cgroups

12 phút đọc - 31 tháng 5, 2026

Các bài viết khác
background image

Bạn có thắc mắc hoặc cần giải pháp tùy chỉnh?

icon

Các tùy chọn linh hoạt

icon

Phạm vi toàn cầu

icon

Triển khai ngay lập tức

icon

Các tùy chọn linh hoạt

icon

Phạm vi toàn cầu

icon

Triển khai ngay lập tức