返回文章列表

Kubernetes 多容器 Pod 設計模式與實踐

本文探討 Kubernetes 中多容器 Pod 的使用場景和設計模式,包含初始化容器 initContainers 的組態、生命週期管理、日誌檢索、以及 emptyDir 和 hostPath 磁碟區的應用與比較。文章提供實用的 YAML 範例和程式碼解析,詳細說明如何設定多容器

容器技術 Kubernetes

在 Kubernetes 環境中,容器化應用程式通常需要多個容器協同工作才能完整執行。利用多容器 Pod,可以將相關的容器組合在一起,分享資源、簡化管理,並提升應用程式的可靠性和效率。本文將詳細介紹如何使用多容器 Pod,包含初始化容器的應用、容器間的資料分享,以及日誌的檢索方式。首先,我們會探討 initContainers 的使用方法,它允許在主要容器啟動前執行一些必要的初始化工作,例如設定資料函式庫、下載組態檔案等。接著,我們會介紹 emptyDir 磁碟區,它提供了一種簡單的機制,讓 Pod 中的容器分享臨時資料。同時,我們也會比較 emptyDir 和 hostPath 磁碟區的特性和使用場景,並說明 hostPath 的潛在風險。最後,我們將示範如何使用 kubectl 命令檢索特定容器的日誌,以及如何利用引數篩選和限制日誌輸出。

使用多容器Pod與設計模式

在Kubernetes中,Pod是佈署和管理容器的基本單位。有時,一個Pod中執行多個容器可以更好地滿足某些應用場景的需求。本章將探討如何在一個Pod中使用多個容器,以及相關的設計模式。

初始化容器(initContainers)

初始化容器(initContainers)是一種特殊的容器,它們在Pod中的主要容器啟動之前執行。initContainers可以用於執行一些準備工作,例如下載組態檔案、安裝依賴項、等待外部服務就緒等。

initContainers的特點

  • 可以有多個initContainers,但它們會按照定義的順序依次執行,而不是平行執行。
  • initContainers可以擁有自己的容器映像,這使得它們可以執行特定的任務,而不需要將這些任務包含在主要容器的映像中。
  • 如果initContainer失敗,Kubernetes不會啟動主要容器。

使用initContainers的例子

以下是一個YAML檔案示例,展示瞭如何使用initContainer下載網站內容並將其複製到分享卷中:

apiVersion: v1
kind: Pod
metadata:
  name: nginx-with-init-container
  labels:
    environment: prod
    tier: frontend
spec:
  restartPolicy: Never
  volumes:
  - name: website-volume
    emptyDir: {}
  initContainers:
  - name: download-website
    image: busybox
    command:
    - sh
    - -c
    - |
      wget https://github.com/iamgini/website-demo-one-page/archive/refs/heads/main.zip -O /tmp/website.zip && \
      mkdir /tmp/website && \
      unzip /tmp/website.zip -d /tmp/website && \
      cp -r /tmp/website/website-demo-one-page-main/* /usr/share/nginx/html
    volumeMounts:
    - name: website-volume
      mountPath: /usr/share/nginx/html
  containers:
  - name: nginx-container
    image: nginx:latest
    volumeMounts:
    - name: website-volume
      mountPath: /usr/share/nginx/html

initContainers的作用

initContainers可以用於各種準備工作,例如:

  • 資料函式庫初始化:設定和組態資料函式庫。
  • 組態檔案下載:下載必要的組態檔案。
  • 套件安裝:安裝主要容器所需的依賴項。
  • 等待外部服務:確保外部服務就緒後再啟動主要容器。
  • 執行前檢查:執行必要的檢查或驗證。
  • 秘密管理:安全地下載和注入秘密到主要容器的環境中。
  • 資料遷移:在主要容器啟動前遷移資料到資料函式庫或儲存系統。
  • 自定義檔案許可權:為主要容器設定適當的檔案許可權。

如何存取特定容器的日誌

當一個Pod中包含多個容器時,可以使用kubectl logs命令來檢索特定容器的日誌。該命令可以串流特定容器的stdout屬性,從而檢索容器的應用程式日誌。

使用kubectl logs命令

要使用kubectl logs命令,需要知道容器的名稱和其父Pod的名稱。以下是命令示例:

kubectl logs -f pods/multi-container-pod --container nginx-container

在這個命令中,--container選項(或簡寫-c)指定了要檢索日誌的容器名稱。這個選項同樣適用於initContainers,只要傳遞其名稱即可檢索其日誌。

程式碼解析:
apiVersion: v1
kind: Pod
metadata:
  name: nginx-with-init-container
  labels:
    environment: prod
    tier: frontend
spec:
  restartPolicy: Never
  volumes:
  - name: website-volume
    emptyDir: {}
  initContainers:
  - name: download-website
    image: busybox
    command:
    - sh
    - -c
    - |
      wget https://github.com/iamgini/website-demo-one-page/archive/refs/heads/main.zip -O /tmp/website.zip && \
      mkdir /tmp/website && \
      unzip /tmp/website.zip -d /tmp/website && \
      cp -r /tmp/website/website-demo-one-page-main/* /usr/share/nginx/html
    volumeMounts:
    - name: website-volume
      mountPath: /usr/share/nginx/html
  containers:
  - name: nginx-container
    image: nginx:latest
    volumeMounts:
    - name: website-volume
      mountPath: /usr/share/nginx/html

內容解密:

  1. 定義了一個名為nginx-with-init-container的Pod,使用busybox映像下載網站內容,並將其複製到分享卷website-volume中。
  2. initContainers部分定義了一個名為download-website的初始化容器,使用wget下載網站內容,並使用unzip解壓縮,最後將內容複製到/usr/share/nginx/html目錄下。
  3. containers部分定義了一個名為nginx-container的主要容器,使用nginx:latest映像,並掛載了分享卷website-volume/usr/share/nginx/html目錄下。

這個YAML檔案展示瞭如何使用initContainer下載網站內容並將其提供給NGINX伺服器。

使用多容器 Pod 與設計模式

存取多容器 Pod 中的日誌

在處理多容器 Pod 時,瞭解如何存取容器日誌是非常重要的。除了基本的 kubectl logs 命令之外,還有其他多種有用的選項可供使用。例如,若要檢索過去兩小時內寫入的日誌,可以使用以下命令:

$ kubectl logs --since=2h pods/multi-container-pod --container nginx-container

此外,可以使用 --tail 選項來檢索日誌輸出的最近幾行。以下是具體做法:

$ kubectl logs --tail=30 pods/multi-container-pod --container nginx-container

這裡,我們檢索了 nginx-container 日誌輸出的最近 30 行。

內容解密:

  • --since=2h 引數用於指定檢索過去兩小時內的日誌,有助於篩選出特定時間範圍內的記錄。
  • --tail=30 引數用於檢索日誌的最近 30 行輸出,方便快速檢視最新的日誌資訊。
  • --container nginx-container 用於指定要檢索日誌的容器名稱,在多容器 Pod 中至關重要。

在同一個 Pod 中的容器之間分享磁碟區

在本文中,我們將學習 Kubernetes 中的磁碟區(Volume)概念及其使用方法。Docker 也有磁碟區,但與 Kubernetes 的磁碟區不同,它們滿足相同的需求但實作方式不同。

什麼是 Kubernetes 磁碟區?

Kubernetes 有兩種磁碟區:

  • 磁碟區(Volume),我們將在這裡討論。
  • PersistentVolume,是一種更進階的功能,我們將在第 9 章「Kubernetes 中的持久儲存」中討論。

簡而言之,Kubernetes 中的磁碟區與 Pod 的生命週期密切相關。當您例項化一個 Pod 時,可以定義並將磁碟區掛載到其中的容器。基本上,磁碟區代表與 Pod 存在相關的儲存空間。一旦 Pod 被刪除,相關聯的磁碟區也會被刪除。

建立和掛載 emptyDir 磁碟區

顧名思義,emptyDir 是一個在 Pod 建立時初始化的空目錄,您可以將其掛載到 Pod 中執行的每個容器的指定位置。這是讓容器之間分享資料最簡單的方法。

Kubernetes 支援多種磁碟區型別,包括從主機檔案系統掛載的、雲端提供商和網路儲存系統等。一些廣泛使用的解決方案包括:

  • hostPath
  • emptyDir
  • nfs
  • persistentVolumeClaim(當您需要使用 PersistentVolume 時)

內容解密:

  • emptyDir 磁碟區在 Pod 建立時初始化為空目錄,並可掛載到容器中,用於容器間分享資料。
  • hostPath 和 nfs 等不同型別的磁碟區提供了多樣化的儲存解決方案,能夠滿足不同的需求。
  • 磁碟區的生命週期與 Pod 相關,Pod 被刪除時,相關聯的磁碟區也會被刪除,但容器當機不會導致磁碟區被刪除。

圖表說明:hostPath 和 emptyDir 磁碟區

此圖示展示了 hostPath 和 emptyDir 磁碟區的基本概念。

@startuml
skinparam backgroundColor #FEFEFE
skinparam componentStyle rectangle

title Kubernetes 多容器 Pod 設計模式與實踐

package "Kubernetes Cluster" {
    package "Control Plane" {
        component [API Server] as api
        component [Controller Manager] as cm
        component [Scheduler] as sched
        database [etcd] as etcd
    }

    package "Worker Nodes" {
        component [Kubelet] as kubelet
        component [Kube-proxy] as proxy
        package "Pods" {
            component [Container 1] as c1
            component [Container 2] as c2
        }
    }
}

api --> etcd : 儲存狀態
api --> cm : 控制迴圈
api --> sched : 調度決策
api --> kubelet : 指令下達
kubelet --> c1
kubelet --> c2
proxy --> c1 : 網路代理
proxy --> c2

note right of api
  核心 API 入口
  所有操作經由此處
end note

@enduml

內容解密:

  • 此圖示闡述了 Pod 與 emptyDir 和 hostPath 磁碟區之間的關係。
  • emptyDir 被多個容器分享,而 hostPath 直接掛載自主機檔案系統。
  • 圖表清晰地展示了不同型別的磁碟區如何在 Pod 中被使用和分享。

使用多容器Pod與設計模式

在以下範例中,我們將建立一個包含兩個容器的Pod,一個是NGINX容器,另一個是Debian容器。我們將覆寫Debian容器的啟動命令,以防止它在完成後離開。這樣,我們就可以讓它無限期執行,並能夠執行額外的命令來檢查我們的emptyDir是否正確初始化。

兩個容器將分享一個掛載在/var/i-am-empty-dir-volume/的磁碟卷,這將是我們的emptyDir磁碟卷,在同一個Pod中初始化。下面是建立Pod的YAML檔案:

# multi-container-with-emptydir-pod.yaml
---
apiVersion: v1
kind: Pod
metadata:
  name: multi-container-with-emptydir-pod
spec:
  containers:
  - name: nginx-container
    image: nginx:latest
    volumeMounts:
    - mountPath: /var/i-am-empty-dir-volume
      name: empty-dir-volume
  - name: debian-container
    image: debian
    command: ["/bin/sh"]
    args: ["-c", "while true; do sleep 30; done;"] # 防止容器在完成後離開
    volumeMounts:
    - mountPath: /var/i-am-empty-dir-volume
      name: empty-dir-volume
  volumes:
  - name: empty-dir-volume # 磁碟卷名稱
    emptyDir: {} # 初始化一個空目錄

內容解密:

  • apiVersionkind 定義了Kubernetes物件的版本和型別,在這裡是Pod。
  • metadata 包含了Pod的名稱。
  • spec 定義了Pod的規格,包括容器列表和磁碟卷。
  • containers 列出了Pod中的兩個容器:NGINX和Debian。
  • volumeMounts 定義了容器內掛載的磁碟卷路徑。
  • volumes 定義了Pod中的磁碟卷,在這裡是一個emptyDir。

現在,我們可以使用以下命令應用YAML檔案:

$ kubectl apply -f multi-container-with-emptydir-pod.yaml
pod/multi-container-with-emptydir-pod created

驗證Pod執行狀態

$ kubectl get po
NAME                          READY   STATUS    RESTARTS   AGE
multi-container-with-emptydir-pod   2/2     Running   0          25s

確認Pod執行後,我們可以檢查兩個容器是否都能存取掛載的emptyDir:

$ kubectl exec multi-container-with-emptydir-pod -c debian-container -- ls /var
$ kubectl exec multi-container-with-emptydir-pod -c nginx-container -- ls /var

內容解密:

  • kubectl exec 命令允許我們在容器內執行命令。
  • -c 指定了要執行命令的容器名稱。
  • ls /var 列出了/var目錄下的內容,確認emptyDir已經被正確掛載。

接下來,我們在Debian容器中建立一個檔案,並檢查它是否能在NGINX容器中被存取:

$ kubectl exec multi-container-with-emptydir-pod -c debian-container -- bin/sh -c "echo 'hello world' >> /var/i-am-empty-dir-volume/hello-world.txt"
$ kubectl exec multi-container-with-emptydir-pod -c nginx-container -- cat /var/i-am-empty-dir-volume/hello-world.txt
hello world

內容解密:

  • 第一個命令在Debian容器中建立了一個名為hello-world.txt的檔案。
  • 第二個命令在NGINX容器中讀取了該檔案,證明瞭兩個容器分享了emptyDir。

使用hostPath磁碟卷

hostPath允許你將主機上的目錄掛載到Pod中的容器。這在某些情況下很有用,但由於可移植性和安全性的問題,在Kubernetes中通常被視為反模式。

建立和掛載hostPath磁碟卷

在minikube環境中,hostPath對應到本地機器的一個目錄。下面是一個使用hostPath的例子:

apiVersion: v1
kind: Pod
metadata:
  name: pod-with-hostpath
spec:
  containers:
  - name: nginx-container
    image: nginx:latest
    volumeMounts:
    - mountPath: /var/host-path-volume
      name: host-path-volume
  volumes:
  - name: host-path-volume
    hostPath:
      path: /tmp/host-path-volume # 主機上的目錄路徑
      type: DirectoryOrCreate # 如果目錄不存在,則建立它

內容解密:

  • hostPath 定義了主機上的目錄路徑。
  • type 指定了hostPath的型別,可以是DirectoryOrCreateDirectoryFileOrCreateFileSocketCharDeviceBlockDevice

總之,使用多容器Pod和不同型別的磁碟卷可以滿足多種應用場景的需求,但需要謹慎考慮可移植性和安全性。建議使用持久化卷(PV)和持久化卷宣告(PVC)來管理持久化資料,以獲得更好的可移植性和安全性。