Init 容器

本頁面概述了初始化容器:在 Pod 中,它們是先於應用容器執行的特殊容器。初始化容器可以包含應用映象中不存在的實用工具或安裝指令碼。

你可以在 Pod 規範中與 containers 陣列(用於描述應用容器)一起指定初始化容器。

在 Kubernetes 中,邊車容器是在主應用容器之前啟動並**持續執行**的容器。本文件介紹的是初始化容器:在 Pod 初始化期間執行並完成的容器。

理解初始化容器

一個 Pod 中可以有多個執行應用的容器,但它也可以有一個或多個初始化容器,這些容器在應用容器啟動之前執行。

初始化容器與常規容器完全相同,除了:

  • 初始化容器總是執行到完成。
  • 每個初始化容器都必須成功完成,然後下一個初始化容器才能啟動。

如果 Pod 的初始化容器失敗,kubelet 會反覆重啟該初始化容器,直到它成功。但是,如果 Pod 的 restartPolicy 為 Never,並且一個初始化容器在 Pod 啟動期間失敗,Kubernetes 會將整個 Pod 視為失敗。

要為 Pod 指定初始化容器,請在 Pod 規範中新增 initContainers 欄位,它是一個 container 型別的陣列(類似於應用 containers 欄位及其內容)。有關更多詳細資訊,請參閱 API 參考中的 Container

初始化容器的狀態在 .status.initContainerStatuses 欄位中作為容器狀態的陣列返回(類似於 .status.containerStatuses 欄位)。

與常規容器的區別

初始化容器支援應用容器的所有欄位和功能,包括資源限制、和安全設定。然而,初始化容器的資源請求和限制的處理方式有所不同,如容器內的資源共享中所述。

常規初始化容器(換句話說:不包括邊車容器)不支援 lifecyclelivenessProbereadinessProbestartupProbe 欄位。初始化容器必須執行到完成,然後 Pod 才能就緒;邊車容器在 Pod 的生命週期內持續執行,並**支援**某些探針。有關邊車容器的更多詳細資訊,請參閱邊車容器

如果你為一個 Pod 指定了多個初始化容器,kubelet 會按順序執行每個初始化容器。每個初始化容器都必須成功,然後下一個才能執行。當所有初始化容器都執行到完成時,kubelet 會初始化 Pod 的應用容器並像往常一樣執行它們。

與邊車容器的區別

初始化容器在主應用容器啟動之前執行並完成其任務。與邊車容器不同,初始化容器不會與主容器一起持續執行。

初始化容器按順序執行到完成,主容器在所有初始化容器成功完成之前不會啟動。

初始化容器不支援 lifecyclelivenessProbereadinessProbestartupProbe,而邊車容器支援所有這些探針來控制其生命週期。

初始化容器與主應用容器共享相同的資源(CPU、記憶體、網路),但不會直接與它們互動。但是,它們可以使用共享捲進行資料交換。

使用初始化容器

由於初始化容器與應用容器使用不同的映象,它們在啟動相關程式碼方面具有一些優勢:

  • 初始化容器可以包含應用映象中不存在的實用工具或自定義設定程式碼。例如,在設定期間無需為了使用 sedawkpythondig 等工具而從另一個映象 FROM 構建映象。
  • 應用程式映象構建者和部署者可以獨立工作,無需共同構建單個應用程式映象。
  • 初始化容器可以以與同一 Pod 中的應用容器不同的檔案系統檢視執行。因此,它們可以訪問應用容器無法訪問的Secrets
  • 由於初始化容器在任何應用容器啟動之前執行到完成,因此它們提供了一種機制,可以在滿足一組先決條件之前阻止或延遲應用容器的啟動。一旦滿足先決條件,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 倉庫克隆到 中。

  • 將值放入配置檔案並執行模板工具,為主應用容器動態生成配置檔案。例如,將 POD_IP 值放入配置中,並使用 Jinja 生成主應用配置檔案。

使用中的初始化容器

此示例定義了一個簡單的 Pod,它有兩個初始化容器。第一個等待 myservice,第二個等待 mydb。一旦兩個初始化容器都完成,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 中初始化容器的日誌,請執行:

kubectl logs myapp-pod -c init-myservice # Inspect the first init container
kubectl logs myapp-pod -c init-mydb      # Inspect the second init container

此時,這些初始化容器將等待發現名為 mydbmyservice服務

以下是你可用於使這些服務出現的配置:

---
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

要建立 mydbmyservice 服務:

kubectl apply -f services.yaml

輸出類似於:

service/myservice created
service/mydb created

然後你將看到這些初始化容器完成,並且 myapp-pod Pod 進入 Running 狀態。

kubectl get -f myapp.yaml

輸出類似於:

NAME        READY     STATUS    RESTARTS   AGE
myapp-pod   1/1       Running   0          9m

這個簡單的示例應該能為你建立自己的初始化容器提供一些啟發。下一步包含指向更詳細示例的連結。

詳細行為

在 Pod 啟動期間,kubelet 會延遲執行初始化容器,直到網路和儲存就緒。然後 kubelet 按照 Pod 規範中出現的順序執行 Pod 的初始化容器。

每個初始化容器都必須成功退出,然後下一個容器才能啟動。如果容器因執行時或以失敗退出而無法啟動,它將根據 Pod 的 restartPolicy 進行重試。但是,如果 Pod 的 restartPolicy 設定為 Always,則初始化容器使用 restartPolicy OnFailure。

在所有初始化容器都成功之前,Pod 無法變為 Ready。初始化容器上的埠不會聚合到 Service 下。正在初始化的 Pod 處於 Pending 狀態,但應將條件 Initialized 設定為 false。

如果 Pod 重啟,或被重啟,所有初始化容器都必須再次執行。

對初始化容器規範的更改僅限於容器映象欄位。直接修改初始化容器的 image 欄位**不會**重啟 Pod 或觸發其重建。如果 Pod 尚未啟動,該更改可能會對 Pod 的啟動方式產生影響。

對於Pod 模板,你通常可以更改初始化容器的任何欄位;進行此更改的影響取決於 Pod 模板的使用位置。

由於初始化容器可以被重啟、重試或重新執行,因此初始化容器程式碼應是冪等的。特別是,寫入任何 emptyDir 卷的程式碼應為輸出檔案可能已經存在的情況做好準備。

初始化容器具有應用容器的所有欄位。然而,Kubernetes 禁止使用 readinessProbe,因為初始化容器無法定義與完成不同的就緒狀態。這在驗證期間會強制執行。

在 Pod 上使用 activeDeadlineSeconds 以防止初始化容器永遠失敗。活動截止時間包括初始化容器。但是,建議僅在團隊將應用程式部署為 Job 時才使用 activeDeadlineSeconds,因為 activeDeadlineSeconds 即使在初始化容器完成之後仍然有效。如果你設定了 activeDeadlineSeconds,則已經正確執行的 Pod 將被殺死。

Pod 中每個應用和初始化容器的名稱必須是唯一的;任何與其他容器共享名稱的容器都會引發驗證錯誤。

容器內的資源共享

鑑於初始化容器、邊車容器和應用容器的執行順序,以下資源使用規則適用:

  • 所有初始化容器上定義的任何特定資源請求或限制的最高值是**有效初始化請求/限制**。如果任何資源未指定資源限制,則將其視為最高限制。
  • Pod 的資源**有效請求/限制**是以下兩者中的較高者:
    • 所有應用容器的資源請求/限制總和
    • 資源的有效初始化請求/限制
  • 排程基於有效請求/限制進行,這意味著初始化容器可以為初始化保留資源,這些資源在 Pod 的生命週期內不會被使用。
  • Pod 的**有效 QoS 等級**的 QoS 等級對於初始化容器和應用容器都是相同的。

配額和限制基於有效的 Pod 請求和限制進行應用。

初始化容器和 Linux cgroup

在 Linux 上,Pod 級別控制組 (cgroup) 的資源分配基於有效的 Pod 請求和限制,與排程器相同。

Pod 重啟原因

Pod 可以重啟,導致初始化容器重新執行,原因如下:

  • Pod 基礎設施容器重啟。這種情況不常見,需要具有節點根訪問許可權的人員才能執行。
  • Pod 中的所有容器都已終止,而 restartPolicy 設定為 Always,強制重啟,並且初始化容器完成記錄因垃圾回收而丟失。

當初始化容器映象發生更改時,或者初始化容器完成記錄因垃圾回收而丟失時,Pod 不會重新啟動。這適用於 Kubernetes v1.20 及更高版本。如果你使用的是更早的 Kubernetes 版本,請查閱你正在使用的版本的文件。

下一步

瞭解更多資訊:

上次修改於 2024 年 9 月 18 日上午 8:41 PST:38271 - 初始化容器概念清晰度 (27779ce888)