初始化容器
本頁面提供初始化容器的總覽:在 Pod 中的應用程式容器之前執行的特殊化容器。初始化容器可以包含應用程式映像檔中不存在的公用程式或設定腳本。
您可以在 Pod 規格中與 containers
陣列 (描述應用程式容器) 一起指定初始化容器。
在 Kubernetes 中,Sidecar 容器是一種在主要應用程式容器之前啟動並持續執行的容器。本文件是關於初始化容器:在 Pod 初始化期間執行完成的容器。
了解初始化容器
Pod 可以有多個容器在其中執行應用程式,但它也可以有一個或多個初始化容器,這些容器在應用程式容器啟動之前執行。
初始化容器與一般容器完全相同,除了
- 初始化容器總是執行到完成。
- 每個初始化容器必須成功完成,下一個容器才能啟動。
如果 Pod 的初始化容器失敗,kubelet 會重複重新啟動該初始化容器,直到成功為止。但是,如果 Pod 的 restartPolicy
為 Never,且初始化容器在該 Pod 啟動期間失敗,Kubernetes 會將整個 Pod 視為失敗。
若要為 Pod 指定初始化容器,請將 initContainers
欄位新增至 Pod 規格,作為 container
項目陣列 (類似於應用程式 containers
欄位及其內容)。請參閱 API 參考中的 Container 以取得更多詳細資訊。
初始化容器的狀態會在 .status.initContainerStatuses
欄位中以容器狀態陣列 (類似於 .status.containerStatuses
欄位) 傳回。
與一般容器的差異
初始化容器支援應用程式容器的所有欄位與功能,包括資源限制、磁碟區 和安全性設定。但是,初始化容器的資源請求與限制處理方式不同,如 容器內的資源共用 中所述。
一般初始化容器 (換句話說:不包括 Sidecar 容器) 不支援 lifecycle
、livenessProbe
、readinessProbe
或 startupProbe
欄位。初始化容器必須在 Pod 準備就緒之前執行完成;Sidecar 容器在 Pod 的生命週期內持續執行,且確實支援某些探測。請參閱 Sidecar 容器 以取得關於 Sidecar 容器的更多詳細資訊。
如果您為 Pod 指定多個初始化容器,kubelet 會循序執行每個初始化容器。每個初始化容器都必須成功,下一個才能執行。當所有初始化容器都執行完成後,kubelet 會初始化 Pod 的應用程式容器,並照常執行它們。
與 Sidecar 容器的差異
初始化容器在主要應用程式容器啟動之前執行並完成其任務。與 Sidecar 容器 不同,初始化容器不會與主要容器一起持續執行。
初始化容器循序執行完成,且主要容器在所有初始化容器都成功完成之前不會啟動。
初始化容器不支援 lifecycle
、livenessProbe
、readinessProbe
或 startupProbe
,而 Sidecar 容器支援所有這些 探測 以控制其生命週期。
初始化容器與主要應用程式容器共用相同的資源 (CPU、記憶體、網路),但不直接與它們互動。但是,它們可以使用共用磁碟區進行資料交換。
使用初始化容器
由於初始化容器的映像檔與應用程式容器不同,因此它們在啟動相關程式碼方面具有一些優勢
- 初始化容器可以包含應用程式映像檔中不存在的公用程式或自訂程式碼以進行設定。例如,不需要為了在設定期間使用
sed
、awk
、python
或dig
等工具而使映像檔FROM
另一個映像檔。 - 應用程式映像檔建構者和部署者角色可以獨立工作,而無需聯合建構單一應用程式映像檔。
- 初始化容器可以與相同 Pod 中的應用程式容器使用不同的檔案系統檢視。因此,它們可以被授予對應用程式容器無法存取的 密鑰 的存取權。
- 由於初始化容器在任何應用程式容器啟動之前執行完成,因此初始化容器提供了一種機制來阻止或延遲應用程式容器啟動,直到滿足一組先決條件。一旦滿足先決條件,Pod 中的所有應用程式容器都可以平行啟動。
- 初始化容器可以安全地執行公用程式或自訂程式碼,否則這些程式碼會使應用程式容器映像檔變得較不安全。透過將不必要的工具分開,您可以限制應用程式容器映像檔的攻擊面。
範例
以下是一些關於如何使用初始化容器的想法
等待 Service 被建立,使用如下的 shell 單行命令:
for i in {1..100}; do sleep 1; if nslookup myservice; then exit 0; fi; done; exit 1
使用如下命令,透過 Downward API 向遠端伺服器註冊此 Pod:
curl -X POST http://$MANAGEMENT_SERVICE_HOST:$MANAGEMENT_SERVICE_PORT/register -d 'instance=$(<POD_NAME>)&ip=$(<POD_IP>)'
等待一段時間後,使用如下命令啟動應用程式容器:
sleep 60
將 Git 儲存庫克隆到 Volume 中
將值放入組態檔,並執行範本工具以動態產生主要應用程式容器的組態檔。例如,將
POD_IP
值放入組態中,並使用 Jinja 產生主要應用程式組態檔。
正在使用的 Init 容器
此範例定義了一個簡單的 Pod,其中包含兩個 Init 容器。第一個等待 myservice
,第二個等待 mydb
。一旦兩個 Init 容器都完成,Pod 將從其 spec
區段運行應用程式容器。
apiVersion: v1
kind: Pod
metadata:
name: myapp-pod
labels:
app.kubernetes.io/name: MyApp
spec:
containers:
- name: myapp-container
image: busybox:1.28
command: ['sh', '-c', 'echo The app is running! && sleep 3600']
initContainers:
- name: init-myservice
image: busybox:1.28
command: ['sh', '-c', "until nslookup myservice.$(cat /var/run/secrets/kubernetes.io/serviceaccount/namespace).svc.cluster.local; do echo waiting for myservice; sleep 2; done"]
- name: init-mydb
image: busybox:1.28
command: ['sh', '-c', "until nslookup mydb.$(cat /var/run/secrets/kubernetes.io/serviceaccount/namespace).svc.cluster.local; do echo waiting for mydb; sleep 2; done"]
您可以透過運行以下命令來啟動此 Pod
kubectl apply -f myapp.yaml
輸出結果類似於此
pod/myapp-pod created
並使用以下命令檢查其狀態
kubectl get -f myapp.yaml
輸出結果類似於此
NAME READY STATUS RESTARTS AGE
myapp-pod 0/1 Init:0/2 0 6m
或查看更多詳細資訊
kubectl describe -f myapp.yaml
輸出結果類似於此
Name: myapp-pod
Namespace: default
[...]
Labels: app.kubernetes.io/name=MyApp
Status: Pending
[...]
Init Containers:
init-myservice:
[...]
State: Running
[...]
init-mydb:
[...]
State: Waiting
Reason: PodInitializing
Ready: False
[...]
Containers:
myapp-container:
[...]
State: Waiting
Reason: PodInitializing
Ready: False
[...]
Events:
FirstSeen LastSeen Count From SubObjectPath Type Reason Message
--------- -------- ----- ---- ------------- -------- ------ -------
16s 16s 1 {default-scheduler } Normal Scheduled Successfully assigned myapp-pod to 172.17.4.201
16s 16s 1 {kubelet 172.17.4.201} spec.initContainers{init-myservice} Normal Pulling pulling image "busybox"
13s 13s 1 {kubelet 172.17.4.201} spec.initContainers{init-myservice} Normal Pulled Successfully pulled image "busybox"
13s 13s 1 {kubelet 172.17.4.201} spec.initContainers{init-myservice} Normal Created Created container init-myservice
13s 13s 1 {kubelet 172.17.4.201} spec.initContainers{init-myservice} Normal Started Started container init-myservice
若要查看此 Pod 中 Init 容器的日誌,請運行
kubectl logs myapp-pod -c init-myservice # Inspect the first init container
kubectl logs myapp-pod -c init-mydb # Inspect the second init container
此時,這些 Init 容器將等待發現名為 mydb
和 myservice
的 Services。
以下是您可以使用的組態,使這些 Services 出現
---
apiVersion: v1
kind: Service
metadata:
name: myservice
spec:
ports:
- protocol: TCP
port: 80
targetPort: 9376
---
apiVersion: v1
kind: Service
metadata:
name: mydb
spec:
ports:
- protocol: TCP
port: 80
targetPort: 9377
若要建立 mydb
和 myservice
服務
kubectl apply -f services.yaml
輸出結果類似於此
service/myservice created
service/mydb created
然後您將看到這些 Init 容器完成,並且 myapp-pod
Pod 進入 Running 狀態
kubectl get -f myapp.yaml
輸出結果類似於此
NAME READY STATUS RESTARTS AGE
myapp-pod 1/1 Running 0 9m
這個簡單的範例應該為您創建自己的 Init 容器提供一些靈感。下一步 包含更詳細範例的連結。
詳細行為
在 Pod 啟動期間,kubelet 會延遲運行 Init 容器,直到網路和儲存準備就緒。然後 kubelet 按照它們在 Pod 的 spec 中出現的順序運行 Pod 的 Init 容器。
每個 Init 容器都必須成功退出,下一個容器才能啟動。如果容器因執行階段錯誤而啟動失敗或退出失敗,則會根據 Pod 的 restartPolicy
進行重試。但是,如果 Pod 的 restartPolicy
設定為 Always,則 Init 容器會使用 restartPolicy
OnFailure。
在所有 Init 容器都成功完成之前,Pod 無法處於 Ready
狀態。Init 容器上的埠不會在 Service 下彙總。正在初始化的 Pod 處於 Pending
狀態,但應將條件 Initialized
設定為 false。
如果 Pod 重新啟動,或被重新啟動,則所有 Init 容器都必須再次執行。
對 Init 容器 spec 的變更僅限於容器映像檔欄位。直接變更 Init 容器的 image
欄位不會重新啟動 Pod 或觸發其重新建立。如果 Pod 尚未啟動,則此變更可能會影響 Pod 的啟動方式。
對於 pod template,您通常可以變更 Init 容器的任何欄位;進行該變更的影響取決於 pod template 的使用位置。
由於 Init 容器可以重新啟動、重試或重新執行,因此 Init 容器程式碼應為等冪的。特別是,寫入任何 emptyDir
Volume 的程式碼應為輸出檔案已存在的可能性做好準備。
Init 容器具有應用程式容器的所有欄位。但是,Kubernetes 禁止使用 readinessProbe
,因為 Init 容器無法定義與完成不同的就緒狀態。這在驗證期間強制執行。
在 Pod 上使用 activeDeadlineSeconds
以防止 Init 容器永遠失敗。活動期限包括 Init 容器。但是,建議僅在團隊將其應用程式部署為 Job 時才使用 activeDeadlineSeconds
,因為即使在 initContainer 完成後,activeDeadlineSeconds
仍然有效。如果設定了 activeDeadlineSeconds
,則已正確運行的 Pod 將被終止。
Pod 中每個應用程式和 Init 容器的名稱必須是唯一的;對於與另一個容器共享名稱的任何容器,都會拋出驗證錯誤。
容器內部的資源共享
考慮到 Init 容器、Sidecar 容器和應用程式容器的執行順序,以下資源使用規則適用
- 在所有 Init 容器上定義的任何特定資源請求或限制的最高值是有效的 Init 請求/限制。如果任何資源沒有指定資源限制,則視為最高限制。
- Pod 的資源有效請求/限制是以下兩者中的較高者
- 所有應用程式容器的資源請求/限制總和
- 資源的有效 Init 請求/限制
- 排程是根據有效請求/限制完成的,這表示 Init 容器可以為初始化保留 Pod 生命周期中未使用的資源。
- Pod 的 QoS(服務品質)層級有效 QoS 層級是 Init 容器和應用程式容器的 QoS 層級。
配額和限制根據有效的 Pod 請求和限制應用。
Init 容器和 Linux cgroups
在 Linux 上,Pod 層級控制群組 (cgroups) 的資源分配基於有效的 Pod 請求和限制,與排程器相同。
Pod 重新啟動原因
Pod 可能會因以下原因重新啟動,導致重新執行 Init 容器
- Pod 基礎架構容器已重新啟動。這很不常見,必須由具有節點 Root 存取權限的人員完成。
- 當
restartPolicy
設定為 Always 時,Pod 中的所有容器都會終止,強制重新啟動,並且由於垃圾收集,Init 容器完成記錄已遺失。
當 Init 容器映像檔變更或 Init 容器完成記錄因垃圾收集而遺失時,Pod 將不會重新啟動。這適用於 Kubernetes v1.20 及更新版本。如果您使用的是較早版本的 Kubernetes,請查閱您正在使用的版本的說明文件。
下一步
深入了解以下內容
- 建立具有 Init 容器的 Pod.
- 偵錯 Init 容器.
- kubelet 和 kubectl 概觀。
- 探針類型:存活探針、就緒探針、啟動探針。
- Sidecar 容器.