K8s · 工作負載 · 第 10 課 · · 9min read

Sidecar Pattern 是什麼?一個 Pod 跑兩個容器的正確姿勢

一個 Pod 通常一個容器,但你也可以放兩個。這個「副駕駛」模式叫 Sidecar,常用來收日誌、抓 metrics、做網路代理。這篇實作一個 nginx + busybox 共享 Volume 的範例。

#Kubernetes #Sidecar #Pattern #Pod #日誌
章節目錄 · 9

一個 Pod 通常一個容器,但你也可以塞兩個

上一篇學完排錯,我們現在處理一個常見場景:nginx 寫了一堆 access log,老闆要把這些日誌即時收集到集中式日誌系統去。你會怎麼做?

直覺做法 1:在 nginx 容器裡再裝一個 Fluentd → ❌ 違反「一個容器一件事」原則。

直覺做法 2:另外跑一個 Pod 去讀 nginx 的日誌 → ❌ 兩個 Pod 各自有檔案系統,要搞跨 Pod 共享儲存太麻煩。

K8s 提供更優雅的方案:同一個 Pod 裡放兩個容器,一個跑 nginx 寫日誌,一個跑 busybox 讀日誌轉發出去。它們共享同一個 Volume,nginx 寫進去 busybox 直接讀出來。這就是 Sidecar 模式

Sidecar = 摩托車 + 邊車

主容器負責核心業務(nginx 服務 Web 請求),Sidecar 像掛在旁邊的邊車,負責輔助功能(收日誌)。邊車不是主角,但它讓主角的工作更完整。

生產環境最常見的 3 種 Sidecar

用途主容器Sidecar
日誌收集nginx / appFluentd / Filebeat
流量代理appEnvoy(Istio Service Mesh)
監控指標appPrometheus Exporter

同一個 Pod 的容器共享什麼?

1. 共享網路(同一個 IP)

  • 兩個容器看到的 IP 都一樣
  • 互相用 localhost 通訊
  • nginx 監聽 80 → Sidecar 用 localhost:80 連它
2. 共享儲存(同一個 Volume)
  • nginx 寫到 /var/log/nginx/access.log
  • Sidecar 掛同一個 Volume,看到同一批檔案
就像兩個人住同一間房子:找室友不用打電話,吼一聲就行;冰箱也是同一個。

emptyDir:Pod 內的臨時共用資料夾

K8s 最簡單的 Volume 類型:

  • Pod 建立時自動出現
  • Pod 刪除時自動消失
  • 不需要 PV / PVC
  • 適合「主容器寫、Sidecar 讀」這種臨時共享
⚠️ emptyDir 不適合存重要資料(Pod 一刪就沒了),它就是兩個容器之間的暫存通道。

實作:nginx + busybox 收日誌

# pod-sidecar.yaml
apiVersion: v1
kind: Pod
metadata:
  name: sidecar-pod
  labels:
    app: sidecar-demo
spec:
  containers:
  - name: nginx
    image: nginx:1.27
    ports:
    - containerPort: 80
    volumeMounts:
    - name: shared-logs
      mountPath: /var/log/nginx
  - name: log-reader
    image: busybox:1.36
    command:
    - /bin/sh
    - -c
    - |
      while [ ! -f /var/log/nginx/access.log ]; do
        sleep 1
      done
      tail -f /var/log/nginx/access.log
    volumeMounts:
    - name: shared-logs
      mountPath: /var/log/nginx
  volumes:
  - name: shared-logs
    emptyDir: {}

三個關鍵細節

1. nginx 的 symlink 行為

nginx 官方 Image 預設把 access.log symlink 到 /dev/stdout。但我們把 emptyDir 掛到 /var/log/nginx 後,會覆蓋掉 symlink,nginx 改寫到真實檔案。這正是我們要的(Sidecar 才讀得到)。

2. busybox 的 while 等待

while [ ! -f /var/log/nginx/access.log ]; do
  sleep 1
done
tail -f /var/log/nginx/access.log

為什麼不直接 tail -f?因為同一個 Pod 的容器是同時啟動的,K8s 不保證誰先跑起來。如果 busybox 比 nginx 早,access.log 還不存在,tail -f 會直接報錯 crash。

加 while 等待避免這個 race condition。多容器 Pod 很常見的小技巧,記住。

3. volumes 在 spec 層級、不在 container 裡

Volume 是給整個 Pod 共享的,所以定義在 spec.volumes(與 spec.containers 同層),然後每個容器各自用 volumeMounts 掛載。

部署 + 驗證

kubectl apply -f pod-sidecar.yaml

kubectl get pods
# READY: 2/2 ← 兩個容器都 ready 才會顯示 2/2
# 1/2 表示有一個還沒 ready,等幾秒再看

# 進 nginx 容器(多容器 Pod 必須加 -c 指定)
kubectl exec -it sidecar-pod -c nginx -- /bin/sh
apt-get update && apt-get install -y curl
curl localhost
curl localhost
curl localhost
exit

# 看 Sidecar 收到的日誌
kubectl logs sidecar-pod -c log-reader
# 你會看到 3 行 access log

💡 多容器 Pod 操作必須加 -cexeclogs 都要用 -c 指定哪個容器,不然 K8s 會問你選哪個。

多容器 Pod vs 多個獨立 Pod

判斷標準很簡單:拿掉一個容器,另一個還能不能正常工作

多容器 Pod多個獨立 Pod
何時用緊密耦合獨立運作
網路共享 IP,用 localhost各自有 IP
擴縮容一起擴一起縮獨立擴縮
生命週期一起生一起死各自獨立
範例nginx + log collectornginx + mysql
範例對比
  • nginx + 日誌收集器:日誌收集器拿掉 → nginx 還能跑;nginx 拿掉 → 日誌收集器沒事做。緊密耦合,同 Pod
  • nginx + MySQL:互相獨立,且 nginx 可能擴 5 個但 MySQL 不要擴。拆開放兩個 Pod
預設原則:一個 Pod 一個容器。只有真的需要共享網路或共享儲存時才考慮多容器 Pod。

重點整理

  • Sidecar = 主容器 + 輔助容器,共享網路 + 共享儲存
  • 同一個 Pod 的容器有相同 IP,可用 localhost 通訊
  • emptyDir 是 Pod 內臨時共用資料夾,Pod 刪就消失
  • 多容器 Pod 是同時啟動,注意 race condition(用 while 等待)
  • nginx 掛 emptyDir 到 /var/log/nginx 會覆蓋 symlink、改寫真檔案
  • exec / logs 多容器 Pod 必須加 -c
  • 判斷標準:拿掉一個容器,另一個還能不能活

下一步

到這裡你會單一 Pod、會多容器 Pod、會排錯。但 kubectl 其實還有很多進階技巧 — 下一篇講kubectl 進階:port-forward、dry-run、--watch,三招會了就跟一般教學程度甩開。