Kubernetes的初始化容器initContainers
initContainers是一種專用的容器,在應(yīng)用程序容器啟動之前運(yùn)行,可以包括一些應(yīng)用程序鏡像中不存在的實(shí)用工具和安裝腳本,可以完成應(yīng)用的必要數(shù)據(jù)初始化等工作。總的來說就是在正式的容器啟動之前做一些準(zhǔn)備工作的。
例如一個(gè)應(yīng)用容器掛載的volume目錄需要一些必不可少的初始化文件,不加init容器的時(shí)候直接掛載volume應(yīng)用容器的那個(gè)目錄是空的(因?yàn)閐ocker掛載一個(gè)volume時(shí),會用volume的內(nèi)容覆蓋容器內(nèi)掛載目錄的內(nèi)容),這就需要讓init容器也跟應(yīng)用容器掛載同一個(gè)volume目錄,將初始化文件放進(jìn)去,然后在啟動應(yīng)用容器就能正常看到那些初始化文件了,我也正是遇到了這樣的問題,才來研究和學(xué)習(xí)init容器的。
1、一個(gè)pod可以運(yùn)行多個(gè)容器,同樣的一個(gè)pod也可能有一個(gè)或多個(gè)先于應(yīng)用容器啟動的 Init 容器,每個(gè)容器啟動失敗后都會根據(jù)你配置的重啟策略進(jìn)行重啟,直到運(yùn)行成功。Init 容器與普通的基本一樣,主要區(qū)別如下
Init 容器總是運(yùn)行到成功運(yùn)行完為止。
前面的Init 容器必須已經(jīng)運(yùn)行完成,才會開始運(yùn)行下一個(gè)init容器,而應(yīng)用程序容器時(shí)并行運(yùn)行的。
2、因?yàn)?Init 容器具有與應(yīng)用程序容器分離的單獨(dú)鏡像,所以使用他們可以具有如下優(yōu)勢:
它們可以包含并運(yùn)行實(shí)用工具,但是出于安全考慮,是不建議在應(yīng)用程序容器鏡像中包含這些實(shí)用工具的。
它們可以包含使用工具和定制化代碼來安裝,但是不能出現(xiàn)在應(yīng)用程序鏡像中。例如,創(chuàng)建鏡像沒必要FROM另一個(gè)鏡像,只需要在安裝過程中使用類似sed、awk、python或dig這樣的工具。
應(yīng)用程序鏡像可以分離出創(chuàng)建和部署的角色,而沒有必要聯(lián)合它們構(gòu)建一個(gè)單獨(dú)的鏡像。
Init 容器使用 Linux Namespace,所以相對應(yīng)用程序容器來說具有不同的文件系統(tǒng)視圖。因此,它們能夠具有訪問 Secret 的權(quán)限,而應(yīng)用程序容器則不能。
它們必須在應(yīng)用程序容器啟動之前運(yùn)行完成,而應(yīng)用程序容器是并行運(yùn)行的,所以 Init 容器能夠提供了一種簡單的阻塞或延遲應(yīng)用容器的啟動的方法,直到滿足了一組先決條件。
3、下面是一些如何使用 Init 容器的思路:
等待一個(gè) Service 創(chuàng)建完成,通過類似如下 shell 命令:
for i in {1..100}; do sleep 1; if dig myservice; then exit 0; fi; exit 1
將 Pod 注冊到遠(yuǎn)程服務(wù)器,通過在命令中調(diào)用 API,類似如下:
curl -X POST http://$MANAGEMENT_SERVICE_HOST:$MANAGEMENT_SERVICE_PORT/register -d 'instance=$(<POD_NAME>)&ip=$(<POD_IP>)'
在啟動應(yīng)用容器之前等一段時(shí)間,使用類似sleep 60的命令。
克隆 Git 倉庫到數(shù)據(jù)卷。
將配置值放到配置文件中,運(yùn)行模板工具為主應(yīng)用容器動態(tài)地生成配置文件。例如,在配置文件中存放 POD_IP 值,并使用 Jinja 生成主應(yīng)用配置文件。
4、 init容器使用示例,
Kubernetes1.6 版本后使用新語法,把 Init 容器的聲明移到spec中,只需要添加initContainers 即可,下面列一個(gè)具有 2 個(gè) Init 容器的簡單 Pod。 第一個(gè)等待myservice啟動,第二個(gè)等待mydb啟動。 一旦這兩個(gè) Service 都啟動完成,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;']
下面的 yaml 文件展示了mydb和myservice兩個(gè) Service:
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
這個(gè) Pod 可以使用下面的命令進(jìn)行啟動和調(diào)試
$ 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
$ 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
$ 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這兩個(gè) Service,我們能夠看到 Init 容器完成,并且myapp-pod被創(chuàng)建:
$ 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 上使用activeDeadlineSeconds,在容器上使用livenessProbe,這樣能夠避免 Init 容器一直失敗, 這就為 Init 容器活躍設(shè)置了一個(gè)期限。在 Pod 中的每個(gè) app 和 Init 容器的名稱必須唯一,與任何其它容器共享同一個(gè)名稱,會在驗(yàn)證時(shí)拋出錯(cuò)誤。
在 Pod 啟動過程中,Init 容器會按順序在網(wǎng)絡(luò)和數(shù)據(jù)卷初始化之后啟動。 每個(gè)容器必須在下一個(gè)容器啟動之前成功退出。 如果由于運(yùn)行時(shí)或失敗退出,導(dǎo)致容器啟動失敗,它會根據(jù) Pod 的restartPolicy指定的策略進(jìn)行重試,在所有的 Init 容器沒有成功之前,Pod 將不會變成Ready狀態(tài),如果 Pod重啟,所有 Init 容器必須重新執(zhí)行。
因?yàn)?Init 容器可能會被重啟、重試或者重新執(zhí)行,所以 Init 容器的代碼應(yīng)該是冪等的(即任意多次執(zhí)行所產(chǎn)生的影響與一次執(zhí)行的影響相同)。 特別地,被寫到EmptyDirs中文件的代碼,應(yīng)該對輸出文件可能已經(jīng)存在做好準(zhǔn)備。
5、Pod 能夠重啟,會導(dǎo)致 Init 容器重新執(zhí)行,重啟主要有如下幾個(gè)原因:
用戶更新 PodSpec 導(dǎo)致 Init 容器鏡像發(fā)生改變。更改 Init 容器的 image 字段,等價(jià)于重啟該 Pod,應(yīng)用容器鏡像的變更只會重啟應(yīng)用容器。
Pod 基礎(chǔ)設(shè)施容器被重啟。這不多見,但某些具有 root 權(quán)限可訪問 Node 的人可能會這樣做。
當(dāng)restartPolicy設(shè)置為 Always,Pod 中所有容器會終止,強(qiáng)制重啟,由于垃圾收集導(dǎo)致 Init 容器完成的記錄丟失。
總結(jié)
以上是生活随笔為你收集整理的Kubernetes的初始化容器initContainers的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 三大类设计模式-23种小类设计模式
- 下一篇: 生活琐碎之淘宝账号安全和交易安全1账号被