K8s 淺談Pod

“Pod” 這一詞是我們經(jīng)常聽到的一個詞語,我們知道Pod 是k8s 最基本的單元對象,Pod里邊包含著容器,容器里邊運(yùn)行著我們的服務(wù),也就是進(jìn)程,那么我們知道為何k8s 要有pod的概念它的作用都有什么,接下來我們要分析pod的存在意義。

什么是Pod

Pod 的中文含義叫 ‘豆莢’,豆莢里邊放的都是豌豆,豌豆可以理解為容器,Pod 可以理解為“虛擬機(jī)”的概念,所以Pod里邊運(yùn)行著一組容器

Pod,其實(shí)是一組共享了某些資源的容器,是k8s最基礎(chǔ)的一個對象,關(guān)于 Pod 最重要的一個事實(shí)是:它只是一個邏輯概念。

為什么有Pod

Pod是一組容器的邏輯概念,根據(jù)容器的設(shè)計理念一個容器里邊只運(yùn)行一個主進(jìn)程,也就是說容器的本質(zhì)就是進(jìn)程,Pod 里的所有容器,共享的是同一個 Network Namespace,并且可以聲明共享同一個 Volume。

接下來我們思考一下Pod 里的容器是如何做到共享Network Namespace、和Volume 等資源的 ?

其實(shí)在創(chuàng)建容器的時候,k8s會默認(rèn)創(chuàng)建一個中間容器,這個容器叫作 Infra 容器,這個容器永遠(yuǎn)都是第一個被創(chuàng)建的,而其他用戶定義的容器,則通過 Join Network Namespace 的方式,與 Infra 容器關(guān)聯(lián)在一起。這樣的組織關(guān)系,可以用下面這樣一個示意圖來表達(dá):


如上圖所示,這個 Pod 里有兩個用戶容器 A 和 B,還有一個 Infra 容器。很容易理解,在 Kubernetes 項目里,Infra 容器一定要占用極少的資源,所以它使用的是一個非常特殊的鏡像,叫作:k8s.gcr.io/pause。這個鏡像是一個用匯編語言編寫的、永遠(yuǎn)處于“暫?!睜顟B(tài)的容器,解壓后的大小也只有 100~200 KB 左右。

而在 Infra 容器“Hold 住”Network Namespace 后,用戶容器就可以加入到 Infra 容器的 Network Namespace 當(dāng)中了。所以,如果你查看這些容器在宿主機(jī)上的 Namespace 文件,它們指向的值一定是完全一樣的。

這也就意味著,對于 Pod 里的容器 A 和容器 B 來說:

  • 它們可以直接使用 localhost 進(jìn)行通信;
  • 它們看到的網(wǎng)絡(luò)設(shè)備跟 Infra 容器看到的完全一樣;
  • 一個 Pod 只有一個 IP 地址,也就是這個 Pod 的 Network Namespace 對應(yīng)的 IP 地址;
  • 當(dāng)然,其他的所有網(wǎng)絡(luò)資源,都是一個 Pod 一份,并且被該 Pod 中的所有容器共享;
  • Pod 的生命周期只跟 Infra 容器一致,而與容器 A 和 B 無關(guān)。

有了這個設(shè)計之后,共享 Volume 就簡單多了:Kubernetes 項目只要把所有 Volume 的定義都設(shè)計在 Pod 層級即可。

Pod 這種“超親密關(guān)系”容器的設(shè)計思想,實(shí)際上就是希望,當(dāng)用戶想在一個容器里跑多個功能并不相關(guān)的應(yīng)用時,應(yīng)該優(yōu)先考慮它們是不是更應(yīng)該被描述成一個 Pod 里的多個容器。

最典型的例子是:WAR 包與 Web 服務(wù)器。

假如現(xiàn)在我們用docker的方式來處理,

第一種方式就是WAR 包 放到 Tomat webapps 的目錄下,制作一個鏡像然后發(fā)布部署,每次更新WAR 包內(nèi)容 或者升級 Tomat 的時候都需要重新制作一個新的鏡像

第二種方式 只發(fā)布一個Tomat 鏡像 ,然后把WAR 包的地址掛載到Tomat 里,但是問題是如何讓每一臺宿主機(jī),都預(yù)先準(zhǔn)備好這個存儲有 WAR 包的目錄呢?這樣來看,你只能獨(dú)立維護(hù)一套分布式存儲系統(tǒng)了。

實(shí)際上,有了 Pod 之后,這樣的問題就很容易解決了。我們可以把 WAR 包和 Tomcat 分別做成鏡像,然后把它們作為一個 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)放在根目錄下。而第二個容器則使用的是一個標(biāo)準(zhǔn)的 Tomcat 鏡像。

不過,你可能已經(jīng)注意到,WAR 包容器的類型不再是一個普通容器,而是一個 Init Container 類型的容器。

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

所以,這個 Init Container 類型的 WAR 包容器啟動后,我執(zhí)行了一句”cp /sample.war /app”,把應(yīng)用的 WAR 包拷貝到 /app 目錄下,然后退出。

而后這個 /app 目錄,就掛載了一個名叫 app-volume 的 Volume。

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

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

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

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

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

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

Pod 生命周期

  1. 執(zhí)行初始化容器init container,其實(shí)在這之上還有一個infra container,pod 的生命周期是跟infra container一致的,然后才開始 init container, init container 可以定義多個, 依次執(zhí)行,只有當(dāng)前 init container 創(chuàng)建成功完成退出之后再執(zhí)行下一個 init container
  2. 開始初始化main container,再初始化main container 如果用戶設(shè)置了 hook 則會執(zhí)行hook的操作 post start hook, 然后開始執(zhí)行初始化main container
  3. main container 創(chuàng)建成功之后 如果用戶設(shè)置了容器的探活設(shè)置(livenessProbe 和 readinessProbe), kubelet 會根據(jù) 設(shè)置的類型來訪問Pod容器,根據(jù)返回的結(jié)果決定是否重啟或者是否能被訪問到。

容器探測的類型

ExecAction:在容器中執(zhí)行命令,狀態(tài)碼為0表示成功,否則即為不健康狀態(tài)。
TCPSocketAction:與容器TCP端口建立連接進(jìn)行診斷,端口能夠成功即為正常,否則為不健康狀態(tài)
HTTPGetAction: 向容器內(nèi)指定IP 和 端口發(fā)送 http 請求,響應(yīng)碼為2xx 或者 3xx即為成功

  • livenessProbe:指示容器是否正在運(yùn)行。如果存活探測失敗,則 kubelet 會殺死容器,并且容器進(jìn)行重啟。如果容器不提供存活探針,則默認(rèn)狀態(tài)為Success。
  • readinessProbe:指示容器是否準(zhǔn)備好服務(wù)請求。如果就緒探測失敗,端點(diǎn)控制器將從與 Pod 匹配的所有 Service 的端點(diǎn)中刪除該 Pod 的 IP 地址。初始延遲之前的就緒狀態(tài)默認(rèn)為 Failure。如果容器不提供就緒探針,則默認(rèn)狀態(tài)為 Success。
  1. 當(dāng)Pod 被刪除的時候 如果用戶設(shè)置了 hook 則會執(zhí)行hook的操作 post stop hook, 執(zhí)行完畢之后刪除Pod

總結(jié)

這遍文章知道了Pod 對象是k8s 最基礎(chǔ)的原子單位,它里邊可以運(yùn)行多個容器,多個容器之間的網(wǎng)絡(luò)是存儲都是共享的,做到共享的實(shí)現(xiàn)方式是引用了一個中間容器 infra container, 在創(chuàng)建Pod 之前首先創(chuàng)建infra container, 然后然后再創(chuàng)建Pod里的容器,如果有init container 那么先創(chuàng)建init container 再創(chuàng)建main container,創(chuàng)建好的container 則通過 Join Network Namespace 的方式,與 Infra 容器關(guān)聯(lián)在一起, Pod 的生命周期與Infra 是一致的。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

友情鏈接更多精彩內(nèi)容