K8S in Action 讀後感(概念簡介)

一、K8S的用武之地

今天,大型單體應用正被逐漸拆分成小的、可獨立運行的組件,我們稱之爲微服務。微服務彼此之間解耦,所以它們可以被獨立開發、部署、升級、伸縮。這使得我們可以對每一個微服務實現快速迭代,並且迭代的速度可以和市場需求變化的速度保持一致。

但是,隨着部署組件的增多和數據中心的增長,配置、管理並保持系統的正常運行變得越來越困難。如果我們想要獲得足夠高的資源利用率並降低硬件成本,把組件部署在什麼地方變得越來越難以決策。手動做所有的事情,顯然不太可行。我們需要一些自動化的措施,包括自動調度、配置、監管和故障處理。這正是K8S的用武之地。

K8S抽象了數據中心的硬件基礎設施,使得對外暴露的只是一個巨大的資源池。它讓我們在部署和運行組件時,不用關注底層的服務器。使用K8S部署多組件應用時,它會爲每個組件都選擇一臺合適的服務器,部署之後它能夠保證每個組件可以輕易地發現其他組件,並彼此之間實現通信。

通過K8S部署應用程序時,你的集羣包含多少節點都是一樣的。集羣規模不會造成什麼差異性,額外的集羣節點只是代表一些額外的可用來部署應用的資源。

二、K8S集羣架構

一個K8S集羣由多個節點組成,節點可分爲兩種類型:

主節點:負責管理和調度工作節點

工作節點:負責運行部署的應用程序,每臺服務器都是一個工作節點(主節點除外)

API服務器:主節點和工作節點的通信媒介

Scheduler:負責調度應用(爲應用的每個可部署組件分配一個工作節點)

Controller Manager:它執行集羣級別的功能,如複製組件、持續跟蹤工作節點、處理失敗節點等。

etcd:持久化存儲集羣配置

容器運行時:Docker、Containerd、RTK或其他容器類型。

Kubelet:管理所在的節點的容器

Kube-Proxy:負責組件之間的負載均衡網絡流量

三、容器的隔離機制

一個容器裏運行的進程實際上運行在宿主機的操作系統上,就像所有其他進程一樣(不像虛擬機,進程是運行在不同的操作系統上的)。但在容器裏的進程仍然是和其他進程隔離的。對於容器內進程本身而言,就好像是在機器和操作系統上運行的唯一一個進程。

容器的隔離機制主要依靠 Linux 命名空間 namespace 和 Linux 控制組 cgroups。namespace 提供隔離視圖(文件、進程、網絡接口、主機名等),cgroups 限制進程可使用的資源(CPU、內存、網絡帶寬等)。

默認情況下,每個 Linux 系統最初僅有一個命名空間,所有系統資源都屬於這一個命名空間,但是你能創建額外的命名空間,以及在它們之間組織資源。這就是容器的隔離機制。

四、K8S的最小單位 pod

一個 pod 由一個或一組緊密相關的容器組成,它們總是一起運行在同一個工作節點上。

每個 pod 就像是一個獨立的邏輯機器,擁有自己的 IP,即使不同的 pod 運行在同一個工作節點,它們也是獨立的。

前面我們講到容器的隔離機制是通過 namespace 和 cgroup 來實現的,一個 pod 內的容器,或者說是容器組通常需要共享某些資源,K8S 通過配置 Docker 來讓一個 pod 內的所有容器共享相同的 network 和 UTS 命名空間,這樣它們都有相同的主機名和網絡接口。但這也意味着在同一 pod 中的容器運行的多個進程不能綁定到相同的端口號,否則會導致端口衝突,這也表示着一個道理有共享就有衝突。

K8S 集羣中的所有 pod 都在同一個共享網絡地址空間中,每個 pod 都可以通過其他 pod 的 IP 地址來實現相互訪問。

pod 的定義

已部署 pod 的 yaml 通常包含三個部分:

metadata:包括名稱、命名空間、標籤和關於該容器的其他信息

spec:包含 pod 內容的實際說明,例如 pod 的容器、卷和其他數據

status:包含運行中的 pod 的當前信息(創建新的 pod 不需要這一部分)

一個簡單的 pod 定義 yaml:

五、pod 的創建和管理者

pod 並不是直接創建出來的,它是通過 ReplicatiionController、ReplicaSet 或 Deployment......等方式創建出來的。

ReplicaSet 是 ReplicationController 的升級版,ReplicaSet 在標籤選擇器上擁有更強大的選擇功能。

但是 ReplicaSet 也很少使用,我們通常用更高級的資源對象 Deployment,它會創建 ReplicaSet 來創建和管理 pod。

當然還有一些其他資源對象如:DaemonSet、Job、CronJob.....等等。

六、服務

由於 pod 是動態的,爲了高可用並且通常不是單節點,沒有一個固定訪問的 IP。前面提到過K8S擁有負載均衡和彈性伸縮的能力,它提供了服務這個資源對象。如果想要從集羣外部訪問 pod 的話,就需要藉助 LoadBalancer 服務來訪問,LoadBalancer 服務擁有一個不變的 IP,這樣 pod 就可以自由的伸縮了。

服務有多種,LoadBalancer 服務相當於是對集羣外部的,Service 是對集羣內部的,主要目的就是使集羣內部的其他 pod 可以訪問當前這組 pod,集羣外部是沒法訪問這個 IP的。

另外,服務並不是和 pod 直接相連的,相反,有一種資源介於兩者之間,它就是 Endpoint 資源,Endpoint 資源就是暴露一個服務的 IP 地址和端口的列表。

如果創建不包含 pod 選擇器的服務,K8S 將不會創建 Endpoint 資源(畢竟缺少選擇器,不知道服務中包含哪些 pod),你可能會想爲什麼要用 Endpoint 資源,直接用選擇器不行嘛。儘管在 spec 服務中定義了 pod 選擇器,但在重定向傳入連接時不會直接使用它。相反,選擇器用於構建 IP 和端口列表,然後存儲在 Endpoint 資源中。當客戶端連接到服務時,服務代理選擇這些 IP 和端口中的一個,並將連接重定向到該位置監聽的服務器。另外,Endpoint 對象需要與服務具有相同的名稱,幷包含該服務的目標 IP 地址和端口列表。如果不是手動創建 Endpoint,Endpoint 對於開發者來說幾乎是無感的。

前面說到的都是將服務暴露給集羣內部(LoadBalancer除外),如果想將服務暴露給集羣外部,可以將服務的類型設置爲 NodePort,這樣每個集羣節點都會在節點上打開一個端口,然後將端口上收到的流量重定向到基礎服務。

如圖所示:

當然也可以將服務的類型設置爲 LoadBalancer,它是 NodePort 類型的一種擴展,這使得服務可以通過一個專用的負載均衡器來訪問,這是有 K8S 中正在運行的雲基礎設施提供的。負載均衡器將流量重定向到跨所有節點的節點端口,客戶端通過負載均衡器的 IP 連接到服務。

如圖所示:

還可以通過 Ingress 資源,這是一種完全不同的機制,通過一個 IP 地址公開多個服務。

七、標籤、註解和命名空間

標籤可用於組織 pod,也可組織所有其他的 K8S 資源。一個資源可以擁有多個標籤,通常我們在創建資源的時候就會將標籤附件到資源上,但也可以之後添加標籤或修改標籤。

標籤是一個鍵值對,上面就是 creation_method 和 env 兩個標籤。另外,上面說了標籤不僅用於組織 pod,還可以組織其他 K8S 資源,比如你的 pod 對硬件有特定要求,比如需要 GPU 等特殊資源,你還可以對節點打標籤。讓 pod 調度到擁有該標籤的節點上。

除標籤外,pod 和其它對象還可以包含註解。註解也是鍵值對,所以它們非常相似。但是註解不是用於保存標識信息而存在的,因爲已經有標籤了,註解主要用於工具使用。

標籤是用於組織資源的,由於每個對象都可以擁有多個標籤,因此這些對象組是可以重疊的,另外當在集羣中工作時,如果沒有明確指定標籤選擇器,我們總能看到所有對象。

但是,當你想將對象分割成完全獨立且不重疊的組時,就需要用到命名空間了。這個命名空間和 Linux 命名空間不一樣,這個僅爲對象名稱提供一個作用域。

八、健康檢查

K8S 可以通過存活探針檢查容器是否還在運行。可以爲 pod 中的每個容器指定存活探針。如果探測失敗,K8S 將定期執行探針並重新啓動容器。

HTTP GET 探針:對給定的URL執行 GET 請求,如果探測器收到相應,並且狀態碼不代表錯誤,則探測成功。

TCP 套接字探針:嘗試與容器指定端口建立 TCP 連接,如果連接建立成功,則探測成功。

Exec 探針:在容器內執行任意命令,並檢查命令的退出狀態碼。如果狀態碼是 0,則探測成功。

九、ReplicationController、ReplicaSet、DaemonSet、Job、CronJob

ReplicationController 是一種 K8S 資源,可確保它的 pod 始終保持運行狀態,如果 pod 因任何原因消失,則 ReplicationController 會注意到缺少了的 pod 並創建替代 pod。

ReplicationController 會持續監控正在運行的 pod 列表,並保 pod 的數目與期望相符,如果運行的 pod 太少則會創建新副本,否則會刪除多餘副本。

一個 ReplicationController 有三個主要部分:

Label Selector(標籤選擇器):確定 ReplicationController 作用域中有哪些 pod

Replica Count(副本個數):指定應運行的 pod 數量

Pod Template(pod 模板):用於創建新的 pod 副本

ReplicaSet 的行爲和 ReplicationController 完全相同,但 pod 選擇器的表達能力更強,可完全替代 ReplicationController。

DaemonSet 和 ReplicaSet 的區別是,它會在每個節點上都運行一個 pod,比如日誌收集器和資源監控器就有這樣的需求。

前面介紹的都是需要持續運行的 pod,如果你想要運行完成工作後就終止任務的情況就需要用到 Job。

Job 資源在創建時會立即運行 pod,但是更多的需求是定時執行,這就要用到 CronJob。

十、Ingress

爲什麼需要 Ingress,一個重要的原因是每個 LoadBalancer 服務都需要自己的負載均衡器,以及獨有的公有 IP 地址,而 Ingress 只需要一個公網 IP 就能爲許多服務提供訪問。當客戶端向 Ingress 發生 HTTP 請求時,Ingress 會根據請求的主機名和路徑決定請求轉發到的服務,如圖所示:

十一、就緒探針

不知道你有沒有考慮到這麼一種情況,如果 pod 的標籤與服務的 pod 選擇器相匹配,那麼 pod 就將作爲服務的後端,請求就會被重定向到 pod 上,但是如果 pod 沒有準備好,如何處理服務請求呢?

該 pod 可能需要時間來加載配置或數據,或者可能需要執行預熱過程以防止第一個用請求時間太長影響用戶體驗。在這種情況下,不希望該 pod 立即開始接受請求,尤其是在運行的實例可以正確快速地處理請求的情況下。不要將請求轉發到正在啓動的 pod 中,直到完全準備就緒。

與前面介紹的存活探針類似,K8S 還允許爲容器定義準備就緒探針,就緒探針也有三種類型:Exec 探針、HTTP GET 探針、TCP Socket 探針。

十二、Headless

上面我們已經知道服務可提供負載均衡的能力,將請求轉發到隨機的一個 pod 上,但是如果你想要讓請求連接到所有的 pod,那該怎麼辦呢?

幸運的是,K8S 允許客戶端通過 DNS 查找發現 pod IP。通常,當執行服務的 DNS 查找時,DNS 服務器會返回單個 IP---服務的集羣 IP。但是如果告訴K8S 不需要爲服務提供集羣 IP(通過在服務 spec 中將 clusterIP 字段設置爲 None 來完成此操作),則 DNS 服務器將返回 pod IP 而不是單個服務 IP。DNS 服務器不會返回單個 DNS 記錄,而是會爲該服務返回多個記錄,每個記錄指向當時支持該服務的單個 pod 的 IP。

將服務的 spec 中的 clusterIP 字段設置爲 None 會使服務成爲 headless 服務:

十三、卷

我們之前說過,pod 類似邏輯主機,在邏輯主機中運行的進程共享諸如 CPU、RAM、網絡接口等資源,或許你會認爲進程也能共享磁盤,那你就錯了。pod 中的每個容器都有自己的獨立文件系統,因爲文件系統來自容器鏡像。

K8S 中的卷是 pod 的一個組成部分,因此像容器一樣在 pod 的規範中就定義了。它們不是獨立的 K8S 對象,也不能單獨創建或刪除。 pod 中的所有容器都可以使用卷,但必須先將它掛載在每個需要訪問它的容器中。在每個容器中,都可以在其文件系統的任意位置掛載卷。

假設有一個帶有三個容器的 pod,一個容器運行了一個 web 服務器,該 web 服務器的 HTML 頁面目錄位於 /var/htdocs,並將站點訪問日誌存儲到 /var/logs 目錄中。第二個容器運行了一個代理來創建 HTML 文件,並將它們存放在 /var/html 中,第三個容器處理在 /var/logs 目錄中找到的日誌。每個容器都有一個明確的用途,但是每個容器單獨使用就沒多大用處了。但是如果將兩個卷添加到 pod 中,並在三個容器的適當路徑上掛載它們,就會創建一個完善的系統。

常見的卷有如下幾種:

  • emptypDir:用於存儲臨時數據的簡單空目錄,卷的生命週期和 pod 相關聯,所以當刪除 pod 時,卷的內容就會丟失。
  • gitRepo:通過檢出 git 倉庫的內容來初始化卷,通過克隆 Git 倉庫並在 pod 啓動時拉取最新版本(創建容器之前)填充數據,缺點是不能和 repo 保持同步,每次將更改推送到 gitRepo時,需要刪除 pod 才能更新。
  • hostPath:用於將目錄從工作節點的文件系統掛載到 pod 中,hostPath 卷指向節點文件系統上的特定文件或目錄,這表示着他和節點相關,另外他是持久性存儲,因爲 gitRepo 和 emptyDir 卷的內容會在 pod 被刪除時被刪除,而 hostPath 的內容則不會被刪除。

1、emptyDir

2、gitRepo

3、hostPath

hostPath 雖然可用於持久化存儲卷,但是使用的很少,通常使用其他雲存儲卷,例如 gce、aws 存儲卷。

十四、ConfigMap 和 Secret

幾乎所有的應用程序都需要配置信息,並且這些配置數據不應該被嵌入應用本身。通常傳遞配置選項給容器化應用程序的方法是環境變量、命令行參數、亦或者是通過 gitRepo 存儲卷,但其實還有一種更好的方式,那就是 ConfigMap。

儘管絕大多數配置選項並未包含敏感信息,少量配置可能含有證書、私鑰,以及其他需要保持安全的相似數據,這類型數據需要被特殊對待,這就需要用到 Secret。

應用配置的關鍵在於能夠在多個環境中區分配置選項,將配置從應用程序源碼中分離,可頻繁變更配置值,而不用重新部署 pod。如果將 pod 定義描述看作是應用程序源碼,顯然需要將配置移除 pod 定義。

1、ConfigMap 定義

2、使用 ConfigMap

十五、Downward API

前面通過環境變量、或者 ConfigMap、Secret 卷嚮應用傳遞配置數據都是預先知道的。但是對於那些不能預先知道的數據,比如 pod 的 IP、主機名或者是 pod 自身的名稱該怎麼獲取呢?此外對於那些已經在 pod 中定義的數據,比如 pod 的標籤和註解又該如何獲取呢?這些問題都可以使用 Downward API 解決。Downward API 允許我們通過環境變量或文件傳遞 pod 的元數據。Downward API 主要是將在 pod 的定義和狀態中取得的數據作爲環境變量和文件的值。

十六、Deployment

Deployment 是一種更高價資源,用於部署應用程序並以聲明的方式升級應用,而不是通過 ReplicationController 或 ReplicaSet 進行部署,它們都被認爲是更底層的概念。當創建一個 Deployment 時,ReplicatSet 資源也會隨之創建。使用 Deployment 可以更容易的更新應用程序,因爲可以直接定義單個 Deployment 資源所需達到的狀態,並讓 K8S 處理中間的狀態。總之,部署應用程序使用 Deployment 就行了。

十七、Statefulset

我們之前使用 ReplicaSet 創建的 pod 都是共享一個持久卷:

那能不能通過一個 ReplicaSet 讓多個 pod 副本都指定獨立的持久卷聲明呢,很明顯這是做不到的,除非你先手動創建 pod,之後在創建 ReplicaSet 來管理它們,但是如果發生節點故障後,你還需要手動管理它們,所以這是不合適的。

由於每個副本都是獨立有狀態的,所以你先得創建多個持久卷聲明,當創建 Statefulset 的時候,你就需要指定 pod 對應的持久卷。

十八、總結

K8S 的一些資源對象書中介紹的大概就這些了,上面只是一些簡單的概念,具體還是需要看書和實操深入理解。

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