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

使用輕量化映像檔

使用 alpinedistroless 版本。檔案越小,啟動速度越快,受攻擊面也越小。

容器唯讀模式 (Read-only)

如果不需要寫入檔案到容器層,開啟唯讀模式可以提升安全性並降低磁碟寫入負擔:

docker run --read-only nginx

故障排除:我的容器被殺掉了?

當容器狀態顯示為 Exited (137) 時,通常代表發生了 OOM (Out Of Memory)

你可以透過以下指令確認:

docker inspect [ID] --format='{{.State.OOMKilled}}'

如果回傳為 true,請檢查應用程式是否有記憶體洩漏,或者適度調高 memory 限制。

總結

  1. 環境分開看待:開發環境可以設寬鬆一些,但生產環境必須明確標註 limits
  2. 保護主機:利用 pids-limitblkio 參數防止資源被單一漏洞程式佔滿。
  3. 效能是調出來的:先用 docker stats 觀察正常負載下的數值,再回過頭來設定合理的保護區間。