理解初始容器
一個pod里可以運行多個容器,它也可以運行一個或者多個初始容器,初始容器先於應用容器運行,除了以下兩點外,初始容器和普通容器沒有什么兩樣:
-
它們總是
run to completion
-
一個初始容器必須成功運行另一個才能運行
如果pod中的一個初始容器運行失敗,則kubernetes會嘗試重啟pod直到初始容器成功運行,如果pod的重啟策略設置為從不(never)
,則不會重啟.
創建容器時,在podspec里添加initContainers
字段,則指定容器即為初始容器,它們的返回狀態作為數組保存在.status.initContainerStatuses
里(與普通容器狀態存儲字段.status.containerStatuses
類似)
初始容器和普通容器的不同:
初始容器支持所有普通容器的特征,包括資源配額限制和存儲卷以及安全設置.但是對資源申請和限制處理初始容器略有不同,下面會介紹.此外,初始容器不支持可用性探針(readiness probe),因為它在ready
之前必須 run to completion
如果在一個pod里指定了多個初始容器,則它們會依次
啟動起來(pod內的普通容器並行啟動),並且只有上一個成功下一個才能啟動.當所有的初始容器都啟動了,kubernetes才開始啟普通應用容器.
初始容器能做什么
由於初始容器和普通應用容器是分開的鏡像,因此他在做一些初始化工作很有優勢:
-
它們可以包含並且運行一些出於安全考慮不適合和應用放在一塊的小工具.
-
它們可以一些小工具和自定義代碼來做些初始化工作,這樣就不需要在普通應用容器里使用
sed
,awk
,python
或者dig
來做初始化工作了 -
應用構建者和發布者可以獨立工作,而不必再聯合起來處理同一個pod
-
它們使用linux
namespaces
因此它們和普通應用pod擁有不同的文件系統視圖.因此他們可以被賦予普通應用容器獲取不到的secrets
-
它們在應用容器啟動前運行,因此它們可以阻止或者延緩普通應用容器的初始化直到需要的條件滿足
示例:
- 通過執行shell命令來等待一個服務創建完成,命令如下:
for i in {1..100}; do sleep 1; if dig 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應用環境布置指南
初始容器使用
apiVersion: v1
kind: Pod
metadata:
name: myapp-pod
labels:
app: myapp
spec:
containers:
- name: myapp-container
image: busybox
command: ['sh', '-c', 'echo The app is running! && sleep 3600']
initContainers:
- name: init-myservice
image: busybox
command: ['sh', '-c', 'until nslookup myservice; do echo waiting for myservice; sleep 2; done;']
- name: init-mydb
image: busybox
command: ['sh', '-c', 'until nslookup mydb; do echo waiting for mydb; sleep 2; done;']
以上pod定義包含兩個初始容器,第一個等待myservice
服務可用,第二個等待mydb
服務可用,這兩個pod執行完成,應用容器開始執行.
下面是myservice
和mydb
兩個服務的yaml文件
kind: Service
apiVersion: v1
metadata:
name: myservice
spec:
ports:
- protocol: TCP
port: 80
targetPort: 9376
---
kind: Service
apiVersion: v1
metadata:
name: mydb
spec:
ports:
- protocol: TCP
port: 80
targetPort: 9377
上面定義的pod可以通過以下使用初始化和調試
kubectl create -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
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
kubectl logs myapp-pod -c init-myservice # Inspect the first init container
kubectl logs myapp-pod -c init-mydb # Inspect the second init container
當我們啟動mydb
和myservice
兩個服務后,我們可以看到初始容器完成並且myapp-pod
pod被創建.
kubectl create -f services.yaml
service/myservice created
service/mydb created
kubectl get -f myapp.yaml
NAME READY STATUS RESTARTS AGE
myapp-pod 1/1 Running 0 9m
這些示例非常簡單但是應該能為你創建自己的初始容器提供一些靈感
行為細節
-
在啟動pod的過程中,在存儲卷和網絡創建以后,初始容器依次創建.上一個容器必須返回成功下一個才能啟動,如果由於運行時錯誤或者其它異常退出,它會依照
restartPolicy
來重試,然而,如果restartPolicy
設置為Always
,初始容器實際上使用的是OnFailure
策略 -
如果pod重啟了,則所有的初始容器要重新執行
-
對初始容器的
spec
的更改僅限於鏡像(image)
字段的修改,更改了初始容器的鏡像字段相當於重啟pod -
由於初始容器可以被重啟,重試和重新執行,因此它里面的代碼應當是冪等的,尤其是寫入文件到
EmptyDirs
的代碼應當注意文件可能已經存在 -
容器中的所有初始容器和普通容器名稱必須惟一.
資源
基於初始容器的執行順序,以下關於資源的規則適用:
-
對於特定資源,所有初始容器申請的最高的生效
-
對於pod,相同資源申請取以下兩者較高的一個:
- 所有普通應用容器申請的資源總和
- 初始容器申請的生效的資源(上面說到,初始容器申請資源取所有初始容器申請最大的一個)
-
調度基於生效的初始請求,這就意味着初始容器可以申請預留資源,即便在pod以后的整個生命周期都用不到
pod重啟原因
一個pod基於以下列出的原因,會重啟,重新執行初始容器:
-
用戶更新初始容器的
PodSpec
導致鏡像發生改變.普通應用容器改變只會使應用容器重啟 -
由於
restartPolicy
被設置為Always
,導致所有容器均被中止,強制重啟,由於垃圾回收初始容器的初始狀態記錄丟失