一個 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 / app | Fluentd / Filebeat |
| 流量代理 | app | Envoy(Istio Service Mesh) |
| 監控指標 | app | Prometheus Exporter |
同一個 Pod 的容器共享什麼?
1. 共享網路(同一個 IP)
- 兩個容器看到的 IP 都一樣
- 互相用
localhost通訊 - nginx 監聽 80 → Sidecar 用
localhost:80連它
- 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 操作必須加-c:exec、logs都要用-c指定哪個容器,不然 K8s 會問你選哪個。
多容器 Pod vs 多個獨立 Pod
判斷標準很簡單:拿掉一個容器,另一個還能不能正常工作?
| 多容器 Pod | 多個獨立 Pod | |
|---|---|---|
| 何時用 | 緊密耦合 | 獨立運作 |
| 網路 | 共享 IP,用 localhost | 各自有 IP |
| 擴縮容 | 一起擴一起縮 | 獨立擴縮 |
| 生命週期 | 一起生一起死 | 各自獨立 |
| 範例 | nginx + log collector | nginx + mysql |
- nginx + 日誌收集器:日誌收集器拿掉 → nginx 還能跑;nginx 拿掉 → 日誌收集器沒事做。緊密耦合,同 Pod。
- nginx + MySQL:互相獨立,且 nginx 可能擴 5 個但 MySQL 不要擴。拆開放兩個 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,三招會了就跟一般教學程度甩開。