【編者的話】為了確保整體的簡(jiǎn)單性和易上手,有時(shí) Kubernetes 會(huì)通過(guò)一些簡(jiǎn)單的抽象隱去操作背后的復(fù)雜邏輯,但作為一名有夢(mèng)想的工程師,掌握其背后的真正思路是十分有必要的。本文以 Kubectl 創(chuàng)建 Pod 為例,向你揭露從客戶端到 Kubelet 的請(qǐng)求的完整生命周期。 想象一下,當(dāng)你想在 Kubernetes 集群部署 Nginx 時(shí),你會(huì)執(zhí)行以下命令: kubectl run nginx --image=nginx --replicas=3 幾秒后,你將看到三個(gè) Nginx Pod 分布在集群 Worker 節(jié)點(diǎn)上。這相當(dāng)神奇,但它背后究竟發(fā)生了什么? Kubernetes 最為人稱道的地方是,它通過(guò)用戶友好的 API 處理跨基礎(chǔ)架構(gòu)的工作負(fù)載部署,通過(guò)簡(jiǎn)單的抽象隱藏其背后的復(fù)雜性。但是,為了充分理解它為我們提供的價(jià)值,我們需要理解它的原理。 本文將帶領(lǐng)你充分了解從客戶端到 Kubelet 請(qǐng)求的完整生命周期,并在必要時(shí)通過(guò)源代碼解釋它到底是什么。如果你想和更多 Kubernetes 技術(shù)專家交流,可以加我微信liyingjiese,備注『加群』。群里每周都有全球各大公司的最佳實(shí)踐以及行業(yè)最新動(dòng)態(tài)。 Kubectl驗(yàn)證和生成器首先,當(dāng)我們按下回車執(zhí)行命令后,Kubectl 會(huì)執(zhí)行客戶端驗(yàn)證,以確保非法請(qǐng)求(如創(chuàng)建不支持的資源或使用格式錯(cuò)誤的鏡像名稱)快速失敗,并不會(huì)發(fā)送給 kube-apiserver——即通過(guò)減少不必要的負(fù)載來(lái)提高系統(tǒng)性能。驗(yàn)證通過(guò)后, Kubectl 開(kāi)始封裝它將發(fā)送給 kube-apiserver 的 HTTP 請(qǐng)求。在 Kubernetes 中,訪問(wèn)或更改狀態(tài)的所有嘗試都通過(guò) kube-apiserver 進(jìn)行,后者又與 etcd 進(jìn)行通信。Kubectl 客戶端也不例外。為了構(gòu)造 HTTP 請(qǐng)求, Kubectl 使用生成器(generators),這是一種負(fù)責(zé)序列化的抽象。 你可能沒(méi)有注意到,通過(guò)執(zhí)行 kubectl run,除了運(yùn)行 Deployment,我們還能利用指定參數(shù) --generator 來(lái)部署其他工作負(fù)載。 如果沒(méi)有指定 --generator 參數(shù)的值, Kubectl 會(huì)自動(dòng)推斷資源的類型,具體如下:
Kubectl 還將確定是否需要觸發(fā)其他操作,例如記錄命令(用于部署或?qū)徲?jì)),或者此命令是否是 dry run。 當(dāng) Kubectl 判斷出要?jiǎng)?chuàng)建一個(gè) Deployment 后,它將使用 DeploymentV1Beta1 generator 配合我們提供的參數(shù),生成一個(gè)運(yùn)行時(shí)對(duì)象(Runtime Object)。 API Group 和版本協(xié)商這里值得指出的是, Kubernetes 使用的是一個(gè)分類為 API Group 的版本化 API。它旨在對(duì)資源進(jìn)行分類,以便于推理。同時(shí),它還為單個(gè) API 提供了更好的版本化方案。例如,Deployment 的 API Group 為 apps,其最新版本為 v1。這也是我們?yōu)槭裁葱枰?Deployment manifests 頂部指定 apiVersion: apps/v1 的原因。 回歸正文, Kubectl 生成運(yùn)行時(shí)對(duì)象之后,就開(kāi)始為它查找合適的 API Group 和版本,然后組裝一個(gè)知道該資源各種 REST 語(yǔ)義的版本化客戶端。 這個(gè)發(fā)現(xiàn)階段被稱為版本協(xié)商(version negotiation),這時(shí) Kubectl 會(huì)掃描 remote API 上的 /apis 路徑以檢索所有可能的 API Group。 由于 kube-apiserver 在 /apis 路徑中公開(kāi)其 OpenAPI 格式的 scheme 文檔,客戶端可以借此輕松找到匹配的 API。 為了提高性能, Kubectl 還將 OpenAPI scheme 緩存到 ~/.kube/cache/discovery 目錄。如果要了解 API 發(fā)現(xiàn)的完整過(guò)程,你可以試著刪除該目錄并在運(yùn)行 Kubectl 命令時(shí)將 -v 參數(shù)的值設(shè)為最大,然后你就可以在日志中看到所有試圖找到這些 API 版本的 HTTP 請(qǐng)求。 最后一步才是真正地發(fā)送 HTTP 請(qǐng)求。一旦請(qǐng)求獲得成功的響應(yīng), Kubectl 將會(huì)根據(jù)所需的輸出格式打印 success message。 客戶端驗(yàn)證我們?cè)谏衔闹袥](méi)有提到的一件事是客戶端身份驗(yàn)證(這是在發(fā)送 HTTP 請(qǐng)求之前處理的),現(xiàn)在讓我們來(lái)看看。為了成功發(fā)送請(qǐng)求, Kubectl 需要先進(jìn)行身份驗(yàn)證。用戶憑據(jù)一般存儲(chǔ)在 kubeconfig 文件中,但該文件可以存儲(chǔ)在其他不同位置。為了定位到它,我們可以執(zhí)行以下操作:
解析文件后,它會(huì)確定當(dāng)前要使用的上下文、當(dāng)前指向的集群以及當(dāng)前與用戶關(guān)聯(lián)的所有身份驗(yàn)證信息。如果用戶提供了額外的參數(shù)(例如 --username),則這些值優(yōu)先,并將覆蓋 kubeconfig 中指定的值。 一旦有了上述信息, Kubectl 就會(huì)填充客戶端的配置,以便它能夠適當(dāng)?shù)匦揎?HTTP 請(qǐng)求:
kube-apiserver認(rèn)證我們的請(qǐng)求已經(jīng)發(fā)送成功,接下來(lái)呢?kube-apiserver!kube-apiserver 是客戶端和系統(tǒng)組件用來(lái)持久化和檢索集群狀態(tài)的主要接口。為了執(zhí)行其功能,它需要能夠驗(yàn)證請(qǐng)求是否合法,這個(gè)過(guò)程被稱為認(rèn)證 (Authentication)。 為了驗(yàn)證請(qǐng)求,當(dāng)服務(wù)器首次啟動(dòng)時(shí),kube-apiserver 會(huì)查看用戶提供的所有 CLI 參數(shù),并組裝合適的 authenticator 列表。 舉個(gè)例子:
每次收到請(qǐng)求時(shí),它都會(huì)遍歷 authenticator 列表進(jìn)行認(rèn)證,直到成功為止:
如果認(rèn)證失敗,則請(qǐng)求失敗并返回匯總的錯(cuò)誤信息。 如果成功,則從請(qǐng)求中刪除 Authorization 標(biāo)頭,并將用戶信息添加到其上下文中,為之后的操作(例如授權(quán)和準(zhǔn)入控制器)提供訪問(wèn)先前建立的用戶身份的能力。 授權(quán)請(qǐng)求已發(fā)送,kube-apiserver 也已成功驗(yàn)證我們是誰(shuí),所以我們終于解脫了?想太多! 雖然我們證明了自己是誰(shuí),但還沒(méi)證明有權(quán)執(zhí)行此操作。畢竟身份(identity)和許可(permission)并不是一回事。因此,kube-apiserver 需要授權(quán)。 kube-apiserver 處理授權(quán)的方式與身份驗(yàn)證非常相似:基于 CLI 參數(shù)輸入,匯集一系列 authorizer,這些 authorizer 將針對(duì)每個(gè)傳入請(qǐng)求運(yùn)行。如果所有 authorizer 都拒絕該請(qǐng)求,則該請(qǐng)求將導(dǎo)致 Forbidden 響應(yīng)并不再繼續(xù)。如果單個(gè) authorizer 被批準(zhǔn),則請(qǐng)求繼續(xù)。 Kubernetes v1.14 的 authorizer 實(shí)例:
Admission Controller好的,到目前為止,我們已經(jīng)過(guò)認(rèn)證并獲得了 kube-apiserver 的授權(quán)。那接下來(lái)呢?從 kube-apiserver 的角度來(lái)看,它已經(jīng)驗(yàn)證了我們的身份并授權(quán)我們執(zhí)行后續(xù)操作,但對(duì)于 Kubernetes,系統(tǒng)的其他組件對(duì)此還有不少疑義,所以 Admission Controller 該閃亮登場(chǎng)了。 雖然認(rèn)證的重點(diǎn)在于證實(shí)用戶是否具有權(quán)限,但是 Admission Controllers 仍會(huì)攔截該請(qǐng)求,以確保它符合集群更廣泛的期望和規(guī)則。它們是對(duì)象持久化到 etcd 之前的最后一個(gè)堡壘,因此它們封裝了剩余的系統(tǒng)檢查以確保操作不會(huì)產(chǎn)生意外或負(fù)面結(jié)果。 Admission Controller 的工作方式類似于 Authentication 和 Authorization,但有一個(gè)區(qū)別:如果單個(gè) Admission Controller 失敗,則整個(gè)鏈斷開(kāi),請(qǐng)求將失敗。 Admission Controller 設(shè)計(jì)的真正優(yōu)勢(shì)在于它致力于提升可擴(kuò)展性。每個(gè)控制器都作為插件存儲(chǔ)在 plugin/pkg/admission 目錄中,最后編譯進(jìn) kube-apiserver 二進(jìn)制文件。 Kubernetes 目前提供十多種 Admission Controller,此處建議閱讀文檔:https://cs.kubernetes. ... lers/ etcd到目前為止, Kubernetes 已經(jīng)完全審查了傳入的請(qǐng)求,并允許它繼續(xù)往下走。在下一步中,kube-apiserver 將反序列化 HTTP 請(qǐng)求,構(gòu)造運(yùn)行時(shí)對(duì)象(有點(diǎn)像 Kubectl generator 的逆過(guò)程),并將它們持久化到 etcd。這里我們稍微分析一下。kube-apiserver 是怎么知道在接受我們的請(qǐng)求時(shí)該怎么做的呢? 在提供任何請(qǐng)求之前,kube-apiserver 會(huì)發(fā)生一系列非常復(fù)雜的步驟。讓我們從第一次運(yùn)行 kube-apiserver 二進(jìn)制文件開(kāi)始:
現(xiàn)在,kube-apiserver 已經(jīng)知道存在哪些路由及內(nèi)部映射,當(dāng)請(qǐng)求匹配時(shí),它可以調(diào)用相應(yīng)的處理程序和存儲(chǔ)程序。這是非常完美的設(shè)計(jì)模式。讓我們假設(shè) HTTP 請(qǐng)求已經(jīng)被 kube-apiserver 收到了:
這么多步驟,能夠堅(jiān)持走到這里是非常了不起的!同時(shí),apiserver 實(shí)際上也做了很多工作。總結(jié)一下:我們部署的 Deployment 現(xiàn)在存在于 etcd 中,但仍沒(méi)有看到它真正地 work…… 注:在 Kubernetes v1.14 之前,這往后還有 Initializer 的步驟,該步驟在 v1.14 被 webhook admission 取代。 控制循環(huán)Deployment Controller截至目前,我們的 Deployment 已經(jīng)存儲(chǔ)于 etcd 中,并且所有的初始化邏輯都已完成。接下來(lái)的階段將涉及 Deployment 所依賴的資源拓?fù)浣Y(jié)構(gòu)。在 Kubernetes, Deployment 實(shí)際上只是 ReplicaSet 的集合,而 ReplicaSet 是 Pod 的集合。那么 Kubernetes 如何從一個(gè) HTTP 請(qǐng)求創(chuàng)建這個(gè)層次結(jié)構(gòu)呢?這就不得不提 Kubernetes 的內(nèi)置控制器(Controller)。 Kubernetes 系統(tǒng)中使用了大量的 Controller, Controller 是一個(gè)用于將系統(tǒng)狀態(tài)從當(dāng)前狀態(tài)調(diào)諧到期望狀態(tài)的異步腳本。所有內(nèi)置的 Controller 都通過(guò)組件 kube-controller-manager 并行運(yùn)行,每種 Controller 都負(fù)責(zé)一種具體的控制流程。 首先,我們介紹一下 Deployment Controller: 將 Deployment 存儲(chǔ)到 etcd 后,我們可以通過(guò) kube-apiserver 使其可見(jiàn)。當(dāng)這個(gè)新資源可用時(shí), Deployment controller 會(huì)檢測(cè)到它,它的工作是監(jiān)聽(tīng) Deployment 的更改。在我們的例子中, Controller 通過(guò)注冊(cè)創(chuàng)建事件的回調(diào)函數(shù)(更多相關(guān)信息,參見(jiàn)下文)。 當(dāng)我們的 Deployment 首次可用時(shí),將執(zhí)行此回調(diào)函數(shù),并將該對(duì)象添加到內(nèi)部工作隊(duì)列(internal work queue)。 當(dāng)它處理我們的 Deployment 對(duì)象時(shí),控制器將檢查我們的 Deployment 并意識(shí)到?jīng)]有與之關(guān)聯(lián)的 ReplicaSet 或 Pod。 它通過(guò)使用標(biāo)簽選擇器(label selectors)查詢 kube-apiserver 來(lái)實(shí)現(xiàn)此功能。有趣的是,這個(gè)同步過(guò)程是狀態(tài)不可知的。另外,它以相同的方式調(diào)諧新對(duì)象和已存在的對(duì)象。 在意識(shí)到?jīng)]有與其關(guān)聯(lián)的 ReplicaSet 或 Pod 后,Deployment Controller 就會(huì)開(kāi)始執(zhí)行彈性伸縮流程(scaling process)。它通過(guò)推出(如創(chuàng)建)一個(gè) ReplicaSet, 為其分配 label selector 并將其版本號(hào)設(shè)置為 1。 ReplicaSet 的 PodSpec 字段是從 Deployment 的 manifest 以及其他相關(guān)元數(shù)據(jù)中復(fù)制而來(lái)。有時(shí) Deployment 在此之后也需要更新(例如,如果設(shè)置了 process deadline)。 當(dāng)完成以上步驟之后,該 Deployment 的 status 就會(huì)被更新,然后重新進(jìn)入與之前相同的循環(huán),等待 Deployment 與期望的狀態(tài)相匹配。由于 Deployment Controller 只關(guān)心 ReplicaSet, 因此需要 ReplicaSet Controller 繼續(xù)調(diào)諧過(guò)程。 ReplicaSet Controller在上一步中,Deployment 控制器創(chuàng)建了屬于該 Deployment 的第一個(gè) ReplicaSet, 但仍然沒(méi)有創(chuàng)建 Pod。所以這里我們要引入一個(gè)新東西:ReplicaSet 控制器!ReplicaSet 控制器的作用是監(jiān)視 ReplicaSet 及其相關(guān)資源 Pod 的生命周期。與大多數(shù)其它控制器一樣,它通過(guò)觸發(fā)某些事件的處理程序來(lái)實(shí)現(xiàn)目標(biāo)。 當(dāng)創(chuàng)建 ReplicaSet 時(shí)(由 Deployment 控制器創(chuàng)建),ReplicaSet 控制器會(huì)檢查新 ReplicaSet 的狀態(tài),并意識(shí)到現(xiàn)有狀態(tài)與期望狀態(tài)之間存在偏差。然后,它會(huì)嘗試通過(guò)調(diào)整 Pod 的副本數(shù)來(lái)調(diào)諧這種狀態(tài)。 Pod 的創(chuàng)建也是批量進(jìn)行的,從數(shù)量 SlowStartInitialBatchSize 開(kāi)始,然后在每次成功的迭代中以一種 slow start 操作加倍。這樣做的目的是在大量 Pod 啟動(dòng)失敗時(shí)(如由于資源配額),可以減輕 kube-apiserver 被大量不必要的 HTTP 請(qǐng)求吞沒(méi)的風(fēng)險(xiǎn)。 Kubernetes 通過(guò) Owner References (子資源的某個(gè)字段中引用其父資源的 ID) 來(lái)執(zhí)行嚴(yán)格的資源對(duì)象層級(jí)結(jié)構(gòu)。這確保了 Controller 管理的資源被刪除(級(jí)聯(lián)刪除)時(shí),子資源就會(huì)被垃圾收集器刪除。同時(shí),它還為父資源提供了一種有效的方式來(lái)避免競(jìng)爭(zhēng)同一個(gè)子資源(想象兩對(duì)父母認(rèn)為他們擁有同一個(gè)孩子的場(chǎng)景)。 Owner References 的另一個(gè)好處是,它是有狀態(tài)的。如果重啟任何的 Controller,那么由于資源對(duì)象的拓?fù)潢P(guān)系與 Controller 無(wú)關(guān),該重啟時(shí)間不會(huì)影響到系統(tǒng)的穩(wěn)定運(yùn)行。這種對(duì)資源隔離的重視也體現(xiàn)在 Controller 本身的設(shè)計(jì)中:Controller 不能對(duì)自己沒(méi)有明確擁有的資源進(jìn)行操作,它們之間互不干涉,互不共享。 有時(shí)系統(tǒng)中也會(huì)出現(xiàn)孤兒 (orphaned) 資源,通常由以下兩種途徑產(chǎn)生:
當(dāng)發(fā)生這種情況時(shí), Controller 將會(huì)確保孤兒資源擁有新的 Owner。多個(gè)父資源可以相互競(jìng)爭(zhēng)同一個(gè)孤兒資源,但只有一個(gè)會(huì)成功(其他父資源會(huì)收到一個(gè)驗(yàn)證錯(cuò)誤)。 Informers你可能已經(jīng)注意到,有些 Controller(例如 RBAC 授權(quán)器或 Deployment Controller)需要檢索集群狀態(tài)然后才能正常運(yùn)行。以 RBAC 授權(quán)器為例,當(dāng)請(qǐng)求進(jìn)入時(shí),授權(quán)器會(huì)將用戶的初始狀態(tài)緩存下來(lái)供以后使用,然后用它來(lái)檢索與 etcd 中的用戶關(guān)聯(lián)的所有角色(Role)和角色綁定(RoleBinding)。 那么 Controller 是如何訪問(wèn)和修改這些資源對(duì)象的呢?答案是引入 Informer。 Infomer 是一種模式,它允許 Controller 訂閱存儲(chǔ)事件并列出它們感興趣的資源。除了提供一個(gè)很好的工作抽象,它還需要處理很多細(xì)節(jié),如緩存。通過(guò)使用這種設(shè)計(jì),它還允許控制器以線程安全的方式進(jìn)行交互,而不必?fù)?dān)心線程沖突。 有關(guān) Informer 的更多信息,可深入閱讀:http://thub. ... tores Scheduler當(dāng)所有的 Controller 正常運(yùn)行后,etcd 中就會(huì)保存一個(gè) Deployment、一個(gè) ReplicaSet 和 三個(gè) Pod, 并且可以通過(guò) kube-apiserver 查看到。然而,這些 Pod 還處于 Pending 狀態(tài),因?yàn)樗鼈冞€沒(méi)有被調(diào)度到集群中合適的 Node 上。最終解決這個(gè)問(wèn)題的 Controller 是 Scheduler。Scheduler 作為一個(gè)獨(dú)立的組件運(yùn)行在集群控制平面上,工作方式與其他 Controller 相同:監(jiān)聽(tīng)事件并調(diào)諧狀態(tài)。 具體來(lái)說(shuō), Scheduler 的作用是過(guò)濾 PodSpec 中 NodeName 字段為空的 Pod 并嘗試將其調(diào)度到合適的節(jié)點(diǎn)。 為了找到合適的節(jié)點(diǎn), Scheduler 會(huì)使用特定的算法,默認(rèn)調(diào)度算法工作流程如下:
一旦算法找到了合適的節(jié)點(diǎn), Scheduler 就會(huì)創(chuàng)建一個(gè) Binding 對(duì)象,該對(duì)象的 Name 和 Uid 與 Pod 相匹配,并且其 ObjectReference 字段包含所選節(jié)點(diǎn)的名稱,然后通過(guò)發(fā)送 POST 請(qǐng)求給 apiserver。 當(dāng) kube-apiserver 接收到此 Binding 對(duì)象時(shí),注冊(cè)表會(huì)將該對(duì)象反序列化(registry deserializes)并更新 Pod 資源中的以下字段:
一旦 Scheduler 將 Pod 調(diào)度到某個(gè)節(jié)點(diǎn)上,該節(jié)點(diǎn)的 Kubelet 就會(huì)接管該 Pod 并開(kāi)始部署。 附注:自定義調(diào)度器:有趣的是預(yù)測(cè)和優(yōu)先級(jí)函數(shù) (predicates and priority functions) 都是可擴(kuò)展的,可以使用 --policy-config-file 標(biāo)志來(lái)定義。這引入了一定程度的靈活性。管理員還可以在獨(dú)立部署中運(yùn)行自定義調(diào)度器(具有自定義處理邏輯的控制器)。如果 PodSpec 中包含 schedulerName,Kubernetes 會(huì)將該 pod 的調(diào)度移交給使用該名稱注冊(cè)的調(diào)度器。 KubeletPod Sync截至目前,所有的 Controller 都完成了工作,讓我們來(lái)總結(jié)一下:
然而,到目前為止,所有的狀態(tài)變化僅僅只是針對(duì)保存在 etcd 中的資源對(duì)象,接下來(lái)的步驟涉及到在 Worker 節(jié)點(diǎn)之間運(yùn)行具體的容器,這是分布式系統(tǒng) Kubernetes 的關(guān)鍵因素。這些事情都是由 Kubelet 完成的。 在 Kubernetes 集群中,每個(gè) Node 節(jié)點(diǎn)上都會(huì)啟動(dòng)一個(gè) Kubelet 服務(wù)進(jìn)程,該進(jìn)程用于處理 Scheduler 下發(fā)到本節(jié)點(diǎn)的任務(wù),管理 Pod 的生命周期。這意味著它將處理 Pod 與 Container Runtime 之間所有的轉(zhuǎn)換邏輯,包括掛載卷、容器日志、垃圾回收以及其他重要事件。 一個(gè)有用的方法,你可以把 Kubelet 當(dāng)成一種特殊的 Controller,它每隔 20 秒(可以自定義)向 kube-apiserver 查詢 Pod,過(guò)濾 NodeName 與自身所在節(jié)點(diǎn)匹配的 Pod 列表。 一旦獲取到了這個(gè)列表,它就會(huì)通過(guò)與自己的內(nèi)部緩存進(jìn)行比較來(lái)檢測(cè)差異,如果有差異,就開(kāi)始同步 Pod 列表。我們來(lái)看看同步過(guò)程是什么樣的:
CRI 和 pause 容器到了這個(gè)階段,大量的初始化工作都已經(jīng)完成,容器已經(jīng)準(zhǔn)備好開(kāi)始啟動(dòng)了,而容器是由容器運(yùn)行時(shí)(例如 Docker) 啟動(dòng)的。為了更具可擴(kuò)展性, Kubelet 使用 CRI 來(lái)與具體的容器運(yùn)行時(shí)進(jìn)行交互。簡(jiǎn)而言之, CRI 提供了 Kubelet 和特定容器運(yùn)行時(shí)實(shí)現(xiàn)之間的抽象。通過(guò) protocol buffers(一種更快的 JSON) 和 gRPC API(一種非常適合執(zhí)行 Kubernetes 操作的API)進(jìn)行通信。 這是一個(gè)非??岬南敕?,因?yàn)橥ㄟ^(guò)在 Kubelet 和容器運(yùn)行時(shí)之間使用已定義的接口約定,容器編排的實(shí)際實(shí)現(xiàn)細(xì)節(jié)變得無(wú)關(guān)緊要。重要的是接口約定,這允許以最小的開(kāi)銷添加新的容器運(yùn)行時(shí),因?yàn)闆](méi)有核心 Kubernetes 代碼需要更改! 回到部署我們的容器,當(dāng)一個(gè) Pod 首次啟動(dòng)時(shí), Kubelet 調(diào)用 RunPodSandbox 遠(yuǎn)程過(guò)程命令。沙箱是描述一組容器的 CRI 術(shù)語(yǔ),在 Kubernetes 中對(duì)應(yīng)的是 Pod。這個(gè)術(shù)語(yǔ)是故意模糊的,因此其他不使用容器的運(yùn)行時(shí),不會(huì)失去其意義(想象一個(gè)基于 hypervisor 的運(yùn)行時(shí),沙箱可能指的是 VM)。 在我們的例子中,我們使用的是 Docker,所以在此容器運(yùn)行時(shí)中,創(chuàng)建沙箱涉及創(chuàng)建 pause 容器。 pause 容器像 Pod 中的所有其他容器的父級(jí)一樣,因?yàn)樗休d了工作負(fù)載容器最終將使用的許多 Pod 級(jí)資源。這些“資源”是 Linux Namespaces(IPC、Network、PID)。 pause 容器提供了一種托管所有這些 Namespaces 的方法,并允許子容器共享它們。成為同一 Network Namespace 一部分的好處是同一個(gè) Pod 中的容器可以使用 localhost 相互訪問(wèn)。 pause 容器的第二個(gè)好處與 PID Namespace 有關(guān)。在這些 Namespace 中,進(jìn)程形成一個(gè)分層樹,頂部的“init” 進(jìn)程負(fù)責(zé)“收獲”僵尸進(jìn)程。更多信息,請(qǐng)深入閱讀:https://www./en/al ... ainer 創(chuàng)建 pause 容器后,將開(kāi)始檢查磁盤狀態(tài)然后啟動(dòng)主容器。 CNI 和 Pod 網(wǎng)絡(luò)現(xiàn)在,我們的 Pod 有了基本的骨架:一個(gè) pause 容器,它托管所有 Namespaces 以允許 Pod 間通信。但容器的網(wǎng)絡(luò)如何運(yùn)作以及建立的?當(dāng) Kubelet 為 Pod 設(shè)置網(wǎng)絡(luò)時(shí),它會(huì)將任務(wù)委托給 CNI(Container Network Interface)插件,其運(yùn)行方式與 Container Runtime Interface 類似。簡(jiǎn)而言之,CNI 是一種抽象,允許不同的網(wǎng)絡(luò)提供商對(duì)容器使用不同的網(wǎng)絡(luò)實(shí)現(xiàn)。 Kubelet 通過(guò) stdin 將 JSON 數(shù)據(jù)(配置文件位于 /etc/cni/net.d 中)傳輸?shù)较嚓P(guān)的 CNI 二進(jìn)制文件(位于 /opt/cni/bin) 中與之交互。下面是 JSON 配置文件的一個(gè)簡(jiǎn)單示例 : { CNI 插件還可以通過(guò) CNI_ARGS 環(huán)境變量為 Pod 指定其他的元數(shù)據(jù),包括 Pod Name 和 Namespace。 接下來(lái)會(huì)發(fā)生什么取決于 CNI 插件,這里我們以 bridge CNI 插件為例:
跨主機(jī)容器網(wǎng)絡(luò)到目前為止,我們已經(jīng)描述了容器如何與宿主機(jī)進(jìn)行通信,但跨主機(jī)之間的容器如何通信呢?通常情況下, Kubernetes 使用 Overlay 網(wǎng)絡(luò)來(lái)進(jìn)行跨主機(jī)容器通信,這是一種動(dòng)態(tài)同步多個(gè)主機(jī)間路由的方法。一個(gè)較常用的 Overlay 網(wǎng)絡(luò)插件是 flannel,它提供了跨節(jié)點(diǎn)的三層網(wǎng)絡(luò)。 Flannel 不會(huì)管容器與宿主機(jī)之間的通信(這是 CNI 插件的職責(zé)),但它對(duì)主機(jī)間的流量傳輸負(fù)責(zé)。為此,它為主機(jī)選擇一個(gè)子網(wǎng)并將其注冊(cè)到 etcd,然后保留集群路由的本地表示,并將傳出的數(shù)據(jù)包封裝在 UDP 數(shù)據(jù)報(bào)中,確保它到達(dá)正確的主機(jī)。 更多信息,請(qǐng)深入閱讀:https://github.com/coreos/flannel 啟動(dòng)容器所有的網(wǎng)絡(luò)配置都已完成。還剩什么?真正啟動(dòng)工作負(fù)載容器!一旦 sanbox 完成初始化并處于 active 狀態(tài), Kubelet 將開(kāi)始為其創(chuàng)建容器。首先啟動(dòng) PodSpec 中定義的 Init Container,然后再啟動(dòng)主容器,具體過(guò)程如下:
Wrap-up最后的最后,現(xiàn)在我們的集群上應(yīng)該會(huì)運(yùn)行三個(gè)容器,分布在一個(gè)或多個(gè)工作節(jié)點(diǎn)上。所有的網(wǎng)絡(luò)、數(shù)據(jù)卷和秘鑰都由 Kubelet 填充,并通過(guò) CRI 接口添加到容器中,配置成功!原文地址:https://github.com/jamiehannaf ... n-k8s 源代碼版譯文地址(強(qiáng)烈建議閱讀):https://github.com/bbbmj/what-happens-when-k8s 原文鏈接:https://mp.weixin.qq.com/s/KqRt1eav5mNaut5PK45D-Q 【編者的話】為了確保整體的簡(jiǎn)單性和易上手,有時(shí) Kubernetes 會(huì)通過(guò)一些簡(jiǎn)單的抽象隱去操作背后的復(fù)雜邏輯,但作為一名有夢(mèng)想的工程師,掌握其背后的真正思路是十分有必要的。本文以 Kubectl 創(chuàng)建 Pod 為例,向你揭露從客戶端到 Kubelet 的請(qǐng)... |
|