cover

Container Runtime:把環境變成可重複的最小單位

容器化最大的價值不是「跑起來」,而是「到哪裡都跑得起來」。 Container Runtime 是整個基礎設施的執行層,它負責建立容器、管理網路、掛載儲存與控制生命週期。只要這一層標準化,後續的部署、監控與 CI/CD 才能一致化。

如果沒有標準化的 runtime,你會面臨:

  • 每台機器環境不同,部署結果不一致
  • 同一個服務在不同環境行為不同
  • 回滾與重建都需要「人腦記憶」

架構概覽

flowchart LR
  Dev[開發者] --> Build[Build Image]
  Build --> Registry[Container Registry]
  Registry --> Host[Docker Host]
  Host --> ContainerA[API Container]
  Host --> ContainerB[Admin Container]
  Host --> ContainerC[Worker Container]

  Portainer[Portainer] --> Host

核心概念

  1. Image 與 Container 的差異

    • Image 是不可變的模板,Container 是運行中的實例。
    • 這讓「部署」變成下載 image 並啟動,降低環境漂移。
    • 最佳實務:避免在容器內做手動修改,一切變更要回到 image。
  2. Docker Compose 作為宣告式部署

    • 透過 YAML 描述服務、網路、環境變數與 volume。
    • 一份 Compose 可重複使用,降低手動操作成本。
    • 對小團隊而言,Compose 就是一套「輕量級部署標準」。
  3. 容器網路(Bridge / Overlay)

    • Bridge 提供單機容器間的通訊。
    • 多機部署時可擴展到 Overlay(Swarm/K8s)。
    • 設計重點:網路命名與隔離,避免所有容器都在同一網段。
  4. Volume 與資料持久化

    • 容器是可重建的,資料必須落在 volume 或外部儲存。
    • 特別是資料庫與上傳檔案,必須明確指定掛載路徑。
    • 常見錯誤:把重要資料留在容器內,重建時資料會消失。
  5. 管理與可視化

    • Portainer 提供 GUI 來管理容器、logs、資源用量。
    • 小團隊可用它建立一致的操作流程。
    • 但仍需搭配權限管理,避免所有人都能直接重啟 production。

使用情境(具體場景)

  • 一致化開發與部署:開發端用 Docker build,正式環境也用同一個 image,避免「我這邊可以」的問題。
  • 快速回復:容器壞掉時直接重啟或重建,減少人工排查成本。
  • 多服務部署:API、Admin、Worker 同時部署在一台主機上,但環境相互隔離。
  • Dev/Test 快速複製:用同一份 compose 在本機或測試機啟動服務,縮短啟動時間。

實作範例 / 設定範例

1) Dockerfile 範例(Node.js)

# Dockerfile
FROM node:20-alpine
 
WORKDIR /app
COPY package*.json ./
RUN npm ci --only=production
 
COPY . .
EXPOSE 3000
 
CMD ["node", "server.js"]

2) Docker Compose 範例(API + Redis)

version: "3.9"
services:
  api:
    image: registry.example.com/api:1.2.0
    ports:
      - "3000:3000"
    environment:
      NODE_ENV: production
    depends_on:
      - redis
 
  redis:
    image: redis:7
    volumes:
      - redis-data:/data
 
volumes:
  redis-data:

3) 容器網路檢視與測試

# 查看 network
docker network ls
 
# 查看 container IP
docker inspect api | jq '.[0].NetworkSettings.Networks'
 
# 在容器內測試連線
docker exec -it api sh -c "apk add --no-cache curl && curl http://redis:6379"

4) Portainer 部署

docker volume create portainer_data
 
docker run -d \
  -p 9000:9000 \
  --name portainer \
  --restart=always \
  -v /var/run/docker.sock:/var/run/docker.sock \
  -v portainer_data:/data \
  portainer/portainer-ce:latest

5) 資源限制與重啟策略

# docker-compose.resources.yaml
version: "3.9"
services:
  api:
    image: registry.example.com/api:1.2.0
    deploy:
      resources:
        limits:
          cpus: "0.5"
          memory: 512M
        reservations:
          memory: 256M
    restart: always

常見問題與風險

  • 資料遺失: 如果沒有用 volume 持久化,容器刪掉資料就消失。資料庫與上傳檔案必須明確掛載。

    • 避免方式:為重要服務建立獨立 volume,並列入備份清單。
  • Image 漂移: 使用 latest 容易導致版本不一致,部署時行為不同。建議使用明確版本號。

    • 避免方式:每次 build 使用 tag(如 commit SHA),並在 registry 保留前一版。
  • 資源競爭: 多容器同時跑在一台主機上,若沒有資源限制,可能互相搶 CPU/記憶體。應搭配 --memory 或 compose 限制。

    • 避免方式:先設定保守上限,再依監控調整。
  • 安全權限: 容器預設以 root 執行容易造成安全風險。應在 Dockerfile 中建立非 root 使用者。

    • 避免方式:使用 USER node 或自建 user。
  • 日誌爆量: 容器 log 無限制增長會耗盡磁碟。需設定 log rotation 或外部收集。

    • 避免方式:設定 --log-opt max-size 或使用 fluentd/otel。

優點

  • 部署流程一致,環境可重複
  • 容器隔離降低相互影響
  • 可快速擴展與回復

缺點 / 限制

  • 容器網路與 storage 的概念成本較高
  • 若缺乏治理,容易出現 image 漂移與資源爭用
  • 單機 Docker 不等於高可用,仍需要上層編排(Swarm/K8s)

落地檢查清單

  • 是否固定 image 版本號
  • 是否明確定義 volume
  • 是否限制容器資源(CPU/記憶體)
  • 是否有 log rotation
  • 是否建立管理入口(如 Portainer)

延伸閱讀