iT邦幫忙

2026 iThome 鐵人賽

DAY 18
0

前言

昨天我們學了 Init Container —— Pod 啟動前的初始化機制。

Init Container 的特性是「完成任務後就退出」,主要負責在主要 Container 啟動前,先把必要的準備工作做好。

但如果有些工作不是一次性的,而是需要一個 Container 持續待在旁邊提供支援呢?

例如:

  • 持續收集 Log
  • 持續代理網路流量
  • 持續同步設定檔
  • 持續監控或協助主要 Container

這時候,就需要用到今天的主角:Sidecar Container —— Pod 的最佳副手

今天我們來學:

  1. 核心概念 — 什麼是 Sidecar Container?
  2. 用比喻理解 — 主廚與副手
  3. 傳統 Sidecar vs 原生 Sidecar — 兩者有什麼差別?
  4. 原生 Sidecar 的核心設定restartPolicy: Always
  5. 實作練習 — 傳統 Sidecar與原生 Sidecar 對比

以下操作皆在 master 節點 執行。


一、核心概念

什麼是 Sidecar Pattern?

Sidecar Pattern 是一種容器設計模式:在同一個 Pod 裡,除了主要的應用 Container 之外,再放一個或多個輔助 Container(Sidecar),負責處理和主程式相關、但不屬於核心業務邏輯的工作。

例如:

  • 收集 Log
  • 代理網路流量
  • 同步設定檔
  • 提供監控或輔助功能

因為 Sidecar 和主要 Container 位於同一個 Pod,所以它們可以共享:

  • 網路:使用同一個 Pod IP,也可以透過 localhost 互相通訊
  • 儲存:可以掛載相同的 Volume,共享檔案或資料
  • Pod 生命週期:會隨著同一個 Pod 一起被建立與移除

常見使用場景

場景 主要 Container Sidecar Container 說明
Log 收集 應用程式 Fluentd / Fluent Bit Sidecar 讀取共享 Volume 中的 Log,並轉發到集中式日誌系統
網路代理 API Server Envoy 等 Proxy Sidecar 負責處理流量代理、mTLS、負載均衡或流量控制
Config Reload Nginx Config Watcher Sidecar 監看設定變化,並在需要時通知主要 Container 重新載入設定
資料同步 Web Server Git Sync Sidecar 定期從 Git 同步最新檔案到共享 Volume,提供給主要 Container 使用

Sidecar vs Init Container

在學原生 Sidecar 之前,先釐清一個容易搞混的概念:

特性 Init Container Sidecar Container
執行時機 主要 Container 啟動之前 啟動後會持續執行,並與主要 Container 一起運作
生命週期 完成任務後退出 通常會持續運行到主要 Container 結束
用途 初始化(下載設定、等待依賴服務就緒) 輔助運行(Log 收集、Proxy、監控)
執行方式 依序執行,前一個完成後才執行下一個 啟動後持續運行,不需要等 Sidecar 結束才啟動主要 Container

簡單來說:

  • Init Container:先把事情做完,再讓主要 Container 啟動
  • Sidecar Container:啟動後留在旁邊,持續協助主要 Container

二、用比喻理解

想像一間餐廳的廚房

  • 主廚(Main Container):負責做菜,代表應用程式的核心業務
  • 副手(Sidecar Container):負責備料、洗碗、傳菜等輔助工作,讓主廚可以專心做菜
  • 廚房(Pod):主廚和副手在同一個空間裡工作,可以共享食材與設備

對應到 Kubernetes:

餐廳廚房 Kubernetes
主廚(做菜) Main Container(核心業務邏輯)
副手(備料、洗碗、傳菜) Sidecar Container(Log、Proxy、Sync 等輔助功能)
廚房 Pod
共用食材區 共享 Volume,例如 emptyDir
共用溝通空間 共享網路,可以透過 localhost 互相通訊

三、傳統 Sidecar vs 原生 Sidecar

傳統做法

在 Kubernetes 原生 Sidecar 機制出現之前,Sidecar Pattern 早就已經被廣泛使用。

傳統做法是:直接在 spec.containers[] 裡放入多個 Container,其中一個是主要 Container,另一個則負責輔助工作。

例如:

spec:
  containers:
  - name: main-app        # 主容器
    image: nginx
  - name: log-collector   # Sidecar(但 K8s 不知道它是 sidecar)
    image: busybox

傳統 Sidecar 的問題:

  • Kubernetes 不知道誰是主角、誰是副手spec.containers[] 裡的 Container 本質上都是一般 Container
  • Pod 結束時,Kubernetes 不會特別保證 Sidecar 一定比主要 Container 晚停止,因此可能影響最後一段 Log 或清理工作
  • 在 Job / CronJob 場景中,如果主要 Container 已經完成,但 Sidecar 仍持續執行,Pod 就無法順利進入完成狀態

原生 Sidecar(K8s 1.28+ — KEP-753)

Kubernetes 1.28 引入了原生 Sidecar Container 的概念。

做法很巧妙:不是新增一個 sidecarContainers 欄位,而是讓 initContainers 支援 restartPolicy: Always

spec:
  initContainers:
  - name: log-collector          # 原生 Sidecar
    image: busybox
    restartPolicy: Always        # 關鍵!讓 Container 持續運行
    command: ["sh", "-c", "tail -f /var/log/app.log"]

  containers:
  - name: main-app               # 主要 Container
    image: nginx

💡 為什麼放在 initContainers 裡?

因為 initContainers 本身有啟動順序,可以讓 Sidecar 在主要 Container 之前先啟動。

加上:

restartPolicy: Always

之後,這個 Container 不會像一般 Init Container 一樣「完成後退出」,而是會持續執行。

如果需要確保 Sidecar 真的準備完成後才啟動主要 Container,還可以搭配 startupProbe

這種設計很適合需要先啟動輔助服務,再啟動主要應用程式的場景。

對照表

特性 傳統 Sidecar 原生 Sidecar
定義位置 spec.containers[] spec.initContainers[] + restartPolicy: Always
啟動順序 Kubernetes 不特別保證 Sidecar 比主要 Container 先啟動 可以在主要 Container 之前啟動
終止順序 沒有 Sidecar 專屬的終止順序保證 主要 Container 結束後,Sidecar 才會被終止
Job 完成判定 Sidecar 持續執行時,可能讓 Job 無法正常完成 Sidecar 不會阻止 Job 在主要 Container 完成後結束
Probe 支援 支援 支援 startupProbe / livenessProbe / readinessProbe
Kubernetes 是否知道它是 Sidecar 不知道,只視為一般 Container 有原生 Sidecar 的生命週期語意

💡 原生 Sidecar 解決的核心痛點

  1. 啟動順序 —— Sidecar 可以先啟動,再啟動主要 Container
  2. 終止順序 —— 主要 Container 先結束,Sidecar 最後再關閉
  3. Job 相容 —— 主要 Container 完成後,不會因為 Sidecar 持續運行而讓 Job 卡住

四、原生 Sidecar 欄位詳解

完整範例

apiVersion: v1
kind: Pod
metadata:
  name: sidecar-demo
spec:
  initContainers:
  - name: log-sidecar
    image: busybox:1.36
    restartPolicy: Always
    command: ["sh", "-c", "tail -f /var/log/app/app.log"]
    volumeMounts:
    - name: log-volume
      mountPath: /var/log/app
    startupProbe:
      exec:
        command: ["test", "-f", "/var/log/app/app.log"]
      periodSeconds: 5
  containers:
  - name: main-app
    image: busybox:1.36
    command: ["sh", "-c", "for i in $(seq 1 10); do echo \"$(date) Log entry $i\" >> /var/log/app/app.log; sleep 2; done; echo 'Main app done'"]
    volumeMounts:
    - name: log-volume
      mountPath: /var/log/app
  volumes:
  - name: log-volume
    emptyDir: {}

關鍵欄位

欄位 說明
restartPolicy: Always 放在 initContainers 中的關鍵設定,讓這個 Container 持續運行,而不是完成後退出,形成原生 Sidecar
startupProbe 用來確認 Sidecar 是否已經成功啟動;在 Probe 通過前,後續的 Init Container 或主要 Container 不會繼續啟動
volumeMounts 可以透過共享 Volume(例如 emptyDir)與主要 Container 交換檔案或資料

五、實作練習

練習 1:傳統 Sidecar — Nginx + Log Tail

先用傳統方式實作一個最基本的 Sidecar:Nginx 寫 access log,Sidecar 負責即時讀取。

vim traditional-sidecar.yaml
apiVersion: v1
kind: Pod
metadata:
  name: traditional-sidecar
spec:
  containers:
  - name: nginx
    image: nginx
    volumeMounts:
    - name: log-volume
      mountPath: /var/log/nginx
  - name: log-reader
    image: busybox:1.36
    command: ["sh", "-c", "tail -f /var/log/nginx/access.log"]
    volumeMounts:
    - name: log-volume
      mountPath: /var/log/nginx
  volumes:
  - name: log-volume
    emptyDir: {}
kubectl apply -f traditional-sidecar.yaml
kubectl get pods traditional-sidecar

觀察 Sidecar 讀取 log:

# 在另一個終端製造一些 access log
kubectl exec traditional-sidecar -c nginx -- curl -s localhost > /dev/null

# 查看 log-reader sidecar 的輸出
kubectl logs traditional-sidecar -c log-reader

https://ithelp.ithome.com.tw/upload/images/20260819/20181928ry7HxUzrB8.png

💡 注意 -c 參數

當 Pod 裡有多個 Container 時,使用 kubectl logskubectl exec 時,可以透過 -c <container-name> 指定要操作哪一個 Container。

練習 2:原生 Sidecar — restartPolicy: Always

現在用原生 Sidecar 改寫,重點是觀察定義方式Pod 結構的差異。

這次我們讓 Sidecar 做一件簡單的事 — 每 5 秒印出一行 heartbeat,不依賴主容器的任何資源:

vim native-sidecar.yaml
apiVersion: v1
kind: Pod
metadata:
  name: native-sidecar
spec:
  initContainers:
  - name: log-sidecar               # 原生 Sidecar
    image: busybox:1.36
    restartPolicy: Always            # 關鍵:讓它持續運行
    command: ["sh", "-c", "while true; do echo \"[$(date)] sidecar is running\"; sleep 5; done"]
  containers:
  - name: nginx                      # 主容器
    image: nginx
kubectl apply -f native-sidecar.yaml
kubectl get pods native-sidecar

https://ithelp.ithome.com.tw/upload/images/20260819/20181928KJVQLXROwV.png

💡 和練習 1 對比一下結構上的不同

練習 1(傳統 Sidecar):主要 Container 和 Sidecar 都寫在 spec.containers[] 裡,Kubernetes 會把它們都視為一般 Container。

練習 2(原生 Sidecar):Sidecar 寫在 spec.initContainers[] 裡,並加上:

restartPolicy: Always

這樣 Kubernetes 就會套用原生 Sidecar 的生命週期行為。

Step 1:觀察 Pod 狀態

kubectl describe pod native-sidecar

https://ithelp.ithome.com.tw/upload/images/20260819/20181928OAwgqLhr5R.png

會看到:

  • log-sidecar 出現在 Init Containers 區段,而且狀態是 Running
  • nginx 出現在 Containers 區段
  • log-sidecar 會先啟動,再讓主要 Container nginx 接著啟動

這表示 log-sidecar 雖然定義在 initContainers 裡,但因為設定了 restartPolicy: Always,所以不會像一般 Init Container 一樣完成後退出,而是會持續運行。

Step 2:查看 Sidecar 的 log 輸出

kubectl logs native-sidecar -c log-sidecar

會看到每 5 秒一行的 heartbeat,代表這個 init container 並沒有跑完就退出,而是持續運行中:

https://ithelp.ithome.com.tw/upload/images/20260819/20181928buikgDOElY.png

練習 3:觀察 Job 場景的差異

這是原生 Sidecar 最大的優勢之一 — 主容器完成後,Pod 可以正常結束:

vim sidecar-job.yaml
apiVersion: batch/v1
kind: Job
metadata:
  name: sidecar-job
spec:
  template:
    spec:
      initContainers:
      - name: log-sidecar
        image: busybox:1.36
        restartPolicy: Always
        command: ["sh", "-c", "while true; do echo '[sidecar] watching...'; sleep 5; done"]
      containers:
      - name: worker
        image: busybox:1.36
        command: ["sh", "-c", "echo 'Working...'; sleep 10; echo 'Job done!'"]
      restartPolicy: Never
kubectl apply -f sidecar-job.yaml

# 觀察 — Job 應該在 worker 完成後正常結束
watch -n 2 kubectl get pods -l job-name=sidecar-job

https://ithelp.ithome.com.tw/upload/images/20260819/20181928dhw3Hxw0Tf.png

會看到:

  • worker Container 執行約 10 秒後完成
  • log-sidecar(原生 Sidecar)接著被終止
  • Pod 最後進入 Completed

💡 如果用傳統 Sidecar 做同樣的事?

如果 log-sidecar 是放在 spec.containers[] 裡的傳統 Sidecar,即使 worker 已經完成,Sidecar 仍可能持續執行。

這樣 Pod 就無法進入 Completed,Job 也會一直等待。

原生 Sidecar 可以配合 Job 的生命週期,在主要 Container 完成後由 Kubernetes 處理 Sidecar 的終止,改善傳統 Sidecar 在 Job 場景中的生命週期問題。


小結

今天我們學了 Sidecar Container —— Pod 內多 Container 協作的核心模式:

重點 說明
Sidecar Pattern 主要 Container + 輔助 Container 在同一個 Pod 中,共享網路和儲存
常見用途 Log 收集、網路代理、Config Reload、資料同步
傳統 Sidecar 放在 containers[],Kubernetes 只會把它視為一般 Container,生命週期控制較有限
原生 Sidecar 放在 initContainers[],搭配 restartPolicy: Always,具備原生 Sidecar 的啟動與終止生命週期
共享機制 可以透過 emptyDir 等 Volume 共享檔案,也能透過 localhost 互相通訊

到目前為止,我們的 Pod 大多都跑在 default Namespace 裡。

但當叢集裡的 Pod 越來越多,不同團隊、不同環境的資源要怎麼分類、隔離與管理

明天我們來學 Namespace —— Kubernetes 的資源隔離與多租戶管理,看看如何把叢集中的資源整理得更有層次!


參考資源


上一篇
Day 17|Init Container — Pod 啟動前的初始化機制
系列文
從零到 CKA:30 天掌握 Kubernetes 核心觀念與實作18
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言