日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問(wèn) 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 编程资源 > 编程问答 >内容正文

编程问答

创建 tls 客户端 凭据时发生严重错误。内部错误状态为 10013_kubectl 创建 Pod 背后到底发生了什么?...

發(fā)布時(shí)間:2024/4/11 编程问答 85 豆豆
生活随笔 收集整理的這篇文章主要介紹了 创建 tls 客户端 凭据时发生严重错误。内部错误状态为 10013_kubectl 创建 Pod 背后到底发生了什么?... 小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

kubectl 創(chuàng)建 Pod 背后到底發(fā)生了什么?

想象一下,如果我想將 nginx 部署到 Kubernetes 集群,我可能會(huì)在終端中輸入類似這樣的命令:

$ kubectl run --image=nginx --replicas=3

然后回車。幾秒鐘后,你就會(huì)看到三個(gè) nginx pod 分布在所有的工作節(jié)點(diǎn)上。這一切就像變魔術(shù)一樣,但你并不知道這一切的背后究竟發(fā)生了什么事情。

Kubernetes 的神奇之處在于:它可以通過(guò)用戶友好的 API 來(lái)處理跨基礎(chǔ)架構(gòu)的 deployments,而背后的復(fù)雜性被隱藏在簡(jiǎn)單的抽象中。但為了充分理解它為我們提供的價(jià)值,我們需要理解它的內(nèi)部原理。

本指南將引導(dǎo)您理解從 client 到 Kubelet 的請(qǐng)求的完整生命周期,必要時(shí)會(huì)通過(guò)源代碼來(lái)說(shuō)明背后發(fā)生了什么。

這是一份可以在線修改的文檔,如果你發(fā)現(xiàn)有什么可以改進(jìn)或重寫的,歡迎提供幫助!

1. kubectl

驗(yàn)證和生成器

當(dāng)敲下回車鍵以后,kubectl 首先會(huì)執(zhí)行一些客戶端驗(yàn)證操作,以確保不合法的請(qǐng)求(例如,創(chuàng)建不支持的資源或使用格式錯(cuò)誤的鏡像名稱)將會(huì)快速失敗,也不會(huì)發(fā)送給 kube-apiserver。通過(guò)減少不必要的負(fù)載來(lái)提高系統(tǒng)性能。

驗(yàn)證通過(guò)之后, kubectl 開始將發(fā)送給 kube-apiserver 的 HTTP 請(qǐng)求進(jìn)行封裝。kube-apiserver 與 etcd 進(jìn)行通信,所有嘗試訪問(wèn)或更改 Kubernetes 系統(tǒng)狀態(tài)的請(qǐng)求都會(huì)通過(guò) kube-apiserver 進(jìn)行,kubectl 也不例外。kubectl 使用生成器(generators)來(lái)構(gòu)造 HTTP 請(qǐng)求。生成器是一個(gè)用來(lái)處理序列化的抽象概念。

通過(guò) kubectl run 不僅可以運(yùn)行 deployment,還可以通過(guò)指定參數(shù) --generator 來(lái)部署其他多種資源類型。如果沒(méi)有指定 --generator 參數(shù)的值,kubectl 將會(huì)自動(dòng)判斷資源的類型。

例如,帶有參數(shù) --restart-policy=Always 的資源將被部署為 Deployment,而帶有參數(shù) --restart-policy=Never 的資源將被部署為 Pod。同時(shí) kubectl 也會(huì)檢查是否需要觸發(fā)其他操作,例如記錄命令(用來(lái)進(jìn)行回滾或?qū)徲?jì))。

在 kubectl 判斷出要?jiǎng)?chuàng)建一個(gè) Deployment 后,它將使用 DeploymentV1Beta1 生成器從我們提供的參數(shù)中生成一個(gè)運(yùn)行時(shí)對(duì)象。

API 版本協(xié)商與 API 組

為了更容易地消除字段或者重新組織資源結(jié)構(gòu),Kubernetes 支持多個(gè) API 版本,每個(gè)版本都在不同的 API 路徑下,例如 /api/v1 或者 /apis/extensions/v1beta1。不同的 API 版本表明不同的穩(wěn)定性和支持級(jí)別,更詳細(xì)的描述可以參考 Kubernetes API 概述。

API 組旨在對(duì)類似資源進(jìn)行分類,以便使得 Kubernetes API 更容易擴(kuò)展。API 的組名在 REST 路徑或者序列化對(duì)象的 apiVersion 字段中指定。例如,Deployment 的 API 組名是 apps,最新的 API 版本是 v1beta2,這就是為什么你要在 Deployment manifests 頂部輸入 apiVersion: apps/v1beta2。

kubectl 在生成運(yùn)行時(shí)對(duì)象后,開始為它找到適當(dāng)?shù)?API 組和 API 版本,然后組裝成一個(gè)版本化客戶端,該客戶端知道資源的各種 REST 語(yǔ)義。該階段被稱為版本協(xié)商,kubectl 會(huì)掃描 remote API 上的 /apis 路徑來(lái)檢索所有可能的 API 組。由于 kube-apiserver 在 /apis 路徑上公開了 OpenAPI 格式的規(guī)范文檔, 因此客戶端很容易找到合適的 API。

為了提高性能,kubectl 將 OpenAPI 規(guī)范緩存到了 ~/.kube/cache 目錄。如果你想了解 API 發(fā)現(xiàn)的過(guò)程,請(qǐng)嘗試刪除該目錄并在運(yùn)行 kubectl 命令時(shí)將 -v 參數(shù)的值設(shè)為最大值,然后你將會(huì)看到所有試圖找到這些 API 版本的HTTP 請(qǐng)求。參考 kubectl 備忘單。

最后一步才是真正地發(fā)送 HTTP 請(qǐng)求。一旦請(qǐng)求發(fā)送之后獲得成功的響應(yīng),kubectl 將會(huì)根據(jù)所需的輸出格式打印 success message。

客戶端身份認(rèn)證

在發(fā)送 HTTP 請(qǐng)求之前還要進(jìn)行客戶端認(rèn)證,這是之前沒(méi)有提到的,現(xiàn)在可以來(lái)看一下。

為了能夠成功發(fā)送請(qǐng)求,kubectl 需要先進(jìn)行身份認(rèn)證。用戶憑證保存在 kubeconfig 文件中,kubectl 通過(guò)以下順序來(lái)找到 kubeconfig 文件:

  • 如果提供了 --kubeconfig 參數(shù), kubectl 就使用 –kubeconfig 參數(shù)提供的 kubeconfig 文件。
  • 如果沒(méi)有提供 –kubeconfig 參數(shù),但設(shè)置了環(huán)境變量 $KUBECONFIG,則使用該環(huán)境變量提供的 kubeconfig 文件。
  • 如果 –kubeconfig 參數(shù)和環(huán)境變量 $KUBECONFIG 都沒(méi)有提供,kubectl 就使用默認(rèn)的 kubeconfig 文件 $HOME/.kube/config。

解析完 kubeconfig 文件后,kubectl 會(huì)確定當(dāng)前要使用的上下文、當(dāng)前指向的群集以及與當(dāng)前用戶關(guān)聯(lián)的任何認(rèn)證信息。如果用戶提供了額外的參數(shù)(例如 –username),則優(yōu)先使用這些參數(shù)覆蓋 kubeconfig 中指定的值。一旦拿到這些信息之后, kubectl 就會(huì)把這些信息填充到將要發(fā)送的 HTTP 請(qǐng)求頭中:

  • x509 證書使用 tls.TLSConfig 發(fā)送(包括 CA 證書)。
  • bearer tokens 在 HTTP 請(qǐng)求頭 Authorization 中發(fā)送。
  • 用戶名和密碼通過(guò) HTTP 基本認(rèn)證發(fā)送。
  • OpenID 認(rèn)證過(guò)程是由用戶事先手動(dòng)處理的,產(chǎn)生一個(gè)像 bearer token 一樣被發(fā)送的 token。

2. kube-apiserver


認(rèn)證

現(xiàn)在我們的請(qǐng)求已經(jīng)發(fā)送成功了,接下來(lái)將會(huì)發(fā)生什么?這時(shí)候就該 kube-apiserver 閃亮登場(chǎng)了!kube-apiserver 是客戶端和系統(tǒng)組件用來(lái)保存和檢索集群狀態(tài)的主要接口。為了執(zhí)行相應(yīng)的功能,kube-apiserver 需要能夠驗(yàn)證請(qǐng)求者是合法的,這個(gè)過(guò)程被稱為認(rèn)證。

那么 apiserver 如何對(duì)請(qǐng)求進(jìn)行認(rèn)證呢?當(dāng) kube-apiserver 第一次啟動(dòng)時(shí),它會(huì)查看用戶提供的所有 CLI 參數(shù),并組合成一個(gè)合適的令牌列表。

舉個(gè)例子:如果提供了 --client-ca-file 參數(shù),則會(huì)將 x509 客戶端證書認(rèn)證添加到令牌列表中;如果提供了 --token-auth-file 參數(shù),則會(huì)將 breaer token 添加到令牌列表中。

每次收到請(qǐng)求時(shí),apiserver 都會(huì)通過(guò)令牌鏈進(jìn)行認(rèn)證,直到某一個(gè)認(rèn)證成功為止:

  • x509 處理程序?qū)Ⅱ?yàn)證 HTTP 請(qǐng)求是否是由 CA 根證書簽名的 TLS 密鑰進(jìn)行編碼的。
  • bearer token 處理程序?qū)Ⅱ?yàn)證 --token-auth-file 參數(shù)提供的 token 文件是否存在。
  • 基本認(rèn)證處理程序確保 HTTP 請(qǐng)求的基本認(rèn)證憑證與本地的狀態(tài)匹配。

如果認(rèn)證失敗,則請(qǐng)求失敗并返回相應(yīng)的錯(cuò)誤信息;如果驗(yàn)證成功,則將請(qǐng)求中的 Authorization 請(qǐng)求頭刪除,并將用戶信息添加到其上下文中。這給后續(xù)的授權(quán)和準(zhǔn)入控制器提供了訪問(wèn)之前建立的用戶身份的能力。

授權(quán)

OK,現(xiàn)在請(qǐng)求已經(jīng)發(fā)送,并且 kube-apiserver 已經(jīng)成功驗(yàn)證我們是誰(shuí),終于解脫了!

然而事情并沒(méi)有結(jié)束,雖然我們已經(jīng)證明了我們是合法的,但我們有權(quán)執(zhí)行此操作嗎?畢竟身份和權(quán)限不是一回事。為了進(jìn)行后續(xù)的操作,kube-apiserver 還要對(duì)用戶進(jìn)行授權(quán)。

kube-apiserver 處理授權(quán)的方式與處理身份驗(yàn)證的方式相似:通過(guò) kube-apiserver 的啟動(dòng)參數(shù) --authorization_mode 參數(shù)設(shè)置。它將組合一系列授權(quán)者,這些授權(quán)者將針對(duì)每個(gè)傳入的請(qǐng)求進(jìn)行授權(quán)。如果所有授權(quán)者都拒絕該請(qǐng)求,則該請(qǐng)求會(huì)被禁止響應(yīng)并且不會(huì)再繼續(xù)響應(yīng)。如果某個(gè)授權(quán)者批準(zhǔn)了該請(qǐng)求,則請(qǐng)求繼續(xù)。

kube-apiserver 目前支持以下幾種授權(quán)方法:

  • webhook: 它與集群外的 HTTP(S) 服務(wù)交互。
  • ABAC: 它執(zhí)行靜態(tài)文件中定義的策略。
  • RBAC: 它使用 rbac.authorization.k8s.io API Group實(shí)現(xiàn)授權(quán)決策,允許管理員通過(guò) Kubernetes API 動(dòng)態(tài)配置策略。
  • Node: 它確保 kubelet 只能訪問(wèn)自己節(jié)點(diǎn)上的資源。

準(zhǔn)入控制

突破了之前所說(shuō)的認(rèn)證和授權(quán)兩道關(guān)口之后,客戶端的調(diào)用請(qǐng)求就能夠得到 API Server 的真正響應(yīng)了嗎?答案是:不能!

從 kube-apiserver 的角度來(lái)看,它已經(jīng)驗(yàn)證了我們的身份并且賦予了相應(yīng)的權(quán)限允許我們繼續(xù),但對(duì)于 Kubernetes 而言,其他組件對(duì)于應(yīng)不應(yīng)該允許發(fā)生的事情還是很有意見(jiàn)的。所以這個(gè)請(qǐng)求還需要通過(guò) Admission Controller 所控制的一個(gè) 準(zhǔn)入控制鏈 的層層考驗(yàn),官方標(biāo)準(zhǔn)的 “關(guān)卡” 有近十個(gè)之多,而且還能自定義擴(kuò)展!

雖然授權(quán)的重點(diǎn)是回答用戶是否有權(quán)限,但準(zhǔn)入控制器會(huì)攔截請(qǐng)求以確保它符合集群的更廣泛的期望和規(guī)則。它們是資源對(duì)象保存到 etcd 之前的最后一個(gè)堡壘,封裝了一系列額外的檢查以確保操作不會(huì)產(chǎn)生意外或負(fù)面結(jié)果。不同于授權(quán)和認(rèn)證只關(guān)心請(qǐng)求的用戶和操作,準(zhǔn)入控制還處理請(qǐng)求的內(nèi)容,并且僅對(duì)創(chuàng)建、更新、刪除或連接(如代理)等有效,而對(duì)讀操作無(wú)效。

注意
準(zhǔn)入控制器的工作方式與授權(quán)者和驗(yàn)證者的工作方式類似,但有一點(diǎn)區(qū)別:與驗(yàn)證鏈和授權(quán)鏈不同,如果某個(gè)準(zhǔn)入控制器檢查不通過(guò),則整個(gè)鏈會(huì)中斷,整個(gè)請(qǐng)求將立即被拒絕并且返回一個(gè)錯(cuò)誤給終端用戶。

準(zhǔn)入控制器設(shè)計(jì)的重點(diǎn)在于提高可擴(kuò)展性,某個(gè)控制器都作為一個(gè)插件存儲(chǔ)在 plugin/pkg/admission 目錄中,并且與某一個(gè)接口相匹配,最后被編譯到 kube-apiserver 二進(jìn)制文件中。

大部分準(zhǔn)入控制器都比較容易理解,接下來(lái)著重介紹 SecurityContextDeny、ResourceQuota 及 LimitRanger 這三個(gè)準(zhǔn)入控制器。

  • SecurityContextDeny 該插件將禁止創(chuàng)建設(shè)置了 Security Context 的 Pod。
  • ResourceQuota 不僅能限制某個(gè) Namespace 中創(chuàng)建資源的數(shù)量,而且能限制某個(gè) Namespace 中被 Pod 所請(qǐng)求的資源總量。該準(zhǔn)入控制器和資源對(duì)象 ResourceQuota 一起實(shí)現(xiàn)了資源配額管理。
  • LimitRanger 作用類似于上面的 ResourceQuota 控制器,針對(duì) Namespace 資源的每個(gè)個(gè)體(Pod 與 Container 等)的資源配額。該插件和資源對(duì)象 LimitRange 一起實(shí)現(xiàn)資源配額管理。

3. etcd


到現(xiàn)在為止,Kubernetes 已經(jīng)對(duì)該客戶端的調(diào)用請(qǐng)求進(jìn)行了全面徹底地審查,并且已經(jīng)驗(yàn)證通過(guò),運(yùn)行它進(jìn)入下一個(gè)環(huán)節(jié)。下一步 kube-apiserver 將對(duì) HTTP 請(qǐng)求進(jìn)行反序列化,然后利用得到的結(jié)果構(gòu)建運(yùn)行時(shí)對(duì)象(有點(diǎn)像 kubectl 生成器的逆過(guò)程),并保存到 etcd 中。下面我們將這個(gè)過(guò)程分解一下。

當(dāng)收到請(qǐng)求時(shí),kube-apiserver 是如何知道它該怎么做的呢?事實(shí)上,在客戶端發(fā)送調(diào)用請(qǐng)求之前就已經(jīng)產(chǎn)生了一系列非常復(fù)雜的流程。我們就從 kube-apiserver 二進(jìn)制文件首次運(yùn)行開始分析吧:

  • 當(dāng)運(yùn)行 kube-apiserver 二進(jìn)制文件時(shí),它會(huì)創(chuàng)建一個(gè)允許 apiserver 聚合的服務(wù)鏈。這是一種對(duì) Kubernetes API 進(jìn)行擴(kuò)展的方式。
  • 同時(shí)會(huì)創(chuàng)建一個(gè) generic apiserver 作為默認(rèn)的 apiserver。
  • 然后利用生成的 OpenAPI 規(guī)范來(lái)填充 apiserver 的配置。
  • 然后 kube-apiserver 遍歷數(shù)據(jù)結(jié)構(gòu)中指定的所有 API 組,并將每一個(gè) API 組作為通用的存儲(chǔ)抽象保存到 etcd 中。當(dāng)你訪問(wèn)或變更資源狀態(tài)時(shí),kube-apiserver 就會(huì)調(diào)用這些 API 組。
  • 每個(gè) API 組都會(huì)遍歷它的所有組版本,并且將每個(gè) HTTP 路由映射到 REST 路徑中。
  • 當(dāng)請(qǐng)求的 METHOD 是 POST 時(shí),kube-apiserver 就會(huì)將請(qǐng)求轉(zhuǎn)交給 資源創(chuàng)建處理器。
  • 現(xiàn)在 kube-apiserver 已經(jīng)知道了所有的路由及其對(duì)應(yīng)的 REST 路徑,以便在請(qǐng)求匹配時(shí)知道調(diào)用哪些處理器和鍵值存儲(chǔ)。多么機(jī)智的設(shè)計(jì)!現(xiàn)在假設(shè)客戶端的 HTTP 請(qǐng)求已經(jīng)被 kube-apiserver 收到了:

  • 如果處理鏈可以將請(qǐng)求與已經(jīng)注冊(cè)的路由進(jìn)行匹配,就會(huì)將該請(qǐng)求交給注冊(cè)到該路由的專用處理器來(lái)處理;如果沒(méi)有任何一個(gè)路由可以匹配該請(qǐng)求,就會(huì)將請(qǐng)求轉(zhuǎn)交給基于路徑的處理器(比如當(dāng)調(diào)用 /apis 時(shí));如果沒(méi)有任何一個(gè)基于路徑的處理器注冊(cè)到該路徑,請(qǐng)求就會(huì)被轉(zhuǎn)交給 not found 處理器,最后返回 404。
  • 幸運(yùn)的是,我們有一個(gè)名為 createHandler 的注冊(cè)路由!它有什么作用呢?首先它會(huì)解碼 HTTP 請(qǐng)求并進(jìn)行基本的驗(yàn)證,例如確保請(qǐng)求提供的 json 與 API 資源的版本相匹配。
  • 接下來(lái)進(jìn)入審計(jì)和準(zhǔn)入控制階段。
  • 然后資源將會(huì)通過(guò) storage provider 保存到 etcd 中。默認(rèn)情況下保存到 etcd 中的鍵的格式為 <namespace>/<name>,你也可以自定義。
  • 資源創(chuàng)建過(guò)程中出現(xiàn)的任何錯(cuò)誤都會(huì)被捕獲,最后 storage provider 會(huì)執(zhí)行 get 調(diào)用來(lái)確認(rèn)該資源是否被成功創(chuàng)建。如果需要額外的清理工作,就會(huì)調(diào)用后期創(chuàng)建的處理器和裝飾器。
  • 最后構(gòu)造 HTTP 響應(yīng)并返回給客戶端。
  • 原來(lái) apiserver 做了這么多的工作,以前竟然沒(méi)有發(fā)現(xiàn)呢!到目前為止,我們創(chuàng)建的 Deployment 資源已經(jīng)保存到了 etcd 中,但 apiserver 仍然看不到它。

    4. 初始化


    在一個(gè)資源對(duì)象被持久化到數(shù)據(jù)存儲(chǔ)之后,apiserver 還無(wú)法完全看到或調(diào)度它,在此之前還要執(zhí)行一系列Initializers。Initializers是一種與資源類型相關(guān)聯(lián)的控制器,它會(huì)在資源對(duì)外可用之前執(zhí)行某些邏輯。如果某個(gè)資源類型沒(méi)有Initializers,就會(huì)跳過(guò)此初始化步驟立即使資源對(duì)外可見(jiàn)。

    正如大佬的博客指出的那樣,Initializers是一個(gè)強(qiáng)大的功能,因?yàn)樗试S我們執(zhí)行通用引導(dǎo)操作。例如:

    • 將代理邊車容器注入到暴露 80 端口的 Pod 中,或者加上特定的 annotation。
    • 將保存著測(cè)試證書的 volume 注入到特定命名空間的所有 Pod 中。
    • 如果 Secret 中的密碼小于 20 個(gè)字符,就組織其創(chuàng)建。

    initializerConfiguration 資源對(duì)象允許你聲明某些資源類型應(yīng)該運(yùn)行哪些Initializers。如果你想每創(chuàng)建一個(gè) Pod 時(shí)就運(yùn)行一個(gè)自定義Initializers,你可以這樣做:

    通過(guò)該配置創(chuàng)建資源對(duì)象 InitializerConfiguration 之后,就會(huì)在每個(gè) Pod 的 metadata.initializers.pending 字段中添加 custom-pod-initializer 字段。該初始化控制器會(huì)定期掃描新的 Pod,一旦在 Pod 的 pending 字段中檢測(cè)到自己的名稱,就會(huì)執(zhí)行其邏輯,執(zhí)行完邏輯之后就會(huì)將 pending 字段下的自己的名稱刪除。

    只有在 pending 字段下的列表中的第一個(gè)Initializers可以對(duì)資源進(jìn)行操作,當(dāng)所有的Initializers執(zhí)行完成,并且 pending 字段為空時(shí),該對(duì)象就會(huì)被認(rèn)為初始化成功。

    你可能會(huì)注意到一個(gè)問(wèn)題:如果 kube-apiserver 不能顯示這些資源,那么用戶級(jí)控制器是如何處理資源的呢?

    為了解決這個(gè)問(wèn)題,kube-apiserver 暴露了一個(gè) ?includeUninitialized 查詢參數(shù),它會(huì)返回所有的資源對(duì)象(包括未初始化的)。

    5. 控制循環(huán)


    Deployments controller

    到了這個(gè)階段,我們的 Deployment 記錄已經(jīng)保存在 etcd 中,并且所有的初始化邏輯都執(zhí)行完成,接下來(lái)的階段將會(huì)涉及到該資源所依賴的拓?fù)浣Y(jié)構(gòu)。在 Kubernetes 中,Deployment 實(shí)際上只是一系列 Replicaset 的集合,而 Replicaset 是一系列 Pod 的集合。那么 Kubernetes 是如何從一個(gè) HTTP 請(qǐng)求按照層級(jí)結(jié)構(gòu)依次創(chuàng)建這些資源的呢?其實(shí)這些工作都是由 Kubernetes 內(nèi)置的 Controller(控制器) 來(lái)完成的。

    Kubernetes 在整個(gè)系統(tǒng)中使用了大量的 Controller,Controller 是一個(gè)用于將系統(tǒng)狀態(tài)從“當(dāng)前狀態(tài)”修正到“期望狀態(tài)”的異步腳本。所有 Controller 都通過(guò) kube-controller-manager 組件并行運(yùn)行,每種 Controller 都負(fù)責(zé)一種具體的控制流程。首先介紹一下 Deployment Controller:

    將 Deployment 記錄存儲(chǔ)到 etcd 并初始化后,就可以通過(guò) kube-apiserver 使其可見(jiàn),然后 Deployment Controller 就會(huì)檢測(cè)到它(它的工作就是負(fù)責(zé)監(jiān)聽 Deployment 記錄的更改)。在我們的例子中,控制器通過(guò)一個(gè) Informer 注冊(cè)一個(gè)創(chuàng)建事件的特定回調(diào)函數(shù)(更多信息參加下文)。

    當(dāng) Deployment 第一次對(duì)外可見(jiàn)時(shí),該 Controller 就會(huì)將該資源對(duì)象添加到內(nèi)部工作隊(duì)列,然后開始處理這個(gè)資源對(duì)象:

    通過(guò)使用標(biāo)簽選擇器查詢 kube-apiserver 來(lái)檢查該 Deployment 是否有與其關(guān)聯(lián)的 ReplicaSet 或 Pod 記錄。

    有趣的是,這個(gè)同步過(guò)程是狀態(tài)不可知的,它核對(duì)新記錄與核對(duì)已經(jīng)存在的記錄采用的是相同的方式。

    在意識(shí)到?jīng)]有與其關(guān)聯(lián)的 ReplicaSet 或 Pod 記錄后,Deployment Controller 就會(huì)開始執(zhí)行彈性伸縮流程:

    創(chuàng)建 ReplicaSet 資源,為其分配一個(gè)標(biāo)簽選擇器并將其版本號(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,因此需要通過(guò) ReplicaSet Controller 來(lái)繼續(xù)協(xié)調(diào)。

    ReplicaSets controller

    在前面的步驟中,Deployment Controller 創(chuàng)建了第一個(gè) ReplicaSet,但仍然還是沒(méi)有 Pod,這時(shí)候就該 ReplicaSet Controller 登場(chǎng)了!ReplicaSet Controller 的工作是監(jiān)視 ReplicaSets 及其相關(guān)資源(Pod)的生命周期。和大多數(shù)其他 Controller 一樣,它通過(guò)觸發(fā)某些事件的處理器來(lái)實(shí)現(xiàn)此目的。

    當(dāng)創(chuàng)建 ReplicaSet 時(shí)(由 Deployment Controller 創(chuàng)建),RS Controller 檢查新 ReplicaSet 的狀態(tài),并檢查當(dāng)前狀態(tài)與期望狀態(tài)之間存在的偏差,然后通過(guò)調(diào)整 Pod 的副本數(shù)來(lái)達(dá)到期望的狀態(tài)。

    Pod 的創(chuàng)建也是批量進(jìn)行的,從 SlowStartInitialBatchSize 開始,然后在每次成功的迭代中以一種 slow start 操作加倍。這樣做的目的是在大量 Pod 啟動(dòng)失敗時(shí)(例如,由于資源配額),可以減輕 kube-apiserver 被大量不必要的 HTTP 請(qǐng)求吞沒(méi)的風(fēng)險(xiǎn)。如果創(chuàng)建失敗,最好能夠優(yōu)雅地失敗,并且對(duì)其他的系統(tǒng)組件造成的影響最小!

    Kubernetes 通過(guò) Owner References(在子級(jí)資源的某個(gè)字段中引用其父級(jí)資源的 ID) 來(lái)構(gòu)造嚴(yán)格的資源對(duì)象層級(jí)結(jié)構(gòu)。這確保了一旦 Controller 管理的資源被刪除(級(jí)聯(lián)刪除),子資源就會(huì)被垃圾收集器刪除,同時(shí)還為父級(jí)資源提供了一種有效的方式來(lái)避免他們競(jìng)爭(zhēng)同一個(gè)子級(jí)資源(想象兩對(duì)父母都認(rèn)為他們擁有同一個(gè)孩子的場(chǎng)景)。

    Owner References 的另一個(gè)好處是:它是有狀態(tài)的。如果有任何 Controller 重啟了,那么由于資源對(duì)象的拓?fù)潢P(guān)系與 Controller 無(wú)關(guān),該操作不會(huì)影響到系統(tǒng)的穩(wěn)定運(yùn)行。這種對(duì)資源隔離的重視也體現(xiàn)在 Controller 本身的設(shè)計(jì)中:Controller 不能對(duì)自己沒(méi)有明確擁有的資源進(jìn)行操作,它們應(yīng)該選擇對(duì)資源的所有權(quán),互不干涉,互不共享。

    有時(shí)系統(tǒng)中也會(huì)出現(xiàn)孤兒(orphaned)資源,通常由以下兩種途徑產(chǎn)生:

    • 父級(jí)資源被刪除,但子級(jí)資源沒(méi)有被刪除
    • 垃圾收集策略禁止刪除子級(jí)資源

    當(dāng)發(fā)生這種情況時(shí),Controller 將會(huì)確保孤兒資源擁有新的 Owner。多個(gè)父級(jí)資源可以相互競(jìng)爭(zhēng)同一個(gè)孤兒資源,但只有一個(gè)會(huì)成功(其他父級(jí)資源會(huì)收到驗(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)。那么問(wèn)題來(lái)了,Controller 是如何訪問(wèn)和修改這些資源對(duì)象的呢?事實(shí)上 Kubernetes 是通過(guò) Informer 機(jī)制來(lái)解決這個(gè)問(wèn)題的。

    Infomer 是一種模式,它允許 Controller 查找緩存在本地內(nèi)存中的數(shù)據(jù)(這份數(shù)據(jù)由 Informer 自己維護(hù))并列出它們感興趣的資源。

    雖然 Informer 的設(shè)計(jì)很抽象,但它在內(nèi)部實(shí)現(xiàn)了大量的對(duì)細(xì)節(jié)的處理邏輯(例如緩存),緩存很重要,因?yàn)樗坏梢詼p少對(duì) Kubenetes API 的直接調(diào)用,同時(shí)也能減少 Server 和 Controller 的大量重復(fù)性工作。通過(guò)使用 Informer,不同的 Controller 之間以線程安全(Thread safety)的方式進(jìn)行交互,而不必?fù)?dān)心多個(gè)線程訪問(wèn)相同的資源時(shí)會(huì)產(chǎn)生沖突。

    有關(guān) Informer 的更多詳細(xì)解析,請(qǐng)參考這篇文章:Kubernetes: Controllers, Informers, Reflectors and Stores

    Scheduler

    當(dāng)所有的 Controller 正常運(yùn)行后,etcd 中就會(huì)保存一個(gè) Deployment、一個(gè) ReplicaSet 和 三個(gè) Pod 資源記錄,并且可以通過(guò) kube-apiserver 查看。然而,這些 Pod 資源現(xiàn)在還處于 Pending狀態(tài),因?yàn)樗鼈冞€沒(méi)有被調(diào)度到集群中合適的 Node 上運(yùn)行。這個(gè)問(wèn)題最終要靠調(diào)度器(Scheduler)來(lái)解決。

    Scheduler 作為一個(gè)獨(dú)立的組件運(yùn)行在集群控制平面上,工作方式與其他 Controller 相同:監(jiān)聽實(shí)際并將系統(tǒng)狀態(tài)調(diào)整到期望的狀態(tài)。具體來(lái)說(shuō),Scheduler 的作用是將待調(diào)度的 Pod 按照特定的算法和調(diào)度策略綁定(Binding)到集群中某個(gè)合適的 Node 上,并將綁定信息寫入 etcd 中(它會(huì)過(guò)濾其 PodSpec 中 NodeName 字段為空的 Pod),默認(rèn)的調(diào)度算法的工作方式如下:

  • 當(dāng) Scheduler 啟動(dòng)時(shí),會(huì)注冊(cè)一個(gè)默認(rèn)的預(yù)選策略鏈,這些預(yù)選策略會(huì)對(duì)備選節(jié)點(diǎn)進(jìn)行評(píng)估,判斷備選節(jié)點(diǎn)是否滿足備選 Pod 的需求。例如,如果 PodSpec 字段限制了 CPU 和內(nèi)存資源,那么當(dāng)備選節(jié)點(diǎn)的資源容量不滿足備選 Pod 的需求時(shí),備選 Pod 就不會(huì)被調(diào)度到該節(jié)點(diǎn)上(資源容量=備選節(jié)點(diǎn)資源總量-節(jié)點(diǎn)中已存在 Pod 的所有容器的需求資源(CPU 和內(nèi)存)的總和)
  • 一旦篩選出符合要求的候選節(jié)點(diǎn),就會(huì)采用優(yōu)選策略計(jì)算出每個(gè)候選節(jié)點(diǎn)的積分,然后對(duì)這些候選節(jié)點(diǎn)進(jìn)行排序,積分最高者勝出。例如,為了在整個(gè)系統(tǒng)中分?jǐn)偣ぷ髫?fù)載,這些優(yōu)選策略會(huì)從備選節(jié)點(diǎn)列表中選出資源消耗最小的節(jié)點(diǎn)。每個(gè)節(jié)點(diǎn)通過(guò)優(yōu)選策略時(shí)都會(huì)算出一個(gè)得分,計(jì)算各項(xiàng)得分,最終選出分值大的節(jié)點(diǎn)作為優(yōu)選的結(jié)果。
  • 一旦找到了合適的節(jié)點(diǎn),Scheduler 就會(huì)創(chuàng)建一個(gè) Binding 對(duì)象,該對(duì)象的 Name 和 Uid 與 Pod 相匹配,并且其 ObjectReference 字段包含所選節(jié)點(diǎn)的名稱,然后通過(guò) POST 請(qǐng)求發(fā)送給 apiserver。

    當(dāng) kube-apiserver 接收到此 Binding 對(duì)象時(shí),注冊(cè)吧會(huì)將該對(duì)象反序列化并更新 Pod 資源中的以下字段:

    • 將 NodeName 的值設(shè)置為 ObjectReference 中的 NodeName。
    • 添加相關(guān)的注釋。
    • 將 PodScheduled 的 status 值設(shè)置為 True??梢酝ㄟ^(guò) kubectl 來(lái)查看:
      $ kubectl get <PODNAME> -o go-template='{{range .status.conditions}}{{if eq .type "PodScheduled"}}{{.status}}{{end}}{{end}}'
      復(fù)制

    一旦 Scheduler 將 Pod 調(diào)度到某個(gè)節(jié)點(diǎn)上,該節(jié)點(diǎn)的 Kubelet 就會(huì)接管該 Pod 并開始部署。

    注意
    預(yù)選策略和優(yōu)選策略都可以通過(guò) –policy-config-file 參數(shù)來(lái)擴(kuò)展,如果默認(rèn)的調(diào)度器不滿足要求,還可以部署自定義的調(diào)度器。如果 podSpec.schedulerName 的值設(shè)置為其他的調(diào)度器,則 Kubernetes 會(huì)將該 Pod 的調(diào)度轉(zhuǎn)交給那個(gè)調(diào)度器。

    6. Kubelet


    Pod 同步

    現(xiàn)在,所有的 Controller 都完成了工作,我們來(lái)總結(jié)一下:

    • HTTP 請(qǐng)求通過(guò)了認(rèn)證、授權(quán)和準(zhǔn)入控制階段。
    • 一個(gè) Deployment、ReplicaSet 和三個(gè) Pod 資源被持久化到 etcd 存儲(chǔ)中。
    • 然后運(yùn)行了一系列Initializers。
    • 最后每個(gè) Pod 都被調(diào)度到合適的節(jié)點(diǎn)。

    然而到目前為止,所有的狀態(tài)變化僅僅只是針對(duì)保存在 etcd 中的資源記錄,接下來(lái)的步驟涉及到運(yùn)行在工作節(jié)點(diǎn)之間的 Pod 的分布狀況,這是分布式系統(tǒng)(比如 Kubernetes)的關(guān)鍵因素。這些任務(wù)都是由 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 相關(guān)的事件。

    如果換一種思維模式,你可以把 Kubelet 當(dāng)成一種特殊的 Controller,它每隔 20 秒(可以自定義)向 kube-apiserver 通過(guò) NodeName 獲取自身 Node 上所要運(yùn)行的 Pod 清單。一旦獲取到了這個(gè)清單,它就會(huì)通過(guò)與自己的內(nèi)部緩存進(jìn)行比較來(lái)檢測(cè)新增加的 Pod,如果有差異,就開始同步 Pod 列表。我們來(lái)詳細(xì)分析一下同步過(guò)程:

  • 如果 Pod 正在創(chuàng)建, Kubelet 就會(huì)f="https://github.com/kubernetes/kubernetes/blob/fc8bfe2d8929e11a898c4557f9323c482b5e8842/pkg/kubelet/kubelet.go#L1519">記錄一些在 Prometheus 中用于追蹤 Pod 啟動(dòng)延時(shí)的指標(biāo)。
  • 然后生成一個(gè) PodStatus 對(duì)象,它表示 Pod 當(dāng)前階段的狀態(tài)。Pod 的狀態(tài)(Phase) 是 Pod 在其生命周期中的最精簡(jiǎn)的概要,包括 Pending,Running,Succeeded,Failed 和 Unkown 這幾個(gè)值。狀態(tài)的產(chǎn)生過(guò)程非常過(guò)程,所以很有必要深入了解一下背后的原理:
    • 首先串行執(zhí)行一系列 Pod 同步處理器(PodSyncHandlers),每個(gè)處理器檢查檢查 Pod 是否應(yīng)該運(yùn)行在該節(jié)點(diǎn)上。當(dāng)所有的處理器都認(rèn)為該 Pod 不應(yīng)該運(yùn)行在該節(jié)點(diǎn)上,則 Pod 的 Phase 值就會(huì)變成 PodFailed,并且將該 Pod 從該節(jié)點(diǎn)上驅(qū)逐出去。例如當(dāng)你創(chuàng)建一個(gè) Job 時(shí),如果 Pod 失敗重試的時(shí)間超過(guò)了 spec.activeDeadlineSeconds 設(shè)置的值,就會(huì)將 Pod 從該節(jié)點(diǎn)驅(qū)逐出去。
    • 接下來(lái),Pod 的 Phase 值由 init 容器 和應(yīng)用容器的狀態(tài)共同來(lái)決定。因?yàn)槟壳叭萜鬟€沒(méi)有啟動(dòng),容器被視為處于等待階段,如果 Pod 中至少有一個(gè)容器處于等待階段,則其 Phase 值為 Pending。
    • 最后,Pod 的 Condition 字段由 Pod 內(nèi)所有容器的狀態(tài)決定。現(xiàn)在我們的容器還沒(méi)有被容器運(yùn)行時(shí)創(chuàng)建,所以 "https://github.com/kubernetes/kubernetes/blob/fc8bfe2d8929e11a898c4557f9323c482b5e8842/pkg/kubelet/status/generate.go#L70-L81">PodReady 的狀態(tài)被設(shè)置為 False??梢酝ㄟ^(guò) kubectl 查看:
      $ kubectl get <PODNAME> -o go-template='{{range .status.conditions}}{{if eq .type "Ready"}}{{.status}}{{end}}{{end}}'
      復(fù)制
  • 生成 PodStatus 之后(Pod 中的 status 字段),Kubelet 就會(huì)將它發(fā)送到 Pod 的狀態(tài)管理器,該管理器的任務(wù)是通過(guò) apiserver 異步更新 etcd 中的記錄。
  • 接下來(lái)運(yùn)行一系列準(zhǔn)入處理器來(lái)確保該 Pod 是否具有相應(yīng)的權(quán)限(包括強(qiáng)制執(zhí)行 "https://github.com/kubernetes/kubernetes/blob/fc8bfe2d8929e11a898c4557f9323c482b5e8842/pkg/kubelet/kubelet.go#L883-L884">AppArmor 配置文件和 NO_NEW_PRIVS),被準(zhǔn)入控制器拒絕的 Pod 將一直保持 Pending 狀態(tài)。
  • 如果 Kubelet 啟動(dòng)時(shí)指定了 cgroups-per-qos 參數(shù),Kubelet 就會(huì)為該 Pod 創(chuàng)建 cgroup 并進(jìn)行相應(yīng)的資源限制。這是為了更方便地對(duì) Pod 進(jìn)行服務(wù)質(zhì)量(QoS)管理。
  • 然后為 Pod 創(chuàng)建相應(yīng)的目錄,包括 Pod 的目錄(/var/run/kubelet/pods/<podID>),該 Pod 的卷目錄(<podDir>/volumes)和該 Pod 的插件目錄(<podDir>/plugins)。
  • 卷管理器會(huì)href="https://github.com/kubernetes/kubernetes/blob/2723e06a251a4ec3ef241397217e73fa782b0b98/pkg/kubelet/volumemanager/volume_manager.go#L330">掛載 Spec.Volumes 中定義的相關(guān)數(shù)據(jù)卷,然后等待是否掛載成功。根據(jù)掛載卷類型的不同,某些 Pod 可能需要等待更長(zhǎng)的時(shí)間(比如 NFS 卷)。
  • 從 apiserver 中檢索 Spec.ImagePullSecrets 中定義的所有 Secret,然后將其注入到容器中。
  • 最后通過(guò)容器運(yùn)行時(shí)接口(Container Runtime Interface(CRI))開始啟動(dòng)容器(下面會(huì)詳細(xì)描述)。
  • CRI 與 pause 容器

    到了這個(gè)階段,大量的初始化工作都已經(jīng)完成,容器已經(jīng)準(zhǔn)備好開始啟動(dòng)了,而容器是由容器運(yùn)行時(shí)(例如 Docker 和 Rkt)啟動(dòng)的。

    為了更容易擴(kuò)展,Kubelet 從 1.5.0 開始通過(guò)容器運(yùn)行時(shí)接口與容器運(yùn)行時(shí)(Container Runtime)交互。簡(jiǎn)而言之,CRI 提供了 Kubelet 和特定的運(yùn)行時(shí)之間的抽象接口,它們之間通過(guò)協(xié)議緩沖區(qū)(它像一個(gè)更快的 JSON)和 gRPC API(一種非常適合執(zhí)行 Kubernetes 操作的 API)。這是一個(gè)非??岬南敕?#xff0c;通過(guò)使用 Kubelet 和運(yùn)行時(shí)之間定義的契約關(guān)系,容器如何編排的具體實(shí)現(xiàn)細(xì)節(jié)已經(jīng)變得無(wú)關(guān)緊要。由于不需要修改 Kubernetes 的核心代碼,開發(fā)者可以以最小的開銷添加新的運(yùn)行時(shí)。

    不好意思有點(diǎn)跑題了,讓我們繼續(xù)回到容器啟動(dòng)的階段。第一次啟動(dòng) Pod 時(shí),Kubelet 會(huì)通過(guò) Remote Procedure Command(RPC) 協(xié)議調(diào)用 RunPodSandbox。sandbox 用于描述一組容器,例如在 Kubernetes 中它表示的是 Pod。sandbox 是一個(gè)很寬泛的概念,所以對(duì)于其他沒(méi)有使用容器的運(yùn)行時(shí)仍然是有意義的(比如在一個(gè)基于 hypervisor 的運(yùn)行時(shí)中,sandbox 可能指的就是虛擬機(jī))。

    我們的例子中使用的容器運(yùn)行時(shí)是 Docker,創(chuàng)建 sandbox 時(shí)首先創(chuàng)建的是 pause 容器。pause 容器作為同一個(gè) Pod 中所有其他容器的基礎(chǔ)容器,它為 Pod 中的每個(gè)業(yè)務(wù)容器提供了大量的 Pod 級(jí)別資源,這些資源都是 Linux 命名空間(包括網(wǎng)絡(luò)命名空間,IPC 命名空間和 PID 命名空間)。

    pause 容器提供了一種方法來(lái)管理所有這些命名空間并允許業(yè)務(wù)容器共享它們,在同一個(gè)網(wǎng)絡(luò)命名空間中的好處是:同一個(gè) Pod 中的容器可以使用 localhost 來(lái)相互通信。pause 容器的第二個(gè)功能與 PID 命名空間的工作方式相關(guān),在 PID 命名空間中,進(jìn)程之間形成一個(gè)樹狀結(jié)構(gòu),一旦某個(gè)子進(jìn)程由于父進(jìn)程的錯(cuò)誤而變成了“孤兒進(jìn)程”,其便會(huì)被 init 進(jìn)程進(jìn)行收養(yǎng)并最終回收資源。關(guān)于 pause 工作方式的詳細(xì)信息可以參考:The Almighty Pause Container。

    一旦創(chuàng)建好了 pause 容器,下面就會(huì)開始檢查磁盤狀態(tài)然后開始啟動(dòng)業(yè)務(wù)容器。

    CNI 和 Pod 網(wǎng)絡(luò)

    現(xiàn)在我們的 Pod 已經(jīng)有了基本的骨架:一個(gè)共享所有命名空間以允許業(yè)務(wù)容器在同一個(gè) Pod 里進(jìn)行通信的 pause 容器。但現(xiàn)在還有一個(gè)問(wèn)題,那就是容器的網(wǎng)絡(luò)是如何建立的?

    當(dāng) Kubelet 為 Pod 創(chuàng)建網(wǎng)絡(luò)時(shí),它會(huì)將創(chuàng)建網(wǎng)絡(luò)的任務(wù)交給 CNI 插件。CNI 表示容器網(wǎng)絡(luò)接口(Container Network Interface),和容器運(yùn)行時(shí)的運(yùn)行方式類似,它也是一種抽象,允許不同的網(wǎng)絡(luò)提供商為容器提供不同的網(wǎng)絡(luò)實(shí)現(xiàn)。通過(guò)將 json 配置文件(默認(rèn)在 /etc/cni/net.d 路徑下)中的數(shù)據(jù)傳送到相關(guān)的 CNI 二進(jìn)制文件(默認(rèn)在 /opt/cni/bin 路徑下)中,cni 插件可以給 pause 容器配置相關(guān)的網(wǎng)絡(luò),然后 Pod 中其他的容器都使用 pause 容器的網(wǎng)絡(luò)。下面是一個(gè)簡(jiǎn)單的示例配置文件:

    CNI 插件還會(huì)通過(guò) CNI_ARGS 環(huán)境變量為 Pod 指定其他的元數(shù)據(jù),包括 Pod 名稱和命名空間。

    下面的步驟因 CNI 插件而異,我們以 bridge 插件舉例:

    • 該插件首先會(huì)在根網(wǎng)絡(luò)命名空間(也就是宿主機(jī)的網(wǎng)絡(luò)命名空間)中設(shè)置本地 Linux 網(wǎng)橋,以便為該主機(jī)上的所有容器提供網(wǎng)絡(luò)服務(wù)。
    • 然后它會(huì)將一個(gè)網(wǎng)絡(luò)接口(veth 設(shè)備對(duì)的一端)插入到 pause 容器的網(wǎng)絡(luò)命名空間中,并將另一端連接到網(wǎng)橋上。你可以這樣來(lái)理解 veth 設(shè)備對(duì):它就像一根很長(zhǎng)的管道,一端連接到容器,一端連接到根網(wǎng)絡(luò)命名空間中,數(shù)據(jù)包就在管道中進(jìn)行傳播。
    • 接下來(lái) json 文件中指定的 IPAM Plugin 會(huì)為 pause 容器的網(wǎng)絡(luò)接口分配一個(gè) IP 并設(shè)置相應(yīng)的路由,現(xiàn)在 Pod 就有了自己的 IP。
      • IPAM Plugin 的工作方式和 CNI Plugin 類似:通過(guò)二進(jìn)制文件調(diào)用并具有標(biāo)準(zhǔn)化的接口,每一個(gè) IPAM Plugin 都必須要確定容器網(wǎng)絡(luò)接口的 IP、子網(wǎng)以及網(wǎng)關(guān)和路由,并將信息返回給 CNI 插件。最常見(jiàn)的 IPAM Plugin 是 host-local,它從預(yù)定義的一組地址池中為容器分配 IP 地址。它將地址池的信息以及分配信息保存在主機(jī)的文件系統(tǒng)中,從而確保了同一主機(jī)上每個(gè)容器的 IP 地址的唯一性。
    • 最后 Kubelet 會(huì)將集群內(nèi)部的 DNS 服務(wù)器的 Cluster IP 地址傳給 CNI 插件,然后 CNI 插件將它們寫到容器的 /etc/resolv.conf 文件中。

    一旦完成了上面的步驟,CNI 插件就會(huì)將操作的結(jié)果以 json 的格式返回給 Kubelet。

    跨主機(jī)容器網(wǎng)絡(luò)

    到目前為止,我們已經(jīng)描述了容器如何與宿主機(jī)進(jìn)行通信,但跨主機(jī)之間的容器如何通信呢?

    通常情況下使用 overlay 網(wǎng)絡(luò)來(lái)進(jìn)行跨主機(jī)容器通信,這是一種動(dòng)態(tài)同步多個(gè)主機(jī)間路由的方法。 其中最常用的 overlay 網(wǎng)絡(luò)插件是 flannel,flannel 具體的工作方式可以參考 CoreOS 的文檔。

    容器啟動(dòng)

    所有網(wǎng)絡(luò)都配置完成后,接下來(lái)就開始真正啟動(dòng)業(yè)務(wù)容器了!

    一旦 sanbox 完成初始化并處于 active 狀態(tài),Kubelet 就可以開始為其創(chuàng)建容器了。首先啟動(dòng) PodSpec 中定義的 init 容器,然后再啟動(dòng)業(yè)務(wù)容器。具體過(guò)程如下:

  • 首先拉取容器的鏡像。如果是私有倉(cāng)庫(kù)的鏡像,就會(huì)利用 PodSpec 中指定的 Secret 來(lái)拉取該鏡像。
  • 然后通過(guò) CRI 接口創(chuàng)建容器。Kubelet 向 PodSpec 中填充了一個(gè) ContainerConfig 數(shù)據(jù)結(jié)構(gòu)(在其中定義了命令,鏡像,標(biāo)簽,掛載卷,設(shè)備,環(huán)境變量等待),然后通過(guò) protobufs 發(fā)送給 CRI 接口。對(duì)于 Docker 來(lái)說(shuō),它會(huì)將這些信息反序列化并填充到自己的配置信息中,然后再發(fā)送給 Dockerd 守護(hù)進(jìn)程。在這個(gè)過(guò)程中,它會(huì)將一些元數(shù)據(jù)標(biāo)簽(例如容器類型,日志路徑,dandbox ID 等待)添加到容器中。
  • 接下來(lái)會(huì)使用 CPU 管理器來(lái)約束容器,這是 Kubelet 1.8 中新添加的 alpha 特性,它使用 UpdateContainerResources CRI 方法將容器分配給本節(jié)點(diǎn)上的 CPU 資源池。
  • 最后容器開始真正啟動(dòng)。
  • 如果 Pod 中配置了容器生命周期鉤子(Hook),容器啟動(dòng)之后就會(huì)運(yùn)行這些 Hook。Hook 的類型包括兩種:Exec(執(zhí)行一段命令) 和 HTTP(發(fā)送HTTP請(qǐng)求)。如果 PostStart Hook 啟動(dòng)的時(shí)間過(guò)長(zhǎng)、掛起或者失敗,容器將永遠(yuǎn)不會(huì)變成 running 狀態(tài)。
  • 7. 總結(jié)


    如果上面一切順利,現(xiàn)在你的集群上應(yīng)該會(huì)運(yùn)行三個(gè)容器,所有的網(wǎng)絡(luò),數(shù)據(jù)卷和秘鑰都被通過(guò) CRI 接口添加到容器中并配置成功。

    上文所述的創(chuàng)建 Pod 整個(gè)過(guò)程的流程圖如下所示:

    總結(jié)

    以上是生活随笔為你收集整理的创建 tls 客户端 凭据时发生严重错误。内部错误状态为 10013_kubectl 创建 Pod 背后到底发生了什么?...的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。

    如果覺(jué)得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。