前言:為什麼需要 Pod?

在 Kubernetes 的世界裡,Pod 是一切的基礎。如果把 Kubernetes 比喻成一座城市,那麼 Pod 就是城市中的「最小住宅單位」。

但為什麼 Kubernetes 不直接管理容器(Container),而要多一層 Pod 的抽象?

簡單回答:因為容器太小,Pod 剛剛好。

想像你要管理一座城市的住宅:

  • 如果直接管理每個「房間」(容器)→ 太細碎,管理成本太高
  • 如果直接管理整棟「大樓」(Node)→ 太粗糙,缺乏彈性
  • 所以我們需要「住宅單位」(Pod)→ 大小適中,便於管理

本文將深入探討:

  • Pod 的核心概念與設計哲學
  • Pod 的內部架構與運作機制
  • Pod 網路模型與通訊方式
  • Pod 生命週期與狀態管理
  • Pod 設計模式與最佳實踐
  • 實戰範例與 YAML 配置

Pod 核心概念:容器的邏輯主機

什麼是 Pod?

官方定義:

Pod 是 Kubernetes 中最小的可部署計算單元,可以包含一個或多個容器,這些容器共享網路、儲存和其他資源。

生活化比喻:

Mermaid Diagram

Pod 就像一個「邏輯主機」

  • 在傳統架構中,多個應用程式運行在同一台虛擬機上
  • 在 Kubernetes 中,多個容器運行在同一個 Pod 上
  • Pod 提供了容器之間的「緊密耦合」環境

Pod 的三大核心特性

1. 共享網路命名空間

Mermaid Diagram

同一個 Pod 內的容器:

  • ✅ 共享同一個 IP 位址
  • ✅ 可以透過 localhost 互相通訊
  • ✅ 但 Port 不能衝突(每個容器用不同 Port)

2. 共享儲存卷(Volume)

Mermaid Diagram

同一個 Pod 內的容器:

  • ✅ 可以掛載相同的 Volume
  • ✅ 實現檔案共享(例如日誌、設定檔)
  • ✅ 不需要透過網路傳輸

3. 原子化調度

Kubernetes 調度器(Scheduler)將 Pod 視為一個整體:

  • 整個 Pod 會被分配到同一個 Node 上
  • Pod 內的所有容器一起啟動、一起停止
  • 資源限制(CPU、Memory)以 Pod 為單位計算

Pod 架構深度解析

Pod 的內部結構

Mermaid Diagram

容器類型說明:

1. Pause 容器(基礎設施容器)

  • Kubernetes 自動創建的隱藏容器
  • 作用:建立並持有網路命名空間
  • 極其輕量(~700KB),幾乎不消耗資源
  • Pod 的生命週期由它決定

2. Init 容器(初始化容器)

  • 在應用容器啟動前執行
  • 按順序執行(一個接一個)
  • 用途:資料庫遷移、設定檔準備、等待依賴服務
  • 失敗會導致 Pod 重啟

3. 應用容器(Main Containers)

  • 實際運行業務邏輯的容器
  • 可以有一個或多個
  • 同時啟動(並行)

4. Sidecar 容器(輔助容器)

  • 與主容器同時運行
  • 用途:日誌收集、監控代理、服務網格(如 Envoy)
  • 獨立的生命週期管理

Pod 網路模型

Kubernetes 網路三大原則:

  1. 每個 Pod 有獨立的 IP
  2. Pod 之間可以直接通訊(不需要 NAT)
  3. Node 可以與所有 Pod 通訊
Mermaid Diagram

Pod 內部通訊範例:

# Pod 定義
apiVersion: v1
kind: Pod
metadata:
  name: multi-container-pod
spec:
  containers:
  - name: nginx
    image: nginx:1.21
    ports:
    - containerPort: 80

  - name: log-agent
    image: fluent/fluentd:latest
    # 可以透過 localhost:80 存取 nginx

在 log-agent 容器內:

# 直接用 localhost 訪問 nginx
curl http://localhost:80

# 成功!因為共享網路命名空間

Pod 生命週期管理

Pod 的狀態機

Mermaid Diagram

Pod 生命週期階段詳解

1. Pending(等待中)

Pod 已被 Kubernetes 接受,但還沒開始運行:

$ kubectl get pod my-pod

NAME     READY   STATUS    RESTARTS   AGE
my-pod   0/1     Pending   0          5s

可能原因:

  • ⏳ 等待調度器分配 Node
  • ⏳ 正在拉取容器映像檔
  • ⏳ Init 容器正在執行
  • ❌ 資源不足(CPU、Memory)
  • ❌ PersistentVolumeClaim 無法綁定

排查指令:

# 查看詳細事件
kubectl describe pod my-pod

# 常見錯誤訊息
Events:
  Type     Reason            Message
  ----     ------            -------
  Warning  FailedScheduling  0/3 nodes are available: insufficient memory

2. Running(運行中)

至少一個容器正在運行:

$ kubectl get pod my-pod

NAME     READY   STATUS    RESTARTS   AGE
my-pod   2/2     Running   0          1m

READY 欄位解讀:

  • 2/2 → 2 個容器都就緒
  • 1/2 → 只有 1 個容器就緒(另一個可能在啟動或崩潰)
  • 0/2 → 沒有容器就緒(可能在重啟)

3. Succeeded(成功完成)

所有容器都成功結束(退出碼 0):

$ kubectl get pod my-job-pod

NAME          READY   STATUS      RESTARTS   AGE
my-job-pod    0/1     Completed   0          2m

適用於:

  • Kubernetes Job
  • CronJob
  • 一次性任務(資料遷移、批次處理)

4. Failed(失敗)

至少一個容器以非零退出碼結束:

$ kubectl get pod my-pod

NAME     READY   STATUS   RESTARTS   AGE
my-pod   0/1     Error    0          30s

常見失敗狀態:

  • Error - 容器退出碼非零
  • CrashLoopBackOff - 容器反覆崩潰
  • ImagePullBackOff - 無法拉取映像檔
  • OOMKilled - 記憶體不足被殺掉

5. Unknown(未知)

無法獲取 Pod 狀態(通常是 Node 失聯):

$ kubectl get pod my-pod

NAME     READY   STATUS    RESTARTS   AGE
my-pod   0/1     Unknown   0          5m

Pod 重啟策略

apiVersion: v1
kind: Pod
metadata:
  name: my-pod
spec:
  restartPolicy: Always  # Always | OnFailure | Never
  containers:
  - name: app
    image: myapp:v1

重啟策略對照表:

策略說明適用場景
Always容器退出就重啟(預設)長期運行的服務(Web、API)
OnFailure只有失敗時才重啟批次處理、資料處理
Never從不重啟一次性任務、偵錯

重啟行為:

Mermaid Diagram

Pod 設計模式

1. Sidecar 模式(最常見)

**概念:**在主容器旁邊放一個輔助容器,提供額外功能。

使用場景:

  • 日誌收集(Fluentd、Filebeat)
  • 監控代理(Prometheus exporter)
  • 服務網格(Istio Envoy)
  • 配置熱更新

架構圖:

Mermaid Diagram

實戰範例:Nginx + 日誌收集

apiVersion: v1
kind: Pod
metadata:
  name: nginx-with-logging
spec:
  containers:
  # 主容器:Nginx Web Server
  - name: nginx
    image: nginx:1.21
    ports:
    - containerPort: 80
    volumeMounts:
    - name: logs
      mountPath: /var/log/nginx

  # Sidecar 容器:日誌收集
  - name: log-collector
    image: fluent/fluentd:latest
    volumeMounts:
    - name: logs
      mountPath: /var/log/nginx
      readOnly: true  # 只讀,避免誤刪

  volumes:
  - name: logs
    emptyDir: {}  # 臨時儲存

2. Ambassador 模式(大使模式)

**概念:**Sidecar 容器作為主容器與外部世界的代理。

使用場景:

  • 資料庫連線池代理
  • Redis 集群代理
  • gRPC 負載平衡

架構圖:

Mermaid Diagram

實戰範例:應用 + Redis Ambassador

apiVersion: v1
kind: Pod
metadata:
  name: app-with-redis-proxy
spec:
  containers:
  # 主容器:應用程式
  - name: app
    image: myapp:v1
    env:
    - name: REDIS_HOST
      value: "localhost"  # 連到 Sidecar
    - name: REDIS_PORT
      value: "6379"

  # Ambassador 容器:Redis Proxy
  - name: redis-proxy
    image: haproxy:2.4
    ports:
    - containerPort: 6379
    volumeMounts:
    - name: config
      mountPath: /usr/local/etc/haproxy

  volumes:
  - name: config
    configMap:
      name: redis-proxy-config

3. Adapter 模式(適配器模式)

**概念:**標準化主容器的輸出格式。

使用場景:

  • 日誌格式轉換(JSON → Plain Text)
  • 監控指標轉換(Prometheus 格式)
  • 資料格式標準化

架構圖:

Mermaid Diagram

實戰範例:日誌格式轉換

apiVersion: v1
kind: Pod
metadata:
  name: app-with-adapter
spec:
  containers:
  # 主容器:舊版應用(非標準日誌)
  - name: legacy-app
    image: old-app:1.0
    volumeMounts:
    - name: logs
      mountPath: /var/log/app

  # Adapter 容器:日誌格式轉換
  - name: log-adapter
    image: busybox
    command:
    - sh
    - -c
    - |
      tail -f /var/log/app/app.log | \
      awk '{print "{\"timestamp\":\""strftime("%Y-%m-%dT%H:%M:%S")"\"
,\"message\":\""$0"\"}"}' \
      > /var/log/standardized/app.json
    volumeMounts:
    - name: logs
      mountPath: /var/log/app
      readOnly: true
    - name: standardized-logs
      mountPath: /var/log/standardized

  volumes:
  - name: logs
    emptyDir: {}
  - name: standardized-logs
    emptyDir: {}

實戰配置範例

基本 Pod 配置

apiVersion: v1
kind: Pod
metadata:
  name: simple-web-app
  labels:
    app: web
    tier: frontend
spec:
  containers:
  - name: nginx
    image: nginx:1.21
    ports:
    - containerPort: 80
      protocol: TCP

    # 資源限制
    resources:
      requests:
        memory: "64Mi"
        cpu: "250m"
      limits:
        memory: "128Mi"
        cpu: "500m"

    # 健康檢查
    livenessProbe:
      httpGet:
        path: /
        port: 80
      initialDelaySeconds: 30
      periodSeconds: 10

    readinessProbe:
      httpGet:
        path: /
        port: 80
      initialDelaySeconds: 5
      periodSeconds: 5

使用 Init 容器

apiVersion: v1
kind: Pod
metadata:
  name: app-with-init
spec:
  # Init 容器(按順序執行)
  initContainers:
  - name: init-db-check
    image: busybox:1.35
    command:
    - sh
    - -c
    - |
      until nc -z db-service 5432; do
        echo "Waiting for database..."
        sleep 2
      done
      echo "Database is ready!"

  - name: init-migration
    image: myapp:v1
    command: ["python", "manage.py", "migrate"]
    env:
    - name: DATABASE_URL
      valueFrom:
        secretKeyRef:
          name: db-secret
          key: url

  # 主容器
  containers:
  - name: app
    image: myapp:v1
    ports:
    - containerPort: 8000

多容器 Pod(完整範例)

apiVersion: v1
kind: Pod
metadata:
  name: full-stack-pod
  labels:
    app: my-app
spec:
  # Init 容器:準備工作
  initContainers:
  - name: setup-config
    image: busybox
    command:
    - sh
    - -c
    - |
      echo "Preparing configuration..."
      cp /config-template/* /app-config/
    volumeMounts:
    - name: config-template
      mountPath: /config-template
    - name: app-config
      mountPath: /app-config

  # 主容器們
  containers:
  # 1. 應用程式
  - name: app
    image: myapp:v2.1
    ports:
    - containerPort: 8080
    env:
    - name: CONFIG_PATH
      value: /app-config
    volumeMounts:
    - name: app-config
      mountPath: /app-config
    - name: logs
      mountPath: /var/log/app
    resources:
      requests:
        memory: "256Mi"
        cpu: "500m"
      limits:
        memory: "512Mi"
        cpu: "1000m"
    livenessProbe:
      httpGet:
        path: /health
        port: 8080
      initialDelaySeconds: 60
      periodSeconds: 10
    readinessProbe:
      httpGet:
        path: /ready
        port: 8080
      initialDelaySeconds: 10
      periodSeconds: 5

  # 2. 日誌收集 Sidecar
  - name: log-collector
    image: fluent/fluentd:v1.14
    volumeMounts:
    - name: logs
      mountPath: /var/log/app
      readOnly: true
    - name: fluentd-config
      mountPath: /fluentd/etc
    resources:
      requests:
        memory: "128Mi"
        cpu: "100m"
      limits:
        memory: "256Mi"
        cpu: "200m"

  # 3. 監控 Exporter Sidecar
  - name: metrics-exporter
    image: prom/node-exporter:latest
    ports:
    - containerPort: 9100
    resources:
      requests:
        memory: "64Mi"
        cpu: "50m"
      limits:
        memory: "128Mi"
        cpu: "100m"

  # Volumes
  volumes:
  - name: config-template
    configMap:
      name: app-config-template
  - name: app-config
    emptyDir: {}
  - name: logs
    emptyDir: {}
  - name: fluentd-config
    configMap:
      name: fluentd-config

  # Pod 設定
  restartPolicy: Always
  serviceAccountName: my-app-sa
  securityContext:
    runAsNonRoot: true
    runAsUser: 1000
    fsGroup: 2000

Pod 管理與操作

常用 kubectl 指令

查看 Pod:

# 列出所有 Pod
kubectl get pods

# 查看特定 namespace 的 Pod
kubectl get pods -n production

# 查看所有 namespace 的 Pod
kubectl get pods --all-namespaces

# 查看詳細資訊
kubectl get pods -o wide

# 查看 YAML 定義
kubectl get pod my-pod -o yaml

# 持續監控 Pod 狀態
kubectl get pods -w

查看 Pod 詳情:

# 查看 Pod 詳細資訊(包含事件)
kubectl describe pod my-pod

# 查看特定容器的資訊
kubectl describe pod my-pod | grep -A 10 "Container ID"

查看 Pod 日誌:

# 查看 Pod 日誌
kubectl logs my-pod

# 查看特定容器的日誌(多容器 Pod)
kubectl logs my-pod -c nginx

# 即時查看日誌(類似 tail -f)
kubectl logs -f my-pod

# 查看之前崩潰的容器日誌
kubectl logs my-pod --previous

# 查看最近 100 行日誌
kubectl logs my-pod --tail=100

# 加上時間戳記
kubectl logs my-pod --timestamps

進入 Pod 容器:

# 進入 Pod 的第一個容器
kubectl exec -it my-pod -- /bin/bash

# 進入特定容器
kubectl exec -it my-pod -c nginx -- /bin/sh

# 執行單一指令
kubectl exec my-pod -- ls /app

# 在特定容器執行指令
kubectl exec my-pod -c nginx -- nginx -t

複製檔案:

# 從 Pod 複製到本地
kubectl cp my-pod:/var/log/app.log ./app.log

# 從本地複製到 Pod
kubectl cp ./config.yaml my-pod:/etc/config/

# 複製到特定容器
kubectl cp my-pod:/data/export.csv ./export.csv -c app

刪除 Pod:

# 刪除單個 Pod
kubectl delete pod my-pod

# 刪除多個 Pod
kubectl delete pod pod1 pod2 pod3

# 使用標籤選擇器刪除
kubectl delete pods -l app=nginx

# 強制刪除(不等待 Grace Period)
kubectl delete pod my-pod --grace-period=0 --force

# 刪除所有 Pod
kubectl delete pods --all

Pod 最佳實踐

1. 資源管理

總是設定資源請求和限制:

spec:
  containers:
  - name: app
    resources:
      requests:  # 最小保證
        memory: "256Mi"
        cpu: "500m"
      limits:    # 最大限制
        memory: "512Mi"
        cpu: "1000m"

原則:

  • requests 用於調度決策
  • limits 用於資源限制
  • ✅ 避免設定過大的 limits(容易 OOMKilled)
  • ✅ CPU 可以超賣,Memory 不可以

2. 健康檢查

同時設定 Liveness 和 Readiness Probe:

spec:
  containers:
  - name: app
    livenessProbe:   # 檢查是否存活
      httpGet:
        path: /health
        port: 8080
      initialDelaySeconds: 60
      periodSeconds: 10
      failureThreshold: 3

    readinessProbe:  # 檢查是否就緒
      httpGet:
        path: /ready
        port: 8080
      initialDelaySeconds: 10
      periodSeconds: 5
      failureThreshold: 3

差異:

  • livenessProbe 失敗 → 重啟容器
  • readinessProbe 失敗 → 從 Service 移除,但不重啟

3. 優雅關閉

spec:
  containers:
  - name: app
    lifecycle:
      preStop:
        exec:
          command:
          - sh
          - -c
          - |
            # 停止接受新請求
            echo "Stopping accepting new requests..."
            # 等待現有請求完成
            sleep 15
            # 清理資源
            echo "Cleaning up..."

  terminationGracePeriodSeconds: 30  # 給予 30 秒關閉時間

4. 避免單一容器做太多事

❌ 不好的做法:

# 單一容器包含 Web Server + Database + Cache
containers:
- name: all-in-one
  image: my-monolith:v1

✅ 好的做法:

# 分離關注點
containers:
- name: web
  image: nginx:1.21
- name: app
  image: myapp:v2
- name: cache
  image: redis:6.2

5. 使用標籤和註解

metadata:
  name: my-pod
  labels:
    app: web
    tier: frontend
    version: v2.1
    environment: production
  annotations:
    prometheus.io/scrape: "true"
    prometheus.io/port: "9090"
    description: "Main web application pod"

常見問題與解決方案

Q1: 什麼時候該用多容器 Pod?

應該用多容器:

  • ✅ 容器之間需要緊密耦合(Sidecar 模式)
  • ✅ 容器需要共享檔案系統
  • ✅ 容器需要在同一台機器上(例如 localhost 通訊)

不應該用多容器:

  • ❌ 獨立的微服務(應該用多個 Pod)
  • ❌ 可以透過網路通訊的服務
  • ❌ 需要獨立擴展的應用

Q2: Pod 和 Deployment 有什麼區別?

特性PodDeployment
層級最小單元控制器
副本單一實例管理多個副本
自癒不會自動重建自動重建
更新不支援滾動更新支援滾動更新
使用場景偵錯、測試生產環境

**結論:**生產環境應該使用 Deployment,而不是直接創建 Pod。

Q3: 為什麼我的 Pod 一直 Pending?

排查步驟:

# 1. 查看 Pod 事件
kubectl describe pod my-pod

# 常見原因:
# - Insufficient CPU/Memory(資源不足)
# - No nodes available(沒有可用節點)
# - PVC not bound(儲存卷未綁定)
# - Image pull error(映像檔拉取失敗)

# 2. 查看節點資源
kubectl top nodes

# 3. 查看 PVC 狀態
kubectl get pvc

Q4: 如何在 Pod 之間共享資料?

不推薦在 Pod 之間直接共享 Volume(不同 Pod 可能在不同 Node)

推薦方案:

  1. 使用網路儲存(NFS、S3)
  2. 使用資料庫或快取(Redis、PostgreSQL)
  3. 使用訊息佇列(RabbitMQ、Kafka)

Q5: Init 容器失敗了怎麼辦?

Init 容器失敗會導致 Pod 無法啟動:

# 查看 Init 容器日誌
kubectl logs my-pod -c init-db-check

# Pod 會不斷重試 Init 容器
# 直到成功或達到 BackoffLimit

解決方案:

  • 修正 Init 容器的錯誤
  • 檢查依賴服務是否就緒
  • 調整超時設定

總結與收穫

關鍵要點

Pod 核心概念:

  • Pod 是 K8s 最小部署單元:容器的邏輯主機
  • 共享網路和儲存:Pod 內容器可以用 localhost 通訊
  • 原子化調度:整個 Pod 被視為一個整體

Pod 設計模式:

  • Sidecar:輔助容器提供額外功能(日誌、監控)
  • Ambassador:代理容器簡化外部通訊
  • Adapter:轉換容器標準化輸出

生產環境建議:

  • ✅ 使用 Deployment 而非直接創建 Pod
  • ✅ 設定資源 requests 和 limits
  • ✅ 配置健康檢查(Liveness & Readiness)
  • ✅ 實現優雅關閉
  • ✅ 使用標籤和註解管理

從 Pod 到 Kubernetes 生態

理解 Pod 只是第一步,接下來你可以探索:

  1. Deployment - 管理 Pod 副本和滾動更新
  2. Service - 為 Pod 提供穩定的網路端點
  3. ConfigMap / Secret - 管理設定和敏感資料
  4. PersistentVolume - 持久化儲存
  5. StatefulSet - 有狀態應用
  6. DaemonSet - 每個節點運行一個 Pod
  7. Job / CronJob - 批次任務

Pod 是 Kubernetes 的基石,掌握 Pod 的概念和使用方式,你就能理解整個 Kubernetes 生態系統的運作原理。


參考資源

官方文件:

實用工具:

延伸閱讀: