k8s 深入篇———— k8s 的pod[五]

前言

簡單整理一下pod的相關知識。

正文

爲什麼我們需要pod。

前面我們知道了k8s一個最重要的作用是解決容器的編排功能,那麼爲什麼有一個pod的東西。

這就是實際中遇到的問題。

那就是容器和容器之間,那就是如何表達容器和容器之間的關係。

就是有些場景下,容器與容器之間是存在關係的。

如果把k8s 比作操作系統,容器比作進程,那麼進程組就是pod。

之所以有這個pod,就是因爲一些容器他們之間需要在公共的namespace、cgroup 下面運行。

也可以理解他們原本就應該在一臺虛擬機下面執行。

像這樣容器間的緊密協作,我們可以稱爲“超親密關係”。

這些具有“超親密關係”容器的典型特徵包括但不限於:互相之間會發生直接的文件交換、使用 localhost 或者 Socket 文件進行本地通信、

會發生非常頻繁的遠程調用、需要共享某些 Linux Namespace(比如,一個容器要加入另一個容器的 Network Namespace)等等。

不過,Pod 在 Kubernetes 項目裏還有更重要的意義,那就是:容器設計模式。

首先,關於 Pod 最重要的一個事實是:它只是一個邏輯概念。

也就是說,Kubernetes 真正處理的,還是宿主機操作系統上 Linux 容器的 Namespace
和 Cgroups,而並不存在一個所謂的 Pod 的邊界或者隔離環境。
那麼,Pod 又是怎麼被“創建”出來的呢?
答案是:Pod,其實是一組共享了某些資源的容器。

具體的說:Pod 裏的所有容器,共享的是同一個 Network Namespace,並且可以聲明
共享同一個 Volume。

所以,在 Kubernetes 項目裏,Pod 的實現需要使用一箇中間容器,這個容器叫作 Infra 容器。

在這個 Pod 中,Infra 容器永遠都是第一個被創建的容器,而其他用戶定義的容器,則通過 Join Network Namespace 的方式,與 Infra 容器關聯在一起。

這樣的組織關係,可以用下面這樣一個示意圖來表達:

這個 Pod 裏有兩個用戶容器 A 和 B,還有一個 Infra 容器。很容易理解,在Kubernetes 項目裏,Infra 容器一定要佔用極少的資源,所以它使用的是一個非常特殊的鏡像,叫作:k8s.gcr.io/pause。

這個鏡像是一個用匯編語言編寫的、永遠處於“暫停”狀態的容器,解壓後的大小也只有 100~200 KB 左右。

而在 Infra 容器“Hold 住”Network Namespace 後,用戶容器就可以加入到 Infra 容器的 Network Namespace 當中了。

所以,如果你查看這些容器在宿主機上的 Namespace文件(這個 Namespace 文件的路徑,我已經在前面的內容中介紹過),它們指向的值一定是完全一樣的。

當然,其他的所有網絡資源,都是一個 Pod 一份,並且被該 Pod 中的所有容器共享;Pod 的生命週期只跟 Infra 容器一致,而與容器 A 和 B 無關。

比如:

apiVersion: "v1"
kind: "Pod"
metadata:
  name: two-contrains
  namespace: name01
spec:
  restartPolicy: "Always"
  volumes:
  - name: shared-data
    hostPath:
      path: /data
  containers:
  - name: nginx-controller
    image: nginx
    volumeMounts:
    - name: shared-data
      mountPath: /usr/share/nginx/html
  - name: debian-container
    image: debian
    volumeMounts:
    - name: shared-data
      mountPath: /pod-data
    command: ["/bin/sh"]
    args: ["-c","echo Hello from the debian container ! > /pod-data/index.html"]

pod 聲明瞭一個volume,然後容器nginx-controller、debian-container 使用。

來看下怎麼實現的。

來看下docker 現象。

對於volumeMounts 而言,其實就是綁定/pod-data 到 /data 中。

對於網絡而言:

其實就是pod 內的幾個容器共享網絡。

對上面而言用的是infra的網絡。

再舉一個例子, 介紹一個initContainers:

k8s中的initContainers和Containers都是用於定義Pod中容器的部分,但是它們的主要區別在於:

  1. 生命週期不同:initContainers是在Pod中所有容器之前啓動的,並且只有在initContainers完成後纔會啓動其他容器。而Containers則是同時啓動的。

  2. 用途不同:initContainers主要用於在啓動Pod之前完成一些初始化操作,例如配置環境變量、檢查依賴等。而Containers則是用於運行應用程序或服務。

  3. 狀態不同:initContainers完成後會退出,而Containers會繼續運行。

總之,initContainers主要用於在Pod啓動時完成一些初始化工作,而Containers則是用於運行應用程序或服務。

是的,initContainers是按照它們在Pod中的順序依次運行的,每個initContainer必須在前一個initContainer完成後才能開始運行。只有所有的initContainers都成功完成後,Pod中的其他容器纔會啓動。

在定義initContainers時,可以使用spec.initContainers字段來指定它們的順序。例如,下面的示例定義了兩個initContainers,分別用於執行初始化操作:

spec:
  initContainers:
  - name: init-container-1
    image: busybox
    command: ['sh', '-c', 'echo "init container 1"']
  - name: init-container-2
    image: busybox
    command: ['sh', '-c', 'echo "init container 2"']
  containers:
  - name: my-app
    image: my-image
    command: ['sh', '-c', 'echo "my app"']

在這個示例中,init-container-1將在init-container-2之前執行。當Pod啓動時,先運行init-container-1,然後等待它完成後再運行init-container-2,最後才啓動my-app容器。

那麼舉一個實際中用到的例子,現在這個例子沒什麼用了,因爲現在java 打包就集成了tomcat,而不是在外面包裹一層:

apiVersion: v1
kind: Pod
metadata:
  name: javaweb-2
spec:
  initContainers:
  - image: geektime/sample:v2
    name: war
    command: ["cp", "/sample.war", "/app"]
    volumeMounts:
    - mountPath: /app
      name: app-volume
  containers:
  - image: geektime/tomcat:7.0
    name: tomcat
    command: ["sh","-c","/root/apache-tomcat-7.0.42-v2/bin/start.sh"]
    volumeMounts:
    - mountPath: /root/apache-tomcat-7.0.42-v2/webapps
      name: app-volume
    ports:
    - containerPort: 8080
      hostPort: 8001
  volumes:
  - name: app-volume
    emptyDir: {}

上面大體意思是將sample.war拷貝到app下面,然後將掛載出來,然後geektime/tomcat:7.0 就可以使用這個war 包了。

實際上,這個所謂的“組合”操作,正是容器設計模式裏最常用的一種模式,它的名字叫:sidecar

顧名思義,sidecar 指的就是我們可以在一個 Pod 中,啓動一個輔助容器,來完成一些獨立於主進程(主容器)之外的工作。

比如,在我們的這個應用 Pod 中,Tomcat 容器是我們要使用的主容器,而 WAR 包容器的存在,只是爲了給它提供一個 WAR 包而已。

所以,我們用 Init Container 的方式優先運行 WAR 包容器,扮演了一個 sidecar 的角色。

這樣做就有用給好處,就是每次都更新的是war包,而不需要關注tomcat運行環境,減少包的大小。

比如,我現在有一個應用,需要不斷地把日誌文件輸出到容器的 /var/log 目錄中。

這時,我就可以把一個 Pod 裏的 Volume 掛載到應用容器的 /var/log 目錄上。

然後,我在這個 Pod 裏同時運行一個 sidecar 容器,它也聲明掛載同一個 Volume 到自己的 /var/log 目錄上。

這樣,接下來 sidecar 容器就只需要做一件事兒,那就是不斷地從自己的 /var/log 目錄裏讀取日誌文件,轉發到 MongoDB 或者 Elasticsearch 中存儲起來。這樣,一個最基本的日誌收集工作就完成了。

跟第一個例子一樣,這個例子中的 sidecar 的主要工作也是使用共享的 Volume 來完成對文件的操作。

但不要忘記,Pod 的另一個重要特性是,它的所有容器都共享同一個 NetworkNamespace。

這就使得很多與 Pod 網絡相關的配置和管理,也都可以交給 sidecar 完成,而完全無須干涉用戶容器。

這裏最典型的例子莫過於 Istio 這個微服務治理項目了。

Istio 項目使用 sidecar 容器完成微服務治理的原理,我在後面很快會講解到。

下面一點需要區分:

在 Kubernetes 中,Infra Container 和 Init Container 都是容器,但它們有不同的用途和生命週期。

Infra Container 是一個在 Pod 中運行的輔助容器,用於提供一些共享資源或服務,例如網絡命名空間、存儲卷、日誌收集、監控等。Infra Container 在 Pod 啓動後一直運行直到 Pod 終止。

Init Container 是一種特殊類型的容器,它是在 Pod 中其他容器啓動之前運行的,用於初始化或準備一些資源。例如,可以使用 Init Container 下載應用程序代碼、初始化數據庫、生成配置文件等。Init Container 在它的工作完成後立即退出,然後 Pod 中的其他容器纔開始啓動。

因此,Infra Container 和 Init Container 的主要區別在於它們的用途和生命週期。Infra Container 是一個持久的輔助容器,爲 Pod 提供一些共享資源或服務;而 Init Container 是一個短暫的容器,用於在其他容器啓動之前初始化或準備一些資源。

然後:

如果在 Kubernetes 中未定義 Infra Container,則 Kubernetes 會自動添加一個名爲 `pause` 的 Infra Container。這是一個非常輕量級的容器,它的作用是爲 Pod 中的其他容器創建 Linux 命名空間和網絡 namespace,併爲網絡 namespace 分配 IP 地址。

在 Pod 中自動添加的 `pause` 容器是一個 Infra Container,它會在其他容器之前啓動,並在其他容器退出之後保持運行狀態。因此,即使在 Pod 定義文件中未定義 Infra Container,Kubernetes 仍然會確保 Infra Container 在 Pod 啓動時運行。

需要注意的是,如果在 Pod 定義文件中顯式定義 Infra Container,則 Kubernetes 不會自動添加 `pause` 容器。在這種情況下,Infra Container 的定義順序決定了它的啓動順序。

一般情況下,我們是不填這個Infra Container。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章