在 Kubernetes 環境中,多容器 Pod 是一種常見的佈署模式,它允許在同一個 Pod 中執行多個緊密關聯的容器。這種模式可以簡化應用程式佈署和管理,但也需要了解一些重要的概念和技巧。本文將探討多容器 Pod 的使用方法和設計模式,涵蓋容器啟動失敗處理、Pod 狀態檢查、刪除 Pod 的寬限期設定、存取特定容器、執行容器內命令、覆寫容器預設命令以及 initContainers 的應用。這些技術有助於更好地理解和運用多容器 Pod,提升 Kubernetes 應用程式的可靠性和效率。透過實際案例和 YAML 組態,文章將逐步解析多容器 Pod 的操作細節和最佳實務,讓開發者能夠更有效地管理和佈署複雜的應用程式。
多容器Pod的使用與設計模式
當Kubernetes無法在Pod中啟動某個容器時,會發生什麼事?Kubernetes會追蹤同一Pod中啟動的所有容器。但是,經常會發生特定容器無法啟動的情況。讓我們透過在YAML清單中引入一個拼寫錯誤,來示範Kubernetes如何反應。
示範容器啟動失敗
在下面的例子中,我們為NGINX容器定義了一個不存在的容器映象;注意nginx:i-do-not-exist標籤:
# failed-multi-container-pod.yaml
apiVersion: v1
kind: Pod
metadata:
name: multi-container-pod
spec:
restartPolicy: Never
containers:
- name: nginx-container
image: nginx:i-do-not-exist
- name: debian-container
image: debian
command: ["/bin/sh"]
args: ["-c", "while true; do date;echo debian-container; sleep 5 ; done"]
現在,我們可以使用kubectl apply -f failed-multi-container-pod.yaml命令來套用這個組態:
$ kubectl apply -f failed-multi-container-pod.yaml
pod/failed-multi-container-pod created
這裡,你可以看到Pod確實被建立了。這是因為即使有不存在的映象,從Kubernetes的角度來看,YAML仍然是有效的。因此,Kubernetes只是建立了Pod並將條目持久化到etcd中,但是我們可以很容易地想象,當kubelet嘗試啟動容器從容器登入檔中檢索映象(例如Docker Hub)時會遇到錯誤。
內容解密:
這段YAML組態定義了一個名為multi-container-pod的Pod,包含兩個容器:nginx-container和debian-container。nginx-container使用了不存在的映象nginx:i-do-not-exist,而debian-container使用了有效的debian映象並執行一個無限迴圈的命令。
檢視Pod狀態
讓我們使用kubectl get pod檢查Pod的狀態:
$ kubectl get pod
NAME READY STATUS RESTARTS AGE
failed-multi-container-pod 1/2 ImagePullBackOff 0 93s
如你所見,Pod的狀態是ImagePullBackOff。這意味著Kubernetes正在嘗試啟動Pod,但由於映象存取問題而失敗。要找出失敗的原因,你需要使用kubectl describe pod failed-multi-container-pod命令來描述Pod:
$ kubectl describe pod failed-multi-container-pod
Name: failed-multi-container-pod
Namespace: default
...<removed for brevity>...
Events:
Type Reason Age From Message
---
-
---
---
---
-
---
-
---
-
---
...<removed for brevity>...
Warning Failed 5m23s (x3 over 6m13s) kubelet Error: ErrImagePull
Warning Failed 4m55s (x5 over 6m10s) kubelet Error: ImagePullBackOff
Normal Pulling 4m42s (x4 over 6m17s) kubelet Pulling image "nginx:i-do-not-exist"
Warning Failed 4m37s (x4 over 6m13s) kubelet Failed to pull image "nginx:i-do-not-exist": Error response from daemon: manifest for nginx:i-do-not-exist not found: manifest unknown: manifest unknown
Normal BackOff 75s (x19 over 6m10s) kubelet Back-off pulling image "nginx:i-do-not-exist"
內容解密:
這段輸出顯示了Pod的事件日誌。可以看到,debian-container是正常的,因為kubelet成功建立了它。但是,nginx-container存在問題。錯誤訊息表明,由於無法找到nginx:i-do-not-exist映象,導致容器無法啟動。
刪除多容器Pod
當你想要刪除包含多個容器的Pod時,你需要使用kubectl delete命令,就像單容器Pod一樣。
$ kubectl delete -f multi-container-pod.yaml
或者,如果你已經知道Pod的名稱,可以這樣做:
$ kubectl delete pods/multi-pod
$ # 或等效地
$ kubectl delete pods multi-pod
內容解密:
刪除Pod可以使用兩種方法:一是指定YAML清單檔案的路徑,二是直接使用Pod的名稱。無論使用哪種方法,都可以有效地刪除Pod及其包含的所有容器。
理解Pod刪除的寬限期
在刪除Pod時,有一個重要的概念叫做寬限期(grace period)。單容器Pod和多容器Pod都有這個寬限期。當你刪除一個Pod時,某些kubectl命令會顯示其狀態為Terminating。這個寬限期預設為30秒。要強制終止一個Pod,可以使用--force標誌。
內容解密:
寬限期是指在刪除Pod時,Kubernetes等待Pod終止的最大時間。如果在寬限期內Pod仍未終止,Kubernetes將強制終止它。使用--grace-period=0 --force選項可以立即刪除Pod並釋放其名稱。
使用多容器Pod與設計模式
存取多容器Pod中的特定容器
當您在同一個Pod中執行多個容器時,可以個別存取每個容器。以下將示範如何存取多容器Pod中的特定容器。
首先,因為我們在之前的範例中刪除了多容器Pod,所以需要重新建立它:
$ kubectl apply -f multi-container-pod.yaml
pod/multi-container-pod created
要存取正在執行的容器,需要使用kubectl exec命令,就像在Docker中使用docker exec命令來在已建立的容器中執行命令一樣。
此命令需要兩個重要引數:
- 包裹容器的Pod名稱
- 容器的名稱,如YAML設定檔中所定義
我們已經知道Pod的名稱是multi-container-pod,但我們不知道容器的名稱。為此,我們可以使用kubectl describe命令來查詢:
$ kubectl describe pods/multi-container-pod
內容解密:
此命令將顯示目標Pod中包含的所有容器的名稱。在我們的範例中,我們可以看到Pod中執行了兩個容器:debian-container和nginx-container。
或者,您可以使用以下命令列出特定Pod中的所有容器名稱:
$ kubectl get pod/multi-container-pod -o jsonpath="{.spec.containers[*].name}"
nginx-container debian-container
內容解密:
此命令使用jsonpath過濾器來檢索指定Pod中的容器名稱。.spec.containers[*].name表示式用於檢索容器列表中的每個容器的名稱屬性。
現在,我們知道了Pod中有兩個容器:nginx-container和busybox-container。讓我們存取nginx-container:
$ kubectl exec -it multi-container-pod --container nginx-container -- /bin/bash
root@multi-container-pod:/# hostname
multi-container-pod
root@multi-container-pod:/#
內容解密:
此命令使用kubectl exec來在指定的容器中執行/bin/bash命令。其中:
-it選項用於分配偽終端並允許與容器互動。--container nginx-container指定了要在其中執行命令的容器。/bin/bash是在指定容器中執行的實際命令,它啟動了一個Bash shell,允許您與容器的命令列互動。
使用此命令,您將進入nginx-container的shell環境中,可以在該容器中執行命令。
在容器中執行命令
Kubernetes 的強大功能之一是能夠隨時存取 Pod 中執行的容器以執行某些命令。我們之前已經做過這件事,但您知道也可以直接從 kubectl 命令列工具執行任何想要的命令嗎?
首先,我們將重新建立多容器 Pod:
$ kubectl apply -f multi-container-pod.yaml
pod/multi-container-pod created
要執行容器中的命令,需要使用 kubectl exec,就像我們之前所做的那樣。但這次,您必須移除 -ti 引數,以防止 kubectl 連線到正在執行的終端機會話。
在這裡,我們執行 ls 命令以列出 multi-container-pod Pod 中 nginx-container 的檔案:
$ kubectl exec pods/multi-container-pod -c nginx-container -- ls
bin
boot
dev
docker-entrypoint.d
docker-entrypoint.sh
...(為了簡潔起見,已移除部分內容)
您可以省略容器名稱,但如果這樣做,kubectl 將使用預設的第一個容器。
接下來,我們將瞭解如何覆寫容器執行的預設命令。
覆寫容器執行的預設命令
覆寫預設命令對於多容器 Pod 至關重要,因為它允許您個別控制每個容器的行為。這意味著您可以自定義每個容器在 Pod 中的工作方式。例如,Web 伺服器容器通常會執行 start server 命令,但您可以覆寫邊車容器的命令來處理日誌記錄。該方法還有助於資源管理。如果容器通常執行繁重的程式,您可以在 Pod 中將其更改為較輕的程式,以確保其他容器有足夠的資源。最後,它有助於管理依賴關係。例如,資料函式庫容器通常會立即啟動,但您可以覆寫其命令以等待相關的應用程式容器準備就緒。
使用 Docker 時,您有機會編寫名為 Dockerfile 的檔案來建立容器映像。Dockerfile 使用兩個關鍵字來告訴我們使用此映像建立的容器在建立時將啟動哪些命令和引數。這兩個關鍵字是 ENTRYPOINT 和 CMD:
ENTRYPOINT是容器將啟動的主要命令。CMD用於替換傳遞給ENTRYPOINT命令的引數。
Dockerfile 示例
一個經典的 Dockerfile 示例,用於執行 sleep 命令 30 秒,如下所示:
# ~/Dockerfile
FROM busybox:latest
ENTRYPOINT ["sleep"]
CMD ["30"]
上述程式碼片段只是普通的 Docker,您應該熟悉這些概念。CMD 引數是可以傳遞給 docker run 命令的內容。如果使用此 Dockerfile 透過 docker build 命令建立此映像,則最終會得到一個 BusyBox 映像,該映像在執行 docker run 命令時只會執行 sleep 命令(ENTRYPOINT)30 秒(CMD 引數)。
Kubernetes 中的命令覆寫
Kubernetes 允許我們透過 YAML Pod 定義檔案覆寫 ENTRYPOINT 和 CMD。為此,您必須在 YAML 組態檔中附加兩個可選鍵:command 和 args。
這是 Kubernetes 帶來的一個非常大的好處,因為您可以決定將引數附加到容器 Dockerfile 所執行的命令,就像使用裸 Docker 的 CMD 引數一樣,或者完全覆寫 ENTRYPOINT!
YAML 組態示例
在這裡,我們將編寫一個新的 manifest 檔案,以覆寫 BusyBox 映像的預設 ENTRYPOINT 和 CMD 引數,使 BusyBox 容器休眠 60 秒。以下是操作方法:
# nginx-debian-with-custom-command-and-args
apiVersion: v1
kind: Pod
metadata:
name: nginx-debian-with-custom-command-and-args
spec:
restartPolicy: Never
containers:
- name: nginx-container
image: nginx:latest
- name: debian-container
image: debian
command: ["sleep"] # 對應於 ENTRYPOINT
args: ["60"] # 對應於 CMD
內容解密:
- 在上述 YAML 組態中,我們定義了一個名為
nginx-debian-with-custom-command-and-args的 Pod,其中包含兩個容器:nginx-container和debian-container。 - 對於
debian-container,我們使用command和args欄位分別覆寫了預設的ENTRYPOINT和CMD。 - 這裡的
command: ["sleep"]對應於 Dockerfile 中的ENTRYPOINT ["sleep"],而args: ["60"]對應於CMD ["30"],將休眠時間改為 60 秒。
建立 Pod 後,我們可以檢查輸出如下:
$ kubectl apply -f nginx-debian-with-custom-command-and-args.yaml
pod/nginx-debian-with-custom-command-and-args created
$ kubectl get po -w
NAME READY STATUS RESTARTS AGE
nginx-debian-with-custom-command-and-args 0/2 ContainerCreating 0 2s
nginx-debian-with-custom-command-and-args 2/2 Running 0 6s
nginx-debian-with-custom-command-and-args 1/2 NotReady 0 66s
因此,覆寫預設命令提供了對多容器 Pod 中容器行為的精細控制。這使得功能定製、資源最佳化和依賴關係管理得以實作,以實作無縫的 Pod 操作。在本文中,我們瞭解到 Kubernetes 允許透過 Pod YAML 定義中的 command 和 args 欄位來覆寫預設值。
initContainers 簡介
initContainers 是 Kubernetes Pod 提供的一個功能,用於在實際容器啟動之前執行設定指令碼。您可以將它們視為在 Pod YAML 清單檔案中定義的其他邊車容器:它們將在建立 Pod 時首先執行。然後,一旦它們完成,Pod 就會開始建立其主要容器。
為什麼使用 initContainers?
initContainers 提供了一種機制,可以在主要應用程式容器啟動之前執行初始化任務,例如資料函式庫遷移、組態設定或依賴項檢查。這有助於確保主要容器在乾淨和組態正確的環境中啟動。
initContainers 的特點
- initContainers 在 Pod 的主要容器之前執行。
- 它們用於執行初始化任務,例如設定或遷移。
- 如果 initContainer 失敗,Pod 將不會啟動其主要容器。
示例
apiVersion: v1
kind: Pod
metadata:
name: my-pod
spec:
containers:
- name: my-container
image: my-app:latest
initContainers:
- name: init-myservice
image: busybox:latest
command: ['sh', '-c', 'echo "Initializing..." && sleep 5']
內容解密:
- 在上述示例中,我們定義了一個名為
my-pod的 Pod,其中包含一個名為my-container的主要容器和一個名為init-myservice的 initContainer。 - initContainer 使用 BusyBox 映像並執行一個簡單的命令來模擬初始化任務。
- 主要容器將在 initContainer 成功完成後啟動。
總之,initContainers 為 Kubernetes Pod 提供了一種強大的機制,用於在主要應用程式容器啟動之前執行初始化任務。這有助於確保主要容器在乾淨和組態正確的環境中啟動。