下圖顯示了在第3章中建立的三種物件,用來在k8s上部署一個最小的應用程式。顯示了它們之間的關係以及它們在系統中的功能。
圖5.1 三種基本物件類型組合已部署的應用程式
您現在對如何透過k8s API暴露這些物件有了基本了解。讓我們從Pod物件開始,因為Pod物件代表了k8s中最重要的核心概念 → 應用程式的實例運行。
了解到pod是一組位於同一位置的容器,也是k8s中的基本建構磚塊。您不是單獨部署容器,而是將一組容器當作一個單元進行部署和管理 → 一個 pod。 儘管pod可能包含多個容器,但一個pod只包含一個容器的情況並不少見。 當一個pod有多個容器時,這些容器都會運行在同一個工作節點上 → 一個pod實例永遠不會跨越多個節點。如下圖:
圖5.2 一個pod的所有容器都運行在同一個節點上。一個 pod 永遠不會跨越多個節點。
讓我們討論一下為什麼我們需要多個容器一起運行,而不是在同一個容器中運行多個程序。
想像一個應用程式由多個程序組成,這些程序利用IPC (Inter-Process Communication)或共用檔案相互溝通,這會要這些程序在同一台電腦上運行。 在第 2 章中,您了解到每個容器就像一台獨立的電腦或VM。一台電腦通常運行多個程序;容器也可以做到這一點。 您可以在一個容器中運行組成應用程式的所有程序,但這使得容器非常難以管理。
容器被設計為只運行一個程序,不計算它產生的任何子進程。容器工具和k8s都是圍繞這個事實開發的。 例如:在容器中運行的程序應該將log寫到standard output。用來顯示log的Docker和k8s指令只會顯示從standard output所抓取log內容。 如果容器中運行單個程序,則它是唯一的寫到output的程序,但如果您在容器中運行多個程序,這些程序都會寫到相同的output。 因此,它們的log會交錯在一起,很難分辨每行log屬於哪個程序。
容器應該只運行一個程序的另一個原因是container runtime只有在容器的根程序死亡時才會重新啟動容器。它不關心這個根程序所建立的任何子進程。 如果根程序產生子進程,根程序獨自負責保持這些所有程序運行。
要充分利用container runtime提供的功能,您應該考慮在每個容器只運行一個程序。
由於您不應該在單個容器中運行多個程序,因此您需要另一個更高級別的構造,它允許您一起運行相關程序,即使被劃分為多個容器也是如此。 這些程序必須能互相彼此通訊就像它們是在一台普通電腦中的程序。這就是pod被導入的原因。
使用pod,您可以一起運行密切相關的程序,為它們(幾乎)提供相同的環境,就好像它們都在單個容器中運行一樣。 這些程序的某些資源是隔離的,但並是不完全 → 它們會共用一些資源。pod為您提供了兩全其美的體驗。 您可以使用容器提供的所有特性,還可以讓程序協同工作。Pod使這些內部互連的容器可以當作一個單位進行管理。
在第二章中,您了解到容器使用一組自己的Linux名稱空間,但它也可以與其他容器共用一些名稱空間。 這種命名空間的共用就是k8s和container runtime將容器組合成pod的方式。
如下圖,一個 pod 中的所有容器共用相同的網路命名空間,因此也共用屬於它的網路介面、IP和port空間。
圖5.3 pod中的容器共用相同的網路介面
因為共用了port空間,在同一個pod的容器中運行的程序不可以綁定相同的port號,而其他pod中的程序有自己的網路介面和port空間,在不同pod之間消除了port衝突。
一個pod中的所有容器是看到相同的系統hostname,是因為它們共用UTS命名空間,並且可以利用往常的IPC機制進行通訊,是因為它們共用IPC命名空間。 也可以將pod設定為對pod的所有容器使用單個PID命名空間,這會使它們共用單個程序樹,但您必須為每個pod單獨啟用這功能。
<aside> 💡 當同一個pod的容器使用不同的PID命名空間時,它們無法看到彼此,也無法在它們彼此之間發送像是SIGTERM或者SIGINT的程序訊號。
</aside>
就是這種特定命名空間的共用,使得運行在pod中的程序給人一種它們在一起運行的印象,即使它們在不同的容器中運行。