系列目錄html
容器常常是爲了解決單一的,窄範圍的問題,好比說微服務.然而現實中,一些複雜問題的完成每每須要多個容器.這裏咱們討論一下如何把多個容器放在同一個pod裏以及容器間的通訊linux
pod是kubernetes裏的一個基本概念,可能咱們從一開始接觸kubernetes的時候就開始接觸pod,並被灌輸pod是kubernetes裏最小的不可分割的工做單元,這裏再從多容器的角度對其進行一些基本闡釋.nginx
簡言之,pod是kubernetes能夠部署和管理的最小單元,換言之也就是說若是你想要運行一個容器,你先要爲這個容器建立一個pod.同時,一個pod也能夠包含多個容器,之因此多個容器包含在一個pod裏,前面講到過的初始容器爲普通應用容器準備環境是一種使用場景,每每因爲業務上的緊密耦合,一組容器也須要放在同一個pod裏.web
須要注意的是,以上場景都非必須把不一樣的容器放在同一個pod裏,可是這樣每每更便於管理,甚至後面會講到的,緊密耦合的業務容器放置在同一個容器裏通訊效率更高.具體怎麼使用還要看實際狀況,綜合權衡.docker
直接部署一個容器看起來更簡單,可是這裏也有更好的緣由爲何在容器基礎上抽象一層.容器是一個存在的實體,並指向一個具體的事物.這個具體的事物多是一個docker容器,但也多是一個rtk容器,或者一個Virtlet管理的虛擬機.它們有不一樣的要求.api
更深層的緣由是爲了管理容器,kubernetes須要更多的信息,好比重啓策略,它定義了容器終止後要採起的策略;或者是一個可用性探針,從應用程序的角度去探測是否一個進程還存活着.瀏覽器
基於這些緣由,kubernetes架構師決定使用一個新的實體,也就是pod,而不是重載容器的信息添加更多屬性,用來在邏輯上包裝一個或者多個容器的管理所須要的信息bash
pod裏的容器運行在一個邏輯上的"主機"上,它們使用相同的網絡名稱空間(也就是說,同一pod裏的容器使用相同的ip和相同的端口段區間)和相同的IPC名稱空間.它們也能夠共享存儲卷.這些特性使它們能夠更有效的通訊.而且pod可使你把緊密耦合的應用容器做爲一個單元來管理.服務器
所以一個應用若是須要多個運行在同一主機上的容器時,爲何爲把它們放在同一個容器裏呢?首先,這樣何故違反了一個容器只負責一個應用的原則.這點很是重要,若是咱們把多個應用放在同一個容器裏,這將使解決問題變得很是麻煩由於它們的日誌記錄混合在了一塊兒,而且它們的生命週期也很難管理.所以一個應用使用多個容器將更簡單,更透明,而且使應用依賴解偶.而且粒度更小的容器更便於不一樣的開發團隊共享和複用.網絡
這裏說到爲了解偶把應用分別放在不一樣容器裏,前面咱們也強調爲了便於管理管緊耦合的應用把它們的容器放在同一個pod裏.一會強調耦合,一個強調解偶看似矛盾,實際上廣泛存在,高內聚低耦合是咱們的追求,然而一個應用的業務邏輯模塊不可能徹底完獨立不存在耦合,這就須要咱們從實際上來考量,作出決策.
Sidecar containers "幫助"主容器,好比日誌文件監視器.一個日誌監視器構建完成之後,能夠由不一樣的應用來使用.另外一個示例是sidecar 容器
爲主容器加載文件和運行須要的數據
代理,橋接和適配器 使主容器與外部世界聯通.好比Apache http服務器或者nginx可承載靜態文件.也能夠作爲一個web的反向代理服務器.
你可使用一個pod來承載一個多層應用(好比wordpress),可是更建議使用不一樣的pod來承載不一樣的層,因這這樣你能夠爲每個層單獨擴容而且把它們分佈到集羣的不一樣節點上.
把不一樣的容器放在同一個pod裏讓它們之間的通訊變得很是直接和簡單,它們能夠經過如下幾種方法達到通訊目的.
你可使用一個共享的存儲捲來簡單高效的地在容器間共享數據.大多數狀況下,使用一個共享目錄在同一pod裏的不一樣容器間共享數據就夠了.
一個標準的同一pod內容器共享存儲卷的用例是一個容器往共享存儲卷裏寫入數據,其它的則從共享目錄裏讀取數據
apiVersion: v1 kind: Pod metadata: name: mc1 spec: volumes: - name: html emptyDir: {} containers: - name: 1st image: nginx volumeMounts: - name: html mountPath: /usr/share/nginx/html - name: 2nd image: debian volumeMounts: - name: html mountPath: /html command: ["/bin/sh", "-c"] args: - while true; do date >> /html/index.html; sleep 1; done
這個示例裏咱們定義了一個存儲卷叫做html,它是emptyDir
類型的.當一個pod在一個節點上建立的時候,它就被分配,只要pod一直運行在這個節點上它就一直存在(emptyDir生命週期和pod相同).就像它的名字所暗示的同樣,它是一個空的目錄,第一個容器運行了一個nginx server而且把它掛載到/usr/share/nginx/html
,第二個容器使用了一個Debian鏡像並把emptyDir掛載到/html
.每一秒鐘,第二個容器就會把當前日期寫入到index.html
裏,它位於共享存儲卷裏.當用戶發起一個http請求,nginx就會讀取它並響應給用戶.
你能夠經過把nginx的端口暴露出去而後經過瀏覽器查看或者查看共享目錄裏的文件來檢測以上是否有效果
kubectl exec mc1 -c 1st -- /bin/cat /usr/share/nginx/html/index.html ... Fri Aug 25 18:36:06 UTC 2017 $ kubectl exec mc1 -c 2nd -- /bin/cat /html/index.html ... Fri Aug 25 18:36:06 UTC 2017 Fri Aug 25 18:36:07 UTC 2017
同一個pod裏的容器共享IPC名稱空間,這就意味着他們能夠經過進程間通訊的手段來進行通訊,好比使用SystemV semaphores
或者POSIX
共享內存
如下示例中,咱們定義一個包含了兩個容器的pod.它們使用相同的docker鏡像,第一個容器是一個生產者,建立一個標準的linux消息隊列,寫一些隨機的消息,而後寫一個特殊的退出消息.第二個容器是一個消費者,打開同一個消息隊列來讀取數據直到讀到退出消息,咱們把重啓策略設置爲Never
,這樣當兩個pod都停止的時候pod就會中止.
apiVersion: v1 kind: Pod metadata: name: mc2 spec: containers: - name: producer image: allingeek/ch6_ipc command: ["./ipc", "-producer"] - name: consumer image: allingeek/ch6_ipc command: ["./ipc", "-consumer"] restartPolicy: Never
而後經過kubectl create來建立pod,用下面命令來查看狀態
$ kubectl get pods --show-all -w NAME READY STATUS RESTARTS AGE mc2 0/2 Pending 0 0s mc2 0/2 ContainerCreating 0 0s mc2 0/2 Completed 0 29
這時候你能夠檢測每個容器的日誌來檢測第二個隊列是否消費了第一個隊列生產的全部消息包括退出消息
$ kubectl logs mc2 -c producer ... Produced: f4 Produced: 1d Produced: 9e Produced: 27 $ kubectl logs mc2 -c consumer ... Consumed: f4 Consumed: 1d Consumed: 9e Consumed: 27 Consumed: done
這裏面存在一個問題,就是生產者所在容器要早於消費者所在容器,咱們必須處理容器的啓動順序問題
當前,同一個pod裏的全部容器都是並行啓動而且沒有辦法肯定哪個容器必須早於哪個容器啓動.就上面的IPC示例,咱們不能總保證第一個容器早於第二個容器啓動.這時候就須要使用到初始容器(init container)來保證啓動順序,關於初始容器,能夠查看官方文檔,本系列也對其進行了翻譯,以便查看.
同一pod下的容器使用相同的網絡名稱空間,這就意味着他們能夠經過'localhost'來進行通訊,它們共享同一個Ip和相同的端口空間
一般pod裏的容器監聽不一樣的端口,想要被外部訪問都須要暴露出去.你能夠經過在一個服務裏暴露多個端口或者使用不一樣的服務來暴露不一樣的端口來實現