Kubernetes Init容器

2022-04-29 13:43 更新

Init 容器

本頁(yè)提供了 Init 容器的概覽。Init 容器是一種特殊容器,在 Pod 內(nèi)的應(yīng)用容器啟動(dòng)之前運(yùn)行。Init 容器可以包括一些應(yīng)用鏡像中不存在的實(shí)用工具和安裝腳本。

你可以在 Pod 的規(guī)約中與用來(lái)描述應(yīng)用容器的 ?containers ?數(shù)組平行的位置指定 Init 容器。

理解 Init 容器

每個(gè) Pod 中可以包含多個(gè)容器, 應(yīng)用運(yùn)行在這些容器里面,同時(shí) Pod 也可以有一個(gè)或多個(gè)先于應(yīng)用容器啟動(dòng)的 Init 容器。

Init 容器與普通的容器非常像,除了如下兩點(diǎn):

  • 它們總是運(yùn)行到完成。
  • 每個(gè)都必須在下一個(gè)啟動(dòng)之前成功完成。

如果 Pod 的 Init 容器失敗,kubelet 會(huì)不斷地重啟該 Init 容器直到該容器成功為止。 然而,如果 Pod 對(duì)應(yīng)的 ?restartPolicy ?值為 "Never",并且 Pod 的 Init 容器失敗, 則 Kubernetes 會(huì)將整個(gè) Pod 狀態(tài)設(shè)置為失敗。

為 Pod 設(shè)置 Init 容器需要在 Pod 規(guī)約 中添加 ?initContainers ?字段, 該字段以 Container 類型對(duì)象數(shù)組的形式組織,和應(yīng)用的 ?containers ?數(shù)組同級(jí)相鄰。 參閱 API 參考的容器章節(jié)了解詳情。

Init 容器的狀態(tài)在 ?status.initContainerStatuses? 字段中以容器狀態(tài)數(shù)組的格式返回 (類似 ?status.containerStatuses? 字段)。

與普通容器的不同之處 

Init 容器支持應(yīng)用容器的全部字段和特性,包括資源限制、數(shù)據(jù)卷和安全設(shè)置。 然而,Init 容器對(duì)資源請(qǐng)求和限制的處理稍有不同,在下面資源節(jié)有說(shuō)明。

同時(shí) Init 容器不支持 ?lifecycle?、?livenessProbe?、?readinessProbe ?和 ?startupProbe?, 因?yàn)樗鼈儽仨氃?nbsp;Pod 就緒之前運(yùn)行完成。

如果為一個(gè) Pod 指定了多個(gè) Init 容器,這些容器會(huì)按順序逐個(gè)運(yùn)行。 每個(gè) Init 容器必須運(yùn)行成功,下一個(gè)才能夠運(yùn)行。當(dāng)所有的 Init 容器運(yùn)行完成時(shí), Kubernetes 才會(huì)為 Pod 初始化應(yīng)用容器并像平常一樣運(yùn)行。

使用 Init 容器

因?yàn)?nbsp;Init 容器具有與應(yīng)用容器分離的單獨(dú)鏡像,其啟動(dòng)相關(guān)代碼具有如下優(yōu)勢(shì):

  • Init 容器可以包含一些安裝過(guò)程中應(yīng)用容器中不存在的實(shí)用工具或個(gè)性化代碼。 例如,沒有必要僅為了在安裝過(guò)程中使用類似 ?sed?、?awk?、?python ?或 ?dig ?這樣的工具而去 ?FROM ?一個(gè)鏡像來(lái)生成一個(gè)新的鏡像。
  • Init 容器可以安全地運(yùn)行這些工具,避免這些工具導(dǎo)致應(yīng)用鏡像的安全性降低。
  • 應(yīng)用鏡像的創(chuàng)建者和部署者可以各自獨(dú)立工作,而沒有必要聯(lián)合構(gòu)建一個(gè)單獨(dú)的應(yīng)用鏡像。
  • Init 容器能以不同于 Pod 內(nèi)應(yīng)用容器的文件系統(tǒng)視圖運(yùn)行。因此,Init 容器可以訪問(wèn) 應(yīng)用容器不能訪問(wèn)的 Secret 的權(quán)限。
  • 由于 Init 容器必須在應(yīng)用容器啟動(dòng)之前運(yùn)行完成,因此 Init 容器 提供了一種機(jī)制來(lái)阻塞或延遲應(yīng)用容器的啟動(dòng),直到滿足了一組先決條件。 一旦前置條件滿足,Pod 內(nèi)的所有的應(yīng)用容器會(huì)并行啟動(dòng)。

示例 

下面是一些如何使用 Init 容器的想法:

  • 等待一個(gè) Service 完成創(chuàng)建,通過(guò)類似如下 shell 命令:
  • for i in {1..100}; do sleep 1; if dig myservice; then exit 0; fi; done; exit 1
    
  • 注冊(cè)這個(gè) Pod 到遠(yuǎn)程服務(wù)器,通過(guò)在命令中調(diào)用 API,類似如下: 
  • curl -X POST http://$MANAGEMENT_SERVICE_HOST:$MANAGEMENT_SERVICE_PORT/register \
      -d 'instance=$(<POD_NAME>)&ip=$(<POD_IP>)'
  • 在啟動(dòng)應(yīng)用容器之前等一段時(shí)間,使用類似命令:
  • sleep 60
    
  • 克隆 Git 倉(cāng)庫(kù)到卷中。
  • 將配置值放到配置文件中,運(yùn)行模板工具為主應(yīng)用容器動(dòng)態(tài)地生成配置文件。 例如,在配置文件中存放 POD_IP 值,并使用 Jinja 生成主應(yīng)用配置文件。

使用 Init 容器的情況

下面的例子定義了一個(gè)具有 2 個(gè) Init 容器的簡(jiǎn)單 Pod。 第一個(gè)等待 ?myservice ?啟動(dòng), 第二個(gè)等待 ?mydb ?啟動(dòng)。 一旦這兩個(gè) Init容器 都啟動(dòng)完成,Pod 將啟動(dòng) ?spec ?節(jié)中的應(yīng)用容器。

apiVersion: v1
kind: Pod
metadata:
  name: myapp-pod
  labels:
    app: 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"]

你通過(guò)運(yùn)行下面的命令啟動(dòng) Pod:

kubectl apply -f myapp.yaml

輸出類似于:

pod/myapp-pod created

使用下面的命令檢查其狀態(tài):

kubectl get -f myapp.yaml

輸出類似于:

NAME        READY     STATUS     RESTARTS   AGE
myapp-pod   0/1       Init:0/2   0          6m

或者查看更多詳細(xì)信息:

kubectl describe -f myapp.yaml

輸出類似于:

Name:          myapp-pod
Namespace:     default
[...]
Labels:        app=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 with docker id 5ced34a04634; Security:[seccomp=unconfined]
  13s          13s         1        {kubelet 172.17.4.201}    spec.initContainers{init-myservice}     Normal        Started       Started container with docker id 5ced34a04634

如需查看 Pod 內(nèi) Init 容器的日志,請(qǐng)執(zhí)行:

kubectl logs myapp-pod -c init-myservice # 查看第一個(gè) Init 容器
kubectl logs myapp-pod -c init-mydb      # 查看第二個(gè) Init 容器

在這一刻,Init 容器將會(huì)等待至發(fā)現(xiàn)名稱為 ?mydb ?和 ?myservice ?的 Service。

如下為創(chuàng)建這些 Service 的配置文件:

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

創(chuàng)建 ?mydb ?和 ?myservice ?服務(wù)的命令:

kubectl create -f services.yaml

輸出類似于:

service "myservice" created
service "mydb" created

這樣你將能看到這些 Init 容器執(zhí)行完畢,隨后 ?my-app? 的 Pod 進(jìn)入 ?Running ?狀態(tài):

kubectl get -f myapp.yaml

輸出類似于:

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

具體行為

在 Pod 啟動(dòng)過(guò)程中,每個(gè) Init 容器會(huì)在網(wǎng)絡(luò)和數(shù)據(jù)卷初始化之后按順序啟動(dòng)。 kubelet 運(yùn)行依據(jù) Init 容器在 Pod 規(guī)約中的出現(xiàn)順序依次運(yùn)行之。

每個(gè) Init 容器成功退出后才會(huì)啟動(dòng)下一個(gè) Init 容器。 如果某容器因?yàn)槿萜鬟\(yùn)行時(shí)的原因無(wú)法啟動(dòng),或以錯(cuò)誤狀態(tài)退出,kubelet 會(huì)根據(jù) Pod 的 ?restartPolicy ?策略進(jìn)行重試。 然而,如果 Pod 的 ?restartPolicy ?設(shè)置為 "Always",Init 容器失敗時(shí)會(huì)使用 ?restartPolicy ?的 "OnFailure" 策略。

在所有的 Init 容器沒有成功之前,Pod 將不會(huì)變成 ?Ready ?狀態(tài)。 Init 容器的端口將不會(huì)在 Service 中進(jìn)行聚集。正在初始化中的 Pod 處于 ?Pending ?狀態(tài), 但會(huì)將狀況 ?Initializing ?設(shè)置為 false。

如果 Pod 重啟,所有 Init 容器必須重新執(zhí)行。

對(duì) Init 容器規(guī)約的修改僅限于容器的 ?image ?字段。 更改 Init 容器的 ?image ?字段,等同于重啟該 Pod。

因?yàn)?nbsp;Init 容器可能會(huì)被重啟、重試或者重新執(zhí)行,所以 Init 容器的代碼應(yīng)該是冪等的。 特別地,基于 ?emptyDirs ?寫文件的代碼,應(yīng)該對(duì)輸出文件可能已經(jīng)存在做好準(zhǔn)備。

Init 容器具有應(yīng)用容器的所有字段。然而 Kubernetes 禁止使用 ?readinessProbe?, 因?yàn)?nbsp;Init 容器不能定義不同于完成態(tài)(Completion)的就緒態(tài)(Readiness)。 Kubernetes 會(huì)在校驗(yàn)時(shí)強(qiáng)制執(zhí)行此檢查。

在 Pod 上使用 ?activeDeadlineSeconds ?和在容器上使用 ?livenessProbe ?可以避免 Init 容器一直重復(fù)失敗。 ?activeDeadlineSeconds ?時(shí)間包含了 Init 容器啟動(dòng)的時(shí)間。 但建議僅在團(tuán)隊(duì)將其應(yīng)用程序部署為 Job 時(shí)才使用 ?activeDeadlineSeconds?, 因?yàn)?nbsp;?activeDeadlineSeconds ?在 Init 容器結(jié)束后仍有效果。 如果你設(shè)置了 ?activeDeadlineSeconds?,已經(jīng)在正常運(yùn)行的 Pod 會(huì)被殺死。

在 Pod 中的每個(gè)應(yīng)用容器和 Init 容器的名稱必須唯一; 與任何其它容器共享同一個(gè)名稱,會(huì)在校驗(yàn)時(shí)拋出錯(cuò)誤。

資源

在給定的 Init 容器執(zhí)行順序下,資源使用適用于如下規(guī)則:

  • 所有 Init 容器上定義的任何特定資源的 limit 或 request 的最大值,作為 Pod 有效初始 request/limit。 如果任何資源沒有指定資源限制,這被視為最高限制。
  • Pod 對(duì)資源的 有效 limit/request 是如下兩者的較大者:
    • 所有應(yīng)用容器對(duì)某個(gè)資源的 limit/request 之和
    • 對(duì)某個(gè)資源的有效初始 limit/request
  • 基于有效 limit/request 完成調(diào)度,這意味著 Init 容器能夠?yàn)槌跏蓟^(guò)程預(yù)留資源, 這些資源在 Pod 生命周期過(guò)程中并沒有被使用。
  • Pod 的 有效 QoS 層 ,與 Init 容器和應(yīng)用容器的一樣。

配額和限制適用于有效 Pod 的請(qǐng)求和限制值。 Pod 級(jí)別的 cgroups 是基于有效 Pod 的請(qǐng)求和限制值,和調(diào)度器相同。

Pod 重啟的原因 

Pod 重啟會(huì)導(dǎo)致 Init 容器重新執(zhí)行,主要有如下幾個(gè)原因:

  • Pod 的基礎(chǔ)設(shè)施容器 (譯者注:如 ?pause ?容器) 被重啟。這種情況不多見, 必須由具備 root 權(quán)限訪問(wèn)節(jié)點(diǎn)的人員來(lái)完成。
  • 當(dāng) ?restartPolicy ?設(shè)置為 "?Always?",Pod 中所有容器會(huì)終止而強(qiáng)制重啟。 由于垃圾收集機(jī)制的原因,Init 容器的完成記錄將會(huì)丟失。

當(dāng) Init 容器的鏡像發(fā)生改變或者 Init 容器的完成記錄因?yàn)槔占仍虮粊G失時(shí), Pod 不會(huì)被重啟。這一行為適用于 Kubernetes v1.20 及更新版本。如果你在使用較早 版本的 Kubernetes,可查閱你所使用的版本對(duì)應(yīng)的文檔。


以上內(nèi)容是否對(duì)您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號(hào)
微信公眾號(hào)

編程獅公眾號(hào)