如何用 cgroups 限制程序資源
在現代 Linux 系統中,資源隔離與控制是確保系統穩定性的關鍵。當多個服務同時運行時,某個 CPU 密集型或記憶體洩漏的程序可能會拖垮整個系統。雖然 nice 指令可以調整優先級,但它無法設定硬性上限。這時,cgroups(Control Groups) 就派上用場了。它允許系統管理員精確限制特定程序組能使用的 CPU、記憶體等資源。
對於中階 Linux 使用者而言,理解並手動操作 cgroups v2 是提升系統調控能力的必經之路。本文將以 Ubuntu 22.04 和 Debian 12 為例,示範如何透過原生指令限制程序資源。
前置準備:確認 cgroups 版本
現代 Linux 發行版(如 Ubuntu 22.04、Debian 12)預設使用 cgroups v2(統一Hierarchy)。請先確認您的系統是否已啟用:
mount | grep cgroup2
若輸出包含 type cgroup2,表示 cgroups v2 已啟用。若未啟用,請在開機參數中加入 systemd.unified_cgroup_hierarchy=1。以下教學皆基於 cgroups v2 環境。
步驟一:建立資源控制目錄
在 cgroups v2 中,所有資源控制器(如 cpu、memory)都掛載在 /sys/fs/cgroup/ 下。我們需要建立一個自定義的目錄來作為資源控制的容器。
假設我們要限制一個名為 myapp 的程序:
# 建立控制群組目錄
sudo mkdir -p /sys/fs/cgroup/myapp
# 確認目錄已建立
ls /sys/fs/cgroup/myapp/
步驟二:設定記憶體限制
我們來限制 myapp 組的最大記憶體使用量為 512MB。在 cgroups v2 中,記憶體控制器通常預設啟用,但有時需手動開啟。
# 啟用記憶體控制器(若尚未啟用)
echo 1 | sudo tee /sys/fs/cgroup/memory.max
# 設定記憶體上限為 512MB (512 * 1024 * 1024 = 536870912 bytes)
echo 536870912 | sudo tee /sys/fs/cgroup/myapp/memory.max
注意:
memory.max設定的是硬限制(Hard Limit)。若程序嘗試超過此數值,會觸發 OOM(Out Of Memory)殺死程序或導致寫入失敗。若需軟限制(Soft Limit),可設定memory.low。
步驟三:設定 CPU 限制
cgroups v2 使用 weight(權重)和 quota(配額)兩種方式控制 CPU。這裡我們示範使用 quota,直接指定 CPU 時間片。
假設我們希望 myapp 最多使用 50% 的單核 CPU 資源:
# 確保 CPU 控制器已啟用
echo 1 | sudo tee /sys/fs/cgroup/cpu.max
# 設定 CPU 配額:每 100ms 的時間片中,最多可使用 50ms
# 格式為 [quota] [period]
echo "50000 100000" | sudo tee /sys/fs/cgroup/myapp/cpu.max
這意味著 myapp 在任何 100ms 的時間窗口內,最多只能使用 50ms 的 CPU 時間。若有多核 CPU,這個限制是針對總和的,因此 50% 單核等於 0.5 個 vCPU。
步驟四:將程序加入控制群組
現在,我們需要將實際的程序 PID 加入這個控制群組。假設我們啟動了一個測試程序(例如 sleep 1000):
# 啟動測試程序並取得 PID
sleep 1000 &
TEST_PID=$!
echo "測試程序 PID: $TEST_PID"
將程序 PID 寫入 cgroup.procs 檔案,即可將其納入資源限制:
echo $TEST_PID | sudo tee /sys/fs/cgroup/myapp/cgroup.procs
重要提示:一旦程序被加入
cgroup.procs,它將立即受到限制。若程序已佔用過多資源,可能會立即被終止或降速。建議先建立好 cgroup 並設定好參數,再啟動程序,或將程序啟動指令寫入 shell script 中執行。
步驟五:驗證限制效果
我們可以透過 cgexec 或直接監控 /sys/fs/cgroup/myapp/ 下的統計檔案來驗證。
查看記憶體使用情況:
cat /sys/fs/cgroup/myapp/memory.current
查看 CPU 使用情況(需安裝 bpfcc 工具包或使用 top 觀察):
# 使用 top 並按 1 查看各核心,觀察該 PID 的 CPU 使用率是否受限
top -p $TEST_PID
若要移除限制,只需將程序從 cgroup.procs 移除,並刪除目錄:
# 移除程序(先確認程序仍在運行)
echo $TEST_PID | sudo tee /sys/fs/cgroup/myapp/cgroup.procs
# 刪除控制群組
sudo rmdir /sys/fs/cgroup/myapp
常見問題
-
為什麼設定
memory.max後程序沒有被殺死? cgroups v2 的記憶體控制與 Linux OOM Killer 互動複雜。預設情況下,當記憶體超過memory.max時,寫入操作會返回EDQUOT錯誤,但程序不一定會立即崩潰。若要強制在超過限制時終止程序,需設定memory.high作為軟限制,並搭配memory.max作為硬限制,或確保應用程式能正確處理記憶體分配錯誤。 -
cgroups v1 與 v2 的差異為何? v1 每個控制器(cpu、memory、io)有獨立 hierarchy,結構複雜且易衝突。v2 採用統一 hierarchy,所有控制器共享同一個目錄結構,配置更直觀。Ubuntu 22.04 和 Debian 12 已全面轉向 v2,舊版的
cgcreate、cgset等工具已棄用,建議直接使用sysfs檔案操作。
小結
透過 cgroups v2,我們可以精確地將程序資源限制在安全範圍內,避免單一服務拖垮整個系統。雖然手動操作 sysfs 檔案較為繁瑣,但它是理解容器技術(如 Docker、Kubernetes)底層原理的關鍵。對於生產環境,建議使用 systemd 的 MemoryLimit、CPUQuota 等參數,或 Docker 的 --memory、--cpus 選項來簡化管理,但掌握底層指令能讓您在除錯時更加得心應手。