18.2 控制組
控制組 (Cgroups) 是 Linux 容器機制的另外一個關鍵元件。如果說命名空間 (Namespace) 決定了容器能 看到 什麼,那麼控制組就決定了容器能 使用 多少資源。
在安全領域中,資源的不可用性本身就是一種安全威脅。控制組負責實現資源的審計和限制,這對於抵禦資源耗盡型攻擊(如拒絕服務攻擊 DoS)至關重要。
18.2.1 為什麼資源限制關乎安全?
預設情況下,Docker 容器對系統資源的使用是沒有限制的:一個容器理論上可以使用宿主機所有的 CPU 計算能力、吃光所有的記憶體、耗盡所有的系統 PID。
想象一下以下場景:
- 一個惡意使用者向你暴露在公網的應用發起海量併發請求。
- 應用程式邏輯中存在記憶體洩漏漏洞。
- 駭客在入侵容器後,在裡面執行了挖礦木馬程式。
如果沒有 Cgroups 的限制,某個容器內的異常行為(或惡意攻擊)將會榨乾宿主機的資源,導致宿主機上其他健康的容器甚至 Docker 守護程序自身因為 OOM(Out Of Memory)崩潰或 CPU 飢餓而停止回應。
18.2.2 核心資源限制實戰
為了確保多租戶平台(如公有或私有的 PaaS 平台)的穩定性,或者在生產環境防止服務級聯故障,我們要養成在啟動容器時 顯式宣告資源上限 的習慣。
1. 記憶體限制
限制記憶體可以防止應用程式因記憶體洩漏或惡意載荷導致宿主機 OOM。
關鍵引數:
-m, --memory="":硬限制,容器可使用的最大記憶體量。--memory-swap="":限制容器可使用的記憶體與 Swap 總量。
實戰範例:
限制容器最多只能使用 512MB 記憶體,並且停用 Swap(將 memory 和 memory-swap 設定成一樣的值即可):
$ docker run -d \
--name web_app \
--memory="512m" \
--memory-swap="512m" \
nginx:alpine
如果該容器內的應用嘗試分配超過 512MB 的記憶體,該程序將會被核心的 OOM Killer 殺掉,但絕不會波及到宿主機的其他部分。
2. CPU 限制
限制 CPU 可以防止個別計算密集型的容器壟斷 CPU 時間片,保證系統的排程公平性。
關鍵引數:
--cpus=<value>:指定容器可以使用的 CPU 核心數量(可以是小數)。-c, --cpu-shares=0:軟限制,設定容器使用 CPU 的相對權重(預設是 1024)。
實戰範例:
限制容器最多使用 1.5 個 CPU 核心的算力:
$ docker run -d \
--name worker_app \
--cpus="1.5" \
busybox \
md5sum /dev/urandom
即使上面的指令是一個死迴圈的雜湊計算程序,容器也永遠無法吃滿雙核 CPU 系統的全部算力。
3. 程序數限制
程序炸彈(Fork Bomb)是一種典型的拒絕服務攻擊方式,它透過不斷 fork() 新進程來耗盡系統的程序表條目,導致系統無法建立任何新任務。
關鍵引數:
--pids-limit=<number>:限制容器內允許建立的最大程序數。
實戰範例:
一個常規的 Web 服務程序數通常在幾十到上百之間。我們可以設定一個合理的上限來防範 Fork 炸彈:
$ docker run -d \
--name app_service \
--pids-limit=100 \
python:alpine python app.py
當容器內的程序總數達到 100 時,任何嘗試派生新進程的操作都會失敗並回傳 Resource temporarily unavailable,從而挫敗相關的攻擊行為。
18.2.3 最佳實踐建議
在生產環境中,不僅要在單機使用 Docker 指令時設定這些引數,更應當在叢集編排工具中將資源配額制度化。
例如,在 Kubernetes 中,強烈建議為每個 Pod 設定 requests 和 limits:
resources:
requests:
memory: "256Mi"
cpu: "250m"
limits:
memory: "512Mi"
cpu: "500m"
透過 Cgroups 的資源邊界控制,你可以從根本上切斷一條導致整個系統雪崩的脆弱鏈路。這也進一步使得 Docker 以及容器技術成為了現代高可用服務的基礎設施首選。