跳轉到

任務八:維運與安全性基礎

開始之前

任務目標

在這個任務中,你將學習:

  • 使用資源限制控制容器的記憶體與 CPU 用量
  • 設定容器重啟策略以提升服務可靠性
  • 掌握日誌管理與查看技巧
  • 維護 Docker 環境整潔:清理未使用的資源
  • 診斷與處理異常狀態的容器
  • 建立安全性基礎觀念:非 root 執行、可信任映像檔、弱點掃描

資源限制:控制容器的資源使用

為何需要資源限制

在預設情況下,Docker 容器可以使用主機上所有可用的記憶體與 CPU。這可能導致:

  • 單一容器耗盡資源:失控的容器可能佔用所有記憶體,導致主機或其他容器無法運作
  • 系統不穩定:記憶體不足可能觸發 OOM Killer,強制終止程式
  • 效能下降:沒有 CPU 限制的容器可能影響其他服務的效能

透過設定資源限制,你可以:

  • 防止單一容器影響整個系統
  • 確保重要服務獲得足夠資源
  • 在有限資源下執行更多容器

限制記憶體用量

使用 --memory 參數限制容器可使用的記憶體:

# 限制記憶體為 512MB
docker run -d --name nginx-limited --memory 512m nginx:alpine

# 限制記憶體為 1GB
docker run -d --name nginx-1g --memory 1g nginx:alpine

常用單位

  • mM:MB(百萬位元組)
  • gG:GB(十億位元組)

當容器嘗試使用超過限制的記憶體時,會被 OOM Killer 終止。

限制 CPU 用量

使用 --cpus 參數限制容器可使用的 CPU 核心數:

# 限制為 0.5 個 CPU 核心(50% 的單核效能)
docker run -d --name nginx-half-cpu --cpus 0.5 nginx:alpine

# 限制為 2 個 CPU 核心
docker run -d --name nginx-2cpu --cpus 2 nginx:alpine

說明

  • --cpus 1:容器最多使用 1 個 CPU 核心的完整效能
  • --cpus 0.5:容器最多使用 0.5 個 CPU 核心的效能
  • --cpus 2:容器最多使用 2 個 CPU 核心的效能

與記憶體限制不同,CPU 限制不會終止容器,只會限制其運算速度。

查看容器資源使用狀況

使用 docker stats 即時監控容器的資源使用情況:

# 查看所有執行中容器的資源使用狀況
docker stats

# 查看特定容器的資源使用狀況
docker stats nginx-limited

輸出範例:

CONTAINER ID   NAME            CPU %     MEM USAGE / LIMIT   MEM %     NET I/O
a1b2c3d4e5f6   nginx-limited   0.01%     5.5MiB / 512MiB    1.07%     1.2kB / 0B

欄位說明

  • CPU %:CPU 使用百分比
  • MEM USAGE / LIMIT:記憶體使用量與限制
  • MEM %:記憶體使用百分比
  • NET I/O:網路輸入/輸出流量

Ctrl+C 退出即時監控。

容器重啟策略:提升服務可靠性

重啟策略對照表

Docker 提供四種重啟策略,用於控制容器在退出後是否自動重啟:

策略 說明 daemon 重啟後
no 不自動重啟(預設) 不啟動
on-failure 僅在非正常退出時重啟 不啟動
always 無論原因都重啟 自動啟動
unless-stopped always 類似,但手動停止的不會啟動 如果之前在執行則啟動

no:預設策略

docker run -d --name app-no-restart --restart no nginx:alpine

說明

  • 容器停止後不會自動重啟
  • 這是預設行為
  • 適合用於一次性任務或開發環境

on-failure:非正常退出時重啟

# 非正常退出時重啟
docker run -d --name app-on-failure --restart on-failure nginx:alpine

# 最多重試 3 次
docker run -d --name app-retry-3 --restart on-failure:3 nginx:alpine

說明

  • 只在容器以非 0 的退出碼結束時重啟
  • 可設定最大重試次數(如 on-failure:3
  • 適合用於可能暫時失敗的服務

適用情境

  • 資料處理任務:可能因暫時性錯誤失敗,重試後可能成功
  • 連接外部服務:初次啟動時外部服務可能尚未就緒

always:始終重啟

docker run -d --name app-always --restart always nginx:alpine

說明

  • 無論容器以何種原因退出,都會自動重啟
  • Docker daemon 重啟後,容器也會自動啟動
  • 容器會無限次重試

適用情境

  • 生產環境的核心服務(Web 應用、API 伺服器)
  • 需要持續運行的背景服務

unless-stopped:除非手動停止

docker run -d --name app-unless-stopped --restart unless-stopped nginx:alpine

說明

  • always 類似,但有一個關鍵差異:
    • 如果你手動執行 docker stop 停止容器,Docker daemon 重啟後不會自動啟動該容器
    • 如果容器是自己退出的(非手動停止),daemon 重啟後會自動啟動
  • 這讓你能明確控制哪些容器不該啟動

適用情境

  • 大多數生產服務(比 always 更靈活)
  • 需要臨時停止某些服務進行維護的場景

適用情境總結

  • 開發環境:使用 noon-failure,避免容器在背景不斷重啟
  • 測試環境:使用 on-failure,失敗時自動重試
  • 生產環境:優先使用 unless-stoppedalways,確保服務持續運行

日誌管理:追蹤容器輸出

預設日誌機制

Docker 使用 json-file 作為預設的 log driver。容器的標準輸出(stdout)與標準錯誤(stderr)會被儲存為 JSON 格式的日誌檔案,通常位於:

/var/lib/docker/containers/<container-id>/<container-id>-json.log

你可以透過 docker logs 指令查看這些日誌,無需直接存取檔案。

基本用法

# 查看容器的所有日誌
docker logs my-app

# 查看日誌並顯示時間戳記
docker logs -t my-app

持續追蹤日誌

使用 -f 參數可即時追蹤新產生的日誌,類似 tail -f

docker logs -f my-app

Ctrl+C 停止追蹤。

適用情境

  • 除錯應用程式
  • 監控服務運行狀態
  • 查看即時的請求或錯誤訊息

限制顯示行數

使用 --tail 參數只顯示最後 N 行日誌:

# 只顯示最後 50 行
docker logs --tail 50 my-app

# 顯示最後 100 行並持續追蹤
docker logs --tail 100 -f my-app

時間過濾

使用 --since 參數過濾指定時間之後的日誌:

# 顯示最近 10 分鐘的日誌
docker logs --since 10m my-app

# 顯示最近 1 小時的日誌
docker logs --since 1h my-app

# 顯示某個時間點之後的日誌
docker logs --since "2026-02-03T10:00:00" my-app

常用時間單位

  • s:秒
  • m:分鐘
  • h:小時

限制日誌檔案大小

在生產環境中,日誌可能快速累積並佔用大量磁碟空間。你可以在啟動容器時設定日誌大小限制:

docker run -d \
  --name my-app \
  --log-opt max-size=10m \
  --log-opt max-file=3 \
  nginx:alpine

參數說明

  • --log-opt max-size=10m:單一日誌檔案最大 10MB
  • --log-opt max-file=3:最多保留 3 個日誌檔案

當日誌檔案達到 10MB 時,Docker 會建立新的日誌檔案。當檔案數量超過 3 個時,最舊的檔案會被刪除。

系統清理:維護 Docker 環境

隨著使用時間增長,Docker 會累積許多未使用的資源:已停止的容器、懸空的映像檔、未使用的 volume 與網路。定期清理這些資源可以:

  • 釋放磁碟空間
  • 提升系統效能
  • 避免資源列表過於雜亂

清理已停止的容器

docker container prune

這個指令會移除所有狀態為 exited 的容器。

輸出範例

WARNING! This will remove all stopped containers.
Are you sure you want to continue? [y/N] y
Deleted Containers:
a1b2c3d4e5f6
g7h8i9j0k1l2

Total reclaimed space: 245MB

清理未使用的映像檔

# 清理懸空的映像檔(dangling images)
docker image prune

# 清理所有未被任何容器使用的映像檔
docker image prune -a

懸空映像檔(Dangling Images)

  • 指沒有標籤(tag)的映像檔
  • 通常是重新建立映像檔時產生的舊版本
  • docker images 中顯示為 <none>:<none>

-a 參數

  • 移除所有未被任何容器(包含已停止的容器)使用的映像檔
  • 使用時需謹慎,確保不會刪除之後還需要的映像檔

清理未使用的 Volume

docker volume prune

這個指令會移除所有未被任何容器(包含已停止的容器)掛載的 volume。

注意

Volume 通常包含重要的應用資料(如資料庫檔案)。執行 volume prune 前請確認沒有需要保留的資料。

清理未使用的網路

docker network prune

這個指令會移除所有未被任何容器使用的自訂網路(預設網路如 bridgehostnone 不會被刪除)。

一次清理多種資源

# 清理容器、映像檔、網路
docker system prune

# 清理容器、映像檔、網路、volume
docker system prune --volumes

# 清理所有未使用的映像檔(不只是懸空的)
docker system prune -a

常用選項

  • --volumes:同時清理未使用的 volume
  • -a--all:清理所有未使用的映像檔,而非只有懸空的
  • -f--force:跳過確認提示,直接執行清理

輸出範例

WARNING! This will remove:
  - all stopped containers
  - all networks not used by at least one container
  - all dangling images
  - all dangling build cache

Are you sure you want to continue? [y/N]

確認機制與強制執行

預設情況下,所有 prune 指令都會要求確認。如果你確定要清理,可以使用 -f 跳過確認:

docker system prune -f

謹慎使用 -f

使用 -f 時請確保你清楚知道哪些資源會被刪除,避免誤刪重要資料。

容器異常處理:診斷與修復

常見的容器狀態

Docker 容器可能處於以下幾種狀態:

狀態 說明
created 容器已建立但尚未啟動
running 容器正在執行
paused 容器已暫停(程式被凍結)
exited 容器已停止
dead 容器處於無法操作的狀態

使用 docker ps -a 可以查看所有容器的狀態:

docker ps -a

輸出範例:

CONTAINER ID   IMAGE         STATUS                      NAMES
a1b2c3d4e5f6   nginx:alpine  Up 2 hours                  web
g7h8i9j0k1l2   postgres:18   Exited (1) 5 minutes ago    db
m3n4o5p6q7r8   redis:alpine  Dead                        cache

診斷 exited 狀態的容器

當容器處於 exited 狀態時,你需要查看退出碼(exit code)來判斷原因。

使用 docker inspect 查看容器的詳細資訊:

docker inspect db --format='{{.State.ExitCode}}'

或查看完整的退出資訊:

docker inspect db --format='{{json .State}}' | jq

輸出範例:

{
  "Status": "exited",
  "Running": false,
  "Paused": false,
  "Restarting": false,
  "OOMKilled": false,
  "Dead": false,
  "Pid": 0,
  "ExitCode": 1,
  "Error": "",
  "StartedAt": "2026-02-03T10:30:00.123456789Z",
  "FinishedAt": "2026-02-03T10:35:00.987654321Z"
}

常見退出碼的含義

退出碼 含義 可能原因
0 正常退出 程式執行完成或被正常終止
1 一般錯誤 應用程式內部錯誤、設定錯誤
137 被 SIGKILL 終止 記憶體不足(OOM)或被強制終止
139 Segmentation Fault 程式崩潰(segfault)
143 被 SIGTERM 終止 收到終止信號(通常是 docker stop

除錯步驟

  1. 查看退出碼:docker inspect <container> --format='{{.State.ExitCode}}'
  2. 查看容器日誌:docker logs <container>
  3. 根據退出碼與日誌判斷問題
  4. 修正問題後重新啟動容器

處理 dead 狀態的容器

dead 狀態通常表示容器無法被正常移除。這可能是因為:

  • Docker daemon 異常
  • 檔案系統問題
  • 資源鎖定

處理方式

# 嘗試強制移除
docker rm -f <container>

# 如果仍無法移除,重啟 Docker daemon
# Linux/macOS
sudo systemctl restart docker

# Docker Desktop
# 從系統托盤重啟 Docker Desktop

使用日誌除錯

當容器無法正常運作時,日誌是最重要的除錯資訊來源:

# 查看完整日誌
docker logs my-app

# 查看最後 50 行日誌
docker logs --tail 50 my-app

# 持續追蹤日誌
docker logs -f my-app

常見問題模式

  • 應用程式無法啟動:檢查日誌中的錯誤訊息(如 missing environment variable)
  • 容器立即退出:檢查日誌與退出碼,可能是啟動指令錯誤
  • 容器重複重啟:檢查日誌與重啟策略,可能是應用程式無法連接依賴服務

安全性基礎

避免以 root 執行容器

風險說明

預設情況下,容器內的程式以 root 使用者身份執行。雖然容器提供了一定程度的隔離,但如果發生容器逃逸(container escape),攻擊者可能以 root 權限影響主機系統。

最佳實踐:除非必要,否則應以非 root 使用者執行容器。

使用 --user 參數

在啟動容器時指定執行使用者:

# 以 UID 1000 的使用者執行
docker run -d --name app --user 1000 nginx:alpine

# 以特定使用者與群組執行
docker run -d --name app --user 1000:1000 nginx:alpine

在 Dockerfile 中設定 USER

更好的做法是在 Dockerfile 中指定預設執行使用者:

Dockerfile
FROM node:20-alpine

# 建立非 root 使用者
RUN addgroup -g 1001 appgroup && \
    adduser -D -u 1001 -G appgroup appuser

# 設定工作目錄
WORKDIR /app

# 複製應用程式檔案
COPY package*.json ./
RUN npm install

COPY . .

# 切換到非 root 使用者
USER appuser

# 啟動應用程式
CMD ["node", "index.js"]

這樣建立的映像檔,在執行時預設就會以非 root 使用者身份執行。

使用可信任的映像檔

優先使用 Docker Official Images

Docker Official Images 是由 Docker 官方或軟體供應商維護的映像檔,經過安全審核與定期更新。

識別方式

  • 映像檔名稱簡短,沒有使用者名稱前綴(如 nginxpostgrespython
  • 在 Docker Hub 上會顯示 DOCKER OFFICIAL IMAGE 標籤
# 官方映像檔
docker pull nginx:alpine
docker pull postgres:18
docker pull python:3.12-slim

優點

  • 定期更新與安全修補
  • 遵循最佳實踐
  • 社群支援充足

認識 Verified Publisher

Verified Publisher 標籤表示該映像檔由經過 Docker 驗證的發行商提供。這些發行商通常是知名軟體公司(如 Microsoft、Red Hat)。

雖然不如官方映像檔,但 Verified Publisher 的映像檔仍值得信任。

避免使用 latest 標籤

latest 標籤指向映像檔的最新版本,但這會帶來以下問題:

  • 不可預測性latest 可能隨時改變,導致應用程式行為不一致
  • 難以復現問題:無法確定當時使用的是哪個版本
  • 潛在的破壞性更新:新版本可能引入不相容的變更

最佳實踐:使用具體的版本標籤。

# ❌ 避免
docker pull nginx:latest

# ✅ 建議
docker pull nginx:1.25-alpine
docker pull postgres:18.1
docker pull python:3.12.1-slim

映像檔弱點掃描:Docker Scout

什麼是 Docker Scout

Docker Scout 是 Docker 官方提供的弱點掃描工具,可以分析映像檔中的已知安全弱點(CVE,Common Vulnerabilities and Exposures)。

Docker Scout 整合在 Docker CLI 中,無需額外安裝。

快速查看弱點摘要

使用 docker scout quickview 快速查看映像檔的弱點摘要:

docker scout quickview nginx:1.25-alpine

輸出範例:

    ✓ Provenance: inferred
    ✓ SBOM: detected

  Target: nginx:1.25-alpine (linux/arm64)

    Critical:   0
    High:       1
    Medium:     3
    Low:        8
    Unspecified: 0

這個指令會顯示映像檔中不同嚴重程度的弱點數量。

查看詳細的 CVE 列表

使用 docker scout cves 查看詳細的弱點列表:

docker scout cves nginx:1.25-alpine

輸出範例(擷取):

    ✓ Provenance: inferred
    ✓ SBOM: detected

  Target: nginx:1.25-alpine (linux/arm64)

  ## Critical Vulnerabilities

  No critical vulnerabilities found

  ## High Vulnerabilities

  1 high vulnerabilities found in 1 package

    CVE-2024-1234  HIGH
    Package: libssl3
    Current version: 3.1.0-r1
    Fixed in: 3.1.1-r0
    Link: https://scout.docker.com/v/CVE-2024-1234

弱點嚴重程度分級

Docker Scout 使用 CVSS(Common Vulnerability Scoring System)標準評估弱點嚴重程度:

等級 說明 建議處理
Critical 極嚴重弱點,可能導致完全入侵 立即修補或更換映像檔
High 高風險弱點,可能嚴重影響安全 盡快修補
Medium 中等風險弱點 視情況修補
Low 低風險弱點 可後續處理

處理建議

  1. 優先修補 Critical 與 High 等級的弱點
  2. 檢查是否有更新的映像檔版本可用
  3. 如果映像檔無法更新,評估弱點的實際影響
  4. 在生產環境使用映像檔前,務必掃描弱點

工作流程範例

# 1. 掃描映像檔
docker scout quickview myapp:latest

# 2. 如果發現高風險弱點,查看詳細資訊
docker scout cves myapp:latest

# 3. 根據建議更新基礎映像檔或套件
# 4. 重新建立映像檔
docker build -t myapp:latest .

# 5. 再次掃描確認問題已修復
docker scout quickview myapp:latest

任務結束

完成!

恭喜你完成了這個任務!現在你已經學會:

  • 使用資源限制控制容器的記憶體與 CPU 用量
  • 設定容器重啟策略以提升服務可靠性
  • 掌握日誌管理與查看技巧
  • 維護 Docker 環境整潔:清理未使用的資源
  • 診斷與處理異常狀態的容器
  • 建立安全性基礎觀念:非 root 執行、可信任映像檔、弱點掃描

這些知識與技能是邁向生產環境的重要基礎,能幫助你建立更穩定、安全、易於維護的容器化應用。