1. 程式人生 > >為什麽我們需要Pod?(容器設計模式sidecar)

為什麽我們需要Pod?(容器設計模式sidecar)

什麽 mman emp dir 方式 新的 實現原理 highlight 插件

Pod,是 Kubernetes 項目中最小的 API 對象

容器的本質是進程,就是未來雲計算系統中的進程;容器鏡像就是這個系統裏的".exe"安裝包

Kubernetes 就是操作系統!

Pod 裏的所有容器,共享的是同一個 Network Namespace,並且可以聲明共享同一個 Volum

所以,

在 Kubernetes 項目裏,Pod 的實現需要使用一個中間容器,這個容器叫作 Infra 容器。在這個 Pod 中,Infra 容器永遠都是第一個被創建的容器,而其他用戶定義的容器,

則通過 Join Network Namespace 的方式,與 Infra 容器關聯在一起。這樣的組織關系.

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

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

而對於同一個 Pod 裏面的所有用戶容器來說,它們的進出流量,也可以認為都是通過 Infra 容器完成的。這一點很重要,因為<strong>將來如果你要為 Kubernetes 開發一個網絡插件時,

應該重點考慮的是如何配置這個 Pod 的 Network Namespace,而不是每一個用戶容器如何使用你的網絡配置,這是沒有意義的

這就意味著,如果你的網絡插件需要在容器裏安裝某些包或者配置才能完成的話,是不可取的:Infra 容器鏡像的 rootfs 裏幾乎什麽都沒有,沒有你隨意發揮的空間。

當然,這同時也意味著你的網絡插件完全不必關心用戶容器的啟動與否,而只需要關註如何配置 Pod,也就是 Infra 容器的 Network Namespace 即可。

這樣,一個 Volume 對應的宿主機目錄對於 Pod 來說就只有一個,Pod 裏的容器只要聲明掛載這個 Volume,就一定可以共享這個 Volume 對應的宿主機目錄。比如下面這個例子:

apiVersion: v1
kind: Pod
metadata:

name: two-containers
spec:
restartPolicy: Never
volumes:
- name: shared-data
hostPath:
path: /data
containers:
- name: nginx-container
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"]

在這個例子中,debian-container 和 nginx-container 都聲明掛載了 shared-data 這個 Volume。而 shared-data 是 hostPath 類型。所以,它對應在宿主機上的目錄就是:/data。而這個目錄,其實就被同時綁定掛載進了上述兩個容器當中

這就是為什麽,nginx-container 可以從它的 /usr/share/nginx/html 目錄中,讀取到 debian-container 生成的 index.html 文件的原因。

二.明白了 Pod 的實現原理後,我們再來討論“容器設計模式

我們現在有一個 Java Web 應用的 WAR 包,它需要被放在 Tomcat 的 webapps 目錄下運行起來

一種方法是,把 WAR 包直接放在 Tomcat 鏡像的 webapps 目錄下,做成一個新的鏡像運行起來。可是,這時候,如果你要更新 WAR 包的內容,或者要升級 Tomcat 鏡像,就要重新制作一個新的發布鏡像,非常麻煩

另一種方法是,你壓根兒不管 WAR 包,永遠只發布一個 Tomcat 容器。不過,這個容器的 webapps 目錄,就必須聲明一個 hostPath 類型的 Volume,從而把宿主機上的 WAR 包掛載進 Tomcat 容器當中運行起來。不過,這樣你就必須要解決一個問題,即:如何讓每一臺宿主機,都預先準備好這個存儲有 WAR 包的目錄呢?這樣來看,你只能獨立維護一套分布式存儲系統了。

實際上,有了 Pod 之後,這樣的問題就很容易解決了。我們可以把 WAR 包和 Tomcat 分別做成鏡像,然後把它們作為一個 Pod 裏的兩個容器“組合”在一起。這個 Pod 的配置文件如下所示

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: {}

在這個 Pod 中,我們定義了兩個容器,第一個容器使用的鏡像是 geektime/sample:v2,這個鏡像裏只有一個 WAR 包(sample.war)放在根目錄下。而第二個容器則使用的是一個標準的 Tomcat 鏡像

不過,你可能已經註意到,WAR 包容器的類型不再是一個普通容器,而是一個 Init Container 類型的容器

在 Pod 中,所有 Init Container 定義的容器,都會比 spec.containers 定義的用戶容器先啟動。並且,Init Container 容器會按順序逐一啟動,而直到它們都啟動並且退出了,用戶容器才會啟動

所以,這個 Init Container 類型的 WAR 包容器啟動後,我執行了一句"cp /sample.war /app",把應用的 WAR 包拷貝到 /app 目錄下,然後退出。而後這個 /app 目錄,就掛載了一個名叫 app-volume 的 Volume。

接下來就很關鍵了。Tomcat 容器,同樣聲明了掛載 app-volume 到自己的 webapps 目錄下

所以,等 Tomcat 容器啟動時,它的 webapps 目錄下就一定會存在 sample.war 文件:這個文件正是 WAR 包容器啟動時拷貝到這個 Volume 裏面的,而這個 Volume 是被這兩個容器共享的。

像這樣,我們就用一種“組合”方式,解決了 WAR 包與 Tomcat 容器之間耦合關系的問題。

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

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

比如:

在我們的這個應用 Pod 中,Tomcat 容器是我們要使用的主容器,而 WAR 包容器的存在,只是為了給它提供一個 WAR 包而已。所以,我們用 Init Container 的方式優先運行 WAR 包容器,扮演了一個 sidecar 的角色。

為什麽我們需要Pod?(容器設計模式sidecar)