Docker 資源限制與效能優化
預設情況下,Docker 容器並不會限制其對硬體資源的胃口。這意味著如果一個容器內的程式發生 Bug(例如:無窮迴圈或記憶體洩漏),它可能會耗盡主機所有的 CPU 和記憶體,導致整個系統甚至其他容器崩潰。
本章將教你如何設定「護欄」,保護你的主機安全並優化執行效能。
記憶體限制 (Memory Constraints)
記憶體限制是最重要的維運設定。如果容器嘗試使用的記憶體超過限制,它會被作業系統的 OOM Killer (Out Of Memory Killer) 殺死。
常用設定參數
- 硬性限制 (
--memory):容器絕對不能超過的上限。 - 軟性限制 (
--memory-reservation):當系統記憶體吃緊時,強迫容器回到該水位,但平常可以使用更多。 - Swap 限制 (
--memory-swap):限制記憶體加上虛擬記憶體的總量。
Docker Compose 範例:
deploy:
resources:
limits:
memory: 512M
reservations:
memory: 128M
進階:OOM 管理
--oom-kill-disable:禁止 OOM Killer 殺死該容器(需搭配--memory使用,且不建議隨意開啟,否則可能導致主機當機)。--oom-score-adj:調整容器被殺掉的優先權。數值越低(如 -500),容器就越不容易被選為犧牲品。
CPU 限制 (CPU Constraints)
Docker 讓容器能共享所有 CPU 週期。在多核系統上,你可以精確指定資源。
--cpus=".5":限制只能使用 0.5 顆 CPU 核心。--cpuset-cpus="0,1":限制運算只能在特定的實體 CPU 核心上執行。
Docker Compose 範例:
services:
web:
image: nginx
deploy:
resources:
limits:
cpus: '0.50'
# cpuset: '0,1' # 需 Compose 3.6+
GPU 資源分配
如果你正在處理 AI 模型或影片轉檔,你會需要將主機的 GPU 分配給容器。這需要配合 NVIDIA Container Toolkit。
# 使用所有 GPU
docker run --gpus all nvidia/cuda:11.0-base nvidia-smi
# 僅分配特定的 GPU
docker run --gpus '"device=0"' my-ai-app
Docker Compose 範例:
services:
ai-app:
image: my-ai-app
deploy:
resources:
reservations:
devices:
- driver: nvidia
count: all # 或輸出特定數量,如 1
capabilities: [gpu]
磁碟 I/O 與進程限制
磁碟 I/O (Block I/O) 限制
防止單一容器的大量讀寫影響到主機硬碟效能:
--device-read-bps:限制每秒讀取的資料量(例如:--device-read-bps /dev/sda:1mb)。--device-write-bps:限制每秒寫入的資料量。
Docker Compose 範例:
services:
db:
image: postgres
blkio_config:
device_read_bps:
- path: /dev/sda
rate: 1mb
device_write_bps:
- path: /dev/sda
rate: 1mb
進程數量限制 (PIDs Limit)
這是為了防止 Fork Bomb(一種惡意程式,會不斷建立新進程直到系統資源枯竭)。
# 限制容器內最多只能有 100 個進程
docker run --pids-limit 100 my-app
Docker Compose 範例:
services:
app:
image: my-app
pids_limit: 100
效能優化最佳實踐
Ulimits 調整
如果你在大流量場景下運行(如 Nginx 或資料庫),可能會遇到 too many open files 錯誤。你可以調整 Linux 的系統限制:
# docker-compose.yaml
ulimits:
nproc: 65535
nofile:
soft: 20000
hard: 40000
使用輕量化映像檔
使用 alpine 或 distroless 版本。檔案越小,啟動速度越快,受攻擊面也越小。
容器唯讀模式 (Read-only)
如果不需要寫入檔案到容器層,開啟唯讀模式可以提升安全性並降低磁碟寫入負擔:
docker run --read-only nginx
故障排除:我的容器被殺掉了?
當容器狀態顯示為 Exited (137) 時,通常代表發生了 OOM (Out Of Memory)。
你可以透過以下指令確認:
docker inspect [ID] --format='{{.State.OOMKilled}}'
如果回傳為 true,請檢查應用程式是否有記憶體洩漏,或者適度調高 memory 限制。
總結
- 環境分開看待:開發環境可以設寬鬆一些,但生產環境必須明確標註
limits。 - 保護主機:利用
pids-limit與blkio參數防止資源被單一漏洞程式佔滿。 - 效能是調出來的:先用
docker stats觀察正常負載下的數值,再回過頭來設定合理的保護區間。