使用 systemd 的 cgroups v2 資源限制
11 分鐘閱讀 - 2026年6月3日

使用 cgroups v2 和 systemd 設定 CPU、記憶體和 I/O 限制。多租戶 Linux 主機的實用配置,搭配 PSI 監控與分片隔離。
使用 systemd 設定 cgroups v2 資源限制
cgroups v2 是 Linux 核心的統一資源控制框架。 它以單一樹狀結構取代了分散的 v1 層級結構,能一致地處理 CPU、記憶體和 I/O,並為 Docker、Kubernetes 和 systemd 中的容器隔離提供基礎。本文將說明如何啟用 cgroups v2、透過 systemd 設定限制,並將其應用於實際的多租戶主機情境。
啟用 cgroups v2
現代發行版預設已啟用 cgroups v2:Ubuntu 21.10 以上、Debian 11 以上、Fedora 31 以上,以及 RHEL/Rocky 9 以上。較舊的系統可能會運行混合層級結構,或仍預設為 v1。請透過以下指令確認:
stat -fc %T /sys/fs/cgroup/
執行 cgroup2fs 的輸出結果可確認 v2 是否已啟用。 tmpfs 通常表示仍為 v1。
若要將混合系統切換為純 v2,請編輯 /etc/default/grub 並在 GRUB_CMDLINE_LINUX_DEFAULT:
systemd.unified_cgroup_hierarchy=1 cgroup_no_v1=all
接著重新生成 GRUB 並重新開機:
sudo update-grub
sudo reboot
在生產環境中,請運行 5.2 版或更新版本的內核,以便為 v2 啟用 cgroup 凍結機制,並使用 systemd 244+ 版本以獲得完整的 cpuset 權限委派。在 Rocky Linux 8 和 RHEL 8 上,您可能還需要透過在 /etc/systemd/system.conf:
DefaultCPUAccounting=yes
DefaultMemoryAccounting=yes
DefaultIOAccounting=yes
使用 sudo systemctl daemon-reexec。重新啟動後,請確認哪些控制器可用:
cat /sys/fs/cgroup/cgroup.controllers
您應會看到 cpu, memory, io,以及 pids 已列出。這些控制器預設未對子 cgroups 啟用。若要啟用它們,請寫入根子樹控制檔:
echo "+cpu +memory +io" | sudo tee /sys/fs/cgroup/cgroup.subtree_control
若要深入了解 v2 與 v1 在內部運作上的差異,Michael Kerrisk 在 NDC TechTown 的演講是最佳的單一參考資源:
systemd 如何組織 cgroups
systemd 會為其啟動的每個服務建立一個 cgroup,並以其單元(unit)命名。 nginx.service 獲取 /sys/fs/cgroup/system.slice/nginx.service/,且其所產生的每個進程皆位於該 cgroup 內。三種單元類型直接對應於此層級結構:
| 單元類型 | 角色 | 說明 |
|---|---|---|
.slice | 內部節點 | 彙整相關服務並定義共用限制 |
.service | 終端節點 | 管理由 systemd 啟動的程序 |
.scope | 葉節點 | 追蹤外部啟動的程序(容器有效載荷、登入會話) |
出廠預設提供四個區段: -.slice (root)、 system.slice, user.slice,以及 machine.slice。對切片施加的任何限制將自動套用至其中的每個服務。
有一條 v2 規則值得記住:進程只能存在於葉節點中。包含子 cgroup 的 cgroup 無法直接託管進程,這也是 systemd 從不將服務放置於切片主幹中的原因。
請務必透過 systemd 設定限制,而非直接寫入 /sys/fs/cgroup/ 。手動寫入不會在重新開機後保留,且會與 systemd 對層級結構的專屬所有權產生衝突。請使用 systemctl set-property 進行一次性變更,並使用 unit drop-ins (systemctl edit nginx.service) 來進行永久性變更。
CPU 限制
cgroups v2 提供兩種 CPU 控制機制:硬性上限(cpu.max,在 systemd 中以 CPUQuota 在 systemd 中)以及比例權重(cpu.weight / CPUWeight).
CPUQuota 是絕對上限。 CPUQuota=50% 允許佔用半個核心; CPUQuota=200% 允許佔用相當於兩個完整核心的處理時間。若服務嘗試超過此限,無論 CPU 其餘部分有多閒置,都會被限流。
CPUWeight 僅在資源競爭時才生效。範圍為 1 至 10,000,預設值為 100。當三項服務的權重分別為 150、100 和 50,且它們同時爭取 CPU 時間時,將分別獲得約 50%、33% 和 17% 的 CPU 時間。 當 CPU 處於閒置狀態時,權重不會對任何操作造成限制。
對於對延遲敏感的工作負載,可使用 AllowedCPUs=。這能減少上下文切換,並保持各核心的快取處於熱狀態:
[Service]
CPUQuota=200%
CPUWeight=150
AllowedCPUs=0-3
當您需要可預測的成本(例如多租戶計費、隔離「吵鬧鄰居」)時,請使用硬配額。當您希望最大化硬體利用率,且僅在負載尖峰期間需要優先順序排序時,請使用權重。
cgroups v2 的記憶體限制
記憶體分為兩層: memory.high (軟限制,限流) 以及 memory.max (硬性限制,OOM)。關於交換區、頁面回收及核心 OOM 殺手(OOM killer)的背景資訊,請參閱我們關於 Linux 記憶體管理的相關文章。
設定 memory.high 約比 memory.max。一旦 memory.high ,這通常能讓工作負載在 OOM 殺手觸發前恢復。若使用率達到 memory.max,核心將終止 cgroup 中的程序。
典型設定:
[Service]
MemoryHigh=400M
MemoryMax=512M
MemorySwapMax=0
MemorySwapMax=0 為此 cgroup 停用交換空間。對於對延遲敏感的工作負載(如資料庫、即時串流),此舉值得採用,因為交換 I/O 會嚴重拖垮尾端延遲。
對於若遺留孤立子進程會導致共享狀態損毀的工作者群組,請將 1 寫入 cgroup 的 memory.oom.group 檔案中寫入此設定。當某個程序因 OOM 被終止時,核心會一併終止該 cgroup 中的所有程序。
請查看 memory.events 以查看某項服務被限流或因記憶體不足而遭終止的頻率:
cat /sys/fs/cgroup/system.slice/nginx.service/memory.events
該 high 和 oom_kill 計數器可告知您限制設定是否恰當。若數值持續不為零,則表示工作負載需要更多餘裕空間。
I/O 限制
I/O 控制器採用相同的雙模式設計:透過 io.max 設定絕對上限,以及透過 io.weight.
限制是針對每個區塊裝置,由 major:minor 編號識別。可透過 lsblk -o NAME,MAJ:MIN。典型的 systemd 配置:
[Service]
IOReadBandwidthMax=/dev/sda 50M
IOWriteBandwidthMax=/dev/sda 30M
IOReadIOPSMax=/dev/sda 1000
IOWriteIOPSMax=/dev/sda 500
io.weight 運作方式如下 cpu.weight:範圍為 1 至 10,000,預設值為 100。若將 500 分配給對外服務,並將 50 分配給夜間備份,即可避免在尖峰時段因備份佔用過多資源而導致磁碟飽和,同時在無其他需求時仍能讓備份使用全額頻寬。
I/O 限制僅在針對正確的裝置時生效。核心會根據區塊裝置追蹤 I/O,因此對 /dev/sda 對流向 /dev/nvme0n1的 I/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 可限制總進程與執行緒數量,防止單一租戶的 fork 炸彈導致主機當機。將租戶服務放入此切片中(透過 Slice=tenant-a.slice 在單元檔案中)將租戶服務放入此切片,它們便會自動繼承所有設定。
此模式亦適用於將嘈雜的背景作業與面向用戶的服務分離。將備份、日誌輪替及批次工作置於 background.slice 具有低 CPUWeight 且 io.weight 的切片中。當系統閒置時,這些任務將獲得充足資源;而當生產流量湧入時,它們則會自動讓出資源。
對於 Docker 和 Podman 等容器執行環境,請在 Delegate=yes 至其 systemd 單元檔案中。此舉可讓它們在無需 root 權限的情況下管理自身的子 cgroup,且設定於父切片上的限制仍適用於其下的所有元件。
使用 systemd-cgtop 和 PSI 進行監控
若要查看各 cgroup 的 CPU、記憶體及 I/O 即時 top 風格視圖,請執行:
systemd-cgtop
若要查看靜態層級結構及各進程所在位置,請使用 systemd-cgls.
v2 版本中對生產環境監控最具價值的功能,莫過於壓力滯留資訊(PSI)。PSI 會報告 cgroup 內任務因等待資源而陷入滯留狀態的時間百分比,並透過每個 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
CPU 使用率為 100% 且壓力為 0% 即表示系統運作正常。每個需要 CPU 的任務都能獲得處理資源。若同一 CPU 使用率為 80% 但壓力達 30%,則表示任務正在排隊等待執行時間。應針對 PSI 進行警示,而非僅關注使用率:它能偵測到使用率指標完全忽略的資源爭用狀況。
無需重啟即可即時調整限制:
sudo systemctl set-property tenant-a.slice MemoryMax=6144M
變更立即生效,並在重新開機後持續生效。結合基於 PSI 的警示機制,這讓您能在負載變化演變成 OOM 強制終止或延遲失控之前,及時做出回應。
若您正在執行高密度多租戶工作負載,且需要具備充足餘裕的主機以流暢地實施這些政策,我們的專用伺服器正是為此而打造。
Linux 記憶體管理:交換、OOM 殺手與 Cgroups
12 分鐘閱讀 - 2026年5月31日