昨天我們結束了排程系列(Day 12–16),從「Pod 該去哪裡」一路學到「資源不足時誰應該優先」。
但回頭想想,到目前為止我們討論的 Pod,大多都是啟動後就直接執行主要程式。
在真實的生產環境中,Pod 啟動前經常還需要先完成一些準備工作,例如:
這些「主程式啟動前的準備工作」,就可以交給 Init Container 來處理。
今天我們來學:
以下操作皆在 master 節點 執行。
Init Container 是 Pod 中一種特殊的 Container,會在主要 Container 啟動之前先執行。
Init Container 有以下幾個重要特性:
restartPolicy 處理;一般情況下會持續重試,直到成功完成簡單來說:
Init Container 先把準備工作做完,主要 Container 才正式開始執行。
你可能會想:「這些準備工作直接寫在主容器的啟動腳本裡不就好了?」
使用 Init Container 的好處主要有三個:
curl 下載設定檔,就不需要把 curl 額外安裝進 Nginx Image💡Pod 啟動流程
- Init Container 1 啟動 → 完成 → 退出
- Init Container 2 啟動 → 完成 → 退出
- ...依照定義順序執行
- 所有 Init Container 完成
- 主要 Container 啟動
想像一間餐廳開店前的準備工作:
對應到 Kubernetes:
| 餐廳準備工作 | Kubernetes |
|---|---|
| 採購員(買食材) | Init Container(下載設定檔 / 憑證) |
| 清潔員(打掃廚房) | Init Container(初始化資料目錄、設定權限) |
| 設備檢查員(檢查設備) | Init Container(等待相依服務就緒) |
| 主廚 + 服務生(正式營業) | 主要 Container(應用程式開始運行) |
💡 重點
每個 Init Container 完成自己的任務後就會結束,不會持續執行。
這就是 Init Container 的特色 —— 完成初始化後退出,而主要 Container 則會接著啟動並持續執行。
apiVersion: v1
kind: Pod
metadata:
name: myapp-pod
spec:
initContainers:
- name: init-download-config
image: busybox:1.36
command: ["sh", "-c", "wget -O /config/app.conf http://config-server/config"]
volumeMounts:
- name: config-volume
mountPath: /config
- name: init-wait-db
image: busybox:1.36
command: ["sh", "-c", "until nc -z mysql-service 3306; do echo waiting for db...; sleep 2; done"]
containers:
- name: myapp
image: myapp:latest
volumeMounts:
- name: config-volume
mountPath: /config
volumes:
- name: config-volume
emptyDir: {}
| 特性 | 說明 |
|---|---|
| 定義位置 | spec.initContainers[],與 spec.containers[] 位於同一層級 |
| 執行順序 | 多個 Init Container 會按照定義順序依序執行,不是並行 |
| 成功條件 | 前一個 Init Container 必須成功完成,下一個才會開始 |
| 失敗行為 | Init Container 執行失敗時,Kubernetes 會依 Pod 的 restartPolicy 決定是否重試 |
| Probe 支援 | 一般 Init Container 不支援 livenessProbe、readinessProbe、startupProbe |
| 資料共享 | 可以和主要 Container 掛載相同的 Volume,用來傳遞初始化後的檔案或資料 |
補充
Kubernetes 也支援在
initContainers中定義restartPolicy: Always的 Sidecar Container。這類 Container 會持續執行,生命週期和一般 Init Container 不同,也可以使用 Probe。
Init Container 的資源需求也會影響 Pod 的排程,但計算方式和主要 Container 不太一樣。
可以先簡單記成:
Pod 的有效資源請求 = Init Containers 中最大的請求 vs 主要 Containers 請求總和,兩者取較大值
而且 CPU、Memory 等資源會分開計算。
例如 CPU:
requests.cpu: 500m
requests.cpu: 200m
requests.cpu: 300m
計算方式:
Init Containers 最大值 = 500m
主要 Containers 總和 = 300m
所以:
Pod 有效 CPU Request = max(500m, 300m) = 500m
因為一般 Init Container 是依序執行的,不會同時執行,所以 Init Containers 之間不需要全部相加,而是取其中最大的資源需求。
Scheduler 會根據這個有效資源請求,判斷哪一台 Node 有足夠的資源可以執行這個 Pod。
這是最常見的 Init Container 使用場景 — 確保依賴的服務已經上線。
先建立一個會「等待」的 Pod:
vim init-wait-service.yaml
apiVersion: v1
kind: Pod
metadata:
name: init-wait-demo
labels:
app: init-wait-demo
spec:
initContainers:
- name: wait-for-myservice
image: busybox:1.36
command: ["sh", "-c", "until nslookup myservice.default.svc.cluster.local; do echo 'Waiting for myservice...'; sleep 3; done; echo 'myservice is ready!'"]
containers:
- name: main-app
image: nginx
kubectl apply -f init-wait-service.yaml
觀察 Pod 狀態:
kubectl get pods init-wait-demo -w
會看到 Pod 停在 Init:0/1 狀態 — 因為 myservice 還不存在。

查看 Init Container 的 log:
kubectl logs init-wait-demo -c wait-for-myservice
會看到不斷輸出 Waiting for myservice...。

現在建立那個 Service,讓 Init Container 通過:
vim myservice.yaml
apiVersion: v1
kind: Service
metadata:
name: myservice
spec:
ports:
- port: 80
protocol: TCP
kubectl apply -f myservice.yaml
再觀察 Pod 狀態:
kubectl get pods init-wait-demo -w


會看到:
myservice 已經可以解析後 → 成功完成並退出Init:0/1 → Running
💡 生產環境中的應用
假設你的 API Server 依賴 MySQL,如果 MySQL 還沒準備好,API Server 就直接啟動,可能會不斷出現連線失敗。
這時可以用 Init Container 先等待 MySQL 可以連線,再讓 API Server 啟動。
這樣就能避免應用程式在相依服務還沒準備完成時過早啟動。
Init Container 把設定檔下載到共享 Volume,主容器直接使用。
vim init-download-config.yaml
apiVersion: v1
kind: Pod
metadata:
name: init-config-demo
labels:
app: init-config-demo
spec:
initContainers:
- name: download-config
image: busybox:1.36
command:
- sh
- -c
- |
echo 'server {' > /config/default.conf
echo ' listen 80;' >> /config/default.conf
echo ' location / {' >> /config/default.conf
echo ' return 200 "Hello from Init Container config!\n";' >> /config/default.conf
echo ' }' >> /config/default.conf
echo '}' >> /config/default.conf
echo "Config file created successfully"
volumeMounts:
- name: config-volume
mountPath: /config
containers:
- name: nginx
image: nginx
volumeMounts:
- name: config-volume
mountPath: /etc/nginx/conf.d
volumes:
- name: config-volume
emptyDir: {}
kubectl apply -f init-download-config.yaml
kubectl get pods init-config-demo -w

等 Pod 變成 Running 後,驗證設定是否生效:
# 查看 Init Container 的 log
kubectl logs init-config-demo -c download-config
# 驗證 Nginx 是否使用了 Init Container 產生的設定
kubectl exec init-config-demo -- curl -s localhost
應該會看到:Hello from Init Container config!

💡 資料流動方向
Init Container(download-config)↓ 寫入設定檔
/config/default.conf↓ 透過
emptyDirVolume 共享
主要 Container(nginx)↓ 掛載並讀取
/etc/nginx/conf.d/default.conf這就是 Init Container 很常見的資料傳遞方式 —— 透過共享 Volume,把初始化產生的檔案交給主要 Container 使用。
觀察多個 Init Container 的執行順序:
vim init-multi-steps.yaml
apiVersion: v1
kind: Pod
metadata:
name: init-multi-demo
labels:
app: init-multi-demo
spec:
initContainers:
- name: step1-check-env
image: busybox:1.36
command: ["sh", "-c", "echo '[Step 1] Checking environment...' && sleep 3 && echo '[Step 1] Environment OK'"]
- name: step2-init-data
image: busybox:1.36
command:
- sh
- -c
- |
echo '[Step 2] Initializing data directory...'
mkdir -p /data/app
echo '{"initialized": true, "timestamp": "'$(date)'"}' > /data/app/init.json
sleep 2
echo '[Step 2] Data initialized'
volumeMounts:
- name: data-volume
mountPath: /data
- name: step3-final-check
image: busybox:1.36
command:
- sh
- -c
- |
echo '[Step 3] Final check...'
cat /data/app/init.json
echo '[Step 3] All checks passed!'
volumeMounts:
- name: data-volume
mountPath: /data
containers:
- name: main-app
image: busybox:1.36
command: ["sh", "-c", "echo 'Main app started!' && cat /data/app/init.json && sleep 3600"]
volumeMounts:
- name: data-volume
mountPath: /data
volumes:
- name: data-volume
emptyDir: {}
kubectl apply -f init-multi-steps.yaml
觀察 Init Container 依序啟動:
kubectl get pods init-multi-demo -w

會看到狀態變化:
Init:0/3 → Step 1 正在跑Init:1/3 → Step 1 完成,Step 2 正在跑Init:2/3 → Step 2 完成,Step 3 正在跑Running → 所有 Init Container 完成,主容器啟動查看每個 Init Container 的 log:
kubectl logs init-multi-demo -c step1-check-env
kubectl logs init-multi-demo -c step2-init-data
kubectl logs init-multi-demo -c step3-final-check
# 主容器的 log — 確認它有拿到 Init Container 準備的資料
kubectl logs init-multi-demo -c main-app

用 kubectl describe 觀察完整的啟動流程:
kubectl describe pod init-multi-demo
在 Events 區段你會看到每個 Init Container 依序啟動和結束的事件紀錄。

💡 觀察重點
三個 Init Container 不會同時啟動,而是依照定義順序依序執行:
Step 1 完成 → Step 2 啟動 → Step 2 完成 → Step 3 啟動Step 2 寫入的
init.json,Step 3 和主要 Container 都可以透過共享 Volume 讀取使用
kubectl describe pod <pod-name>查看時,可以在 Init Containers 區段看到已完成的 Init Container:State: Terminated Reason: Completed這表示該 Init Container 已經成功完成任務並退出。
如果 Init Container 執行失敗,Kubernetes 會根據 Pod 的 restartPolicy 決定後續行為:
| restartPolicy | Init Container 失敗時的行為 |
|---|---|
Always(預設) |
失敗的 Init Container 會被重新執行,直到成功 |
OnFailure |
失敗的 Init Container 會被重新執行,直到成功 |
Never |
不會重試,Pod 會被標記為 Failed |
💡 注意
Init Container 失敗時,通常是重新執行失敗的那一個,不是每次都從第一個 Init Container 重新開始。
不過 Init Container 仍可能因為 Pod 重新啟動或重建而再次執行,因此初始化操作最好設計成冪等(idempotent) —— 執行多次也不會造成錯誤或重複副作用。
我們來快速驗證一下:
vim init-fail-demo.yaml
apiVersion: v1
kind: Pod
metadata:
name: init-fail-demo
labels:
app: init-fail-demo
spec:
initContainers:
- name: will-fail
image: busybox:1.36
command: ["sh", "-c", "echo 'Init starting...' && exit 1"]
containers:
- name: main-app
image: nginx
kubectl apply -f init-fail-demo.yaml
kubectl get pods init-fail-demo -w

會看到 Pod 一直在 Init:CrashLoopBackOff 狀態 — Init Container 失敗 → 重試 → 失敗 → 重試,主容器永遠不會啟動。
# 查看失敗的 Init Container log
kubectl logs init-fail-demo -c will-fail
# 查看事件
kubectl describe pod init-fail-demo

| 場景 | Init Container 做什麼 | 範例 |
|---|---|---|
| 等待依賴服務 | 用迴圈確認 Service 是否可達,再讓主要 Container 啟動 | until nslookup db-service; do sleep 2; done |
| 下載設定檔 | 從遠端取得設定檔,寫入共享 Volume | wget -O /config/app.conf http://config-server/config |
| 初始化資料目錄 | 建立目錄、準備檔案結構或設定權限 | mkdir -p /data/app && chown 1000:1000 /data/app |
| 資料庫 Migration | 在應用程式啟動前先執行資料庫 Schema 更新 | python manage.py migrate |
| 等待外部系統 | 確認外部 API 或相依系統已經可以使用 | until curl -sf http://api-gateway/health; do sleep 5; done |
| 取得敏感設定 | 由 Init Container 從 Vault 等系統取得 Secret,再透過共享 Volume 提供給主要 Container 使用 | Init Container 負責取 Secret,主要 Container 只讀取結果 |
今天學了 Init Container —— 「完成任務後就退出」的初始化容器。
但有些工作不是一次性的,而是需要在主要 Container 執行期間持續在旁邊提供支援,例如:
這類 Container 就稱為 Sidecar Container。
先簡單比較:
| 特性 | Init Container | Sidecar Container |
|---|---|---|
| 執行時機 | 主要 Container 啟動之前 | 啟動後會與主要 Container 一起執行 |
| 生命週期 | 完成任務後退出 | 持續執行,通常伴隨 Pod 的生命週期 |
| 用途 | 初始化、準備工作 | 持續提供輔助功能 |
| 比喻 | 開店前的準備人員 | 營業期間一直在旁邊協助的副手 |
💡 先知道一個概念
Kubernetes 原生 Sidecar 的做法,是在
initContainers中設定:restartPolicy: Always一般 Init Container 完成後會退出;設定
restartPolicy: Always的 Sidecar 則會持續執行,並和主要 Container 一起工作。這項機制在 Kubernetes v1.28 以 Alpha 推出,目前已成為 Stable 功能。
明天我們會再深入了解 Sidecar Container 的啟動順序、生命週期與實際使用方式。
今天我們學了 Init Container —— Pod 啟動前的初始化機制:
| 重點 | 說明 |
|---|---|
| 定義位置 | spec.initContainers[],與主要 Container 位於同一層級 |
| 執行特性 | 依序執行,前一個成功完成後,下一個才會開始 |
| 資料傳遞 | 可以透過共享 Volume,例如 emptyDir,把初始化產生的資料交給主要 Container |
| 常見場景 | 等待依賴服務、下載設定檔、初始化資料目錄、執行 DB Migration |
| 失敗處理 | Init Container 失敗時,Kubernetes 會依 Pod 的 restartPolicy 決定是否重試 |
| 資源計算 | Init Containers 中最大的資源請求,會和主要 Containers 的資源請求總和比較,取較大的值 |
Init Container 負責的是「開店前的準備工作」—— 任務完成後就退出。
但如果有些工作不是一次性的,而是需要一個 Container 持續待在旁邊提供支援呢?
明天我們來學 Sidecar Container —— Pod 的最佳副手,看看它如何和主要 Container 一起運作,以及 Kubernetes 原生 Sidecar 的生命週期設計。