Kubernetes API 服務及跟各組件的交互關係

前陣子客戶那邊有個問題需要先了解 Kubernetes 從部署 Deployment 到實際上跟這些主要組件溝通的流程,不然很難進行 Troubleshooting (a.k.a. 觀落陰)。幸好本次研究有 3 位大大的一起參與 Max、地瓜、喜德,最終找到了一個跟現象毫無關係的根本問題 =_=,但因為過程算蠻難得的,特別記錄下來給大家參考參考

照例講個雜事,Red Hat OpenShift 4.6 (等同 Kubernetes 1.19) 要出啦!這是一個可以一路用到 2022 年 3 月 的版本,各位 Infra 不用在為了更新和升級落淚了 Q_Q,有興趣者可以參考 What’s New in OpenShift Container Platform 4.6

按照我個人對於 Kubernetes 內部溝通理解,約略分為下列 11 個溝通步驟,當然我知道再更細分的話,還可以分更多步驟出來,但我這圖僅針對 Container 最終能運行為主截止

如果有時候遇到一些不可知的神祕現象,可以思考一下整個系統,是位在哪一層卡住

Kubernetes Deployment 分解動作講解

  1. 使用者自行撰寫好或透過別人弄好的 Kubernetes YAML Generator 產生 Kubernetes Deployment YAML 檔案,且通过 Kubernetes API 访问集群一文所列的方式:kubectl / RESTful API / client-go / kubernetes-client/python 等方式發送到 Kubernetes API Server 宣告進行部署應用程式。這個階段 Kubernetes API Server 會進行依序是 Authentication、Authorization、Admission Control 等驗證和檢查語法,詳細過程可參考 Chapter 17. Admission Control and Authorization。如果都沒有問題的話,將會儲存 Deployment Object 至 etcd cluster
  2. 目前為止,Pod 還沒被啟動起來,但 Controller Manager 已經收到來自 Kubernetes API Server 的要求
  3. Controller Manager 會去檢查是否有數量正確的副本 (Replicas) 且對應正確需求的 Pod 已經在運行
  4. 如果沒有正確數量的副本正在運行,則會發送基於 Pod Spec 的要求到 Kubernetes API Server 請求建立對應數量的 Pod 起來
  5. Kubernetes API Server 寫入和讀取之前,會先經過兩關 Admission 和 Validation,前者 Admission 透過確認叢集全域設定來檢查是否可以新增或更新 Object,並可能依據叢集設定提供預設值,後者 Validation 則是在新增或更新過程中,來檢查傳入的 Object 是否合法,及是否僅包含有效參數,詳細可參考 Kubernetes Deep Dive: API Server – Part 2
  6. Kubernetes API Server 持續會對 etcd 所存放的 Kuberentes Object 進行更新或移除,詳細可參考 Kubernetes Deep Dive: API Server – Part 2
  7. 雖然 etcd 內部已經存有 Pod 的資訊,但不過僅僅是一條紀錄而已,現實狀況是還沒有分配到 Worker Node 上。這時候 Kubernetes API Server 會通知 Kubernetes Scheduler:目前有新增 Pod 的需求,並且還沒有 Worker Node 可以運行它
  8. Kubernetes Scheduler 會通過程式檢查現行資源使用狀況以及現有的 Pod 的分配,然後去計算出最適合擺放每ㄧ個新 Pod 的 Worker Node 位置。當決定出來之後,Kubernetes Scheduler 將會設定每一個 Pod 裡面的 spec.nodeName,將其轉換成 Worker Node 名稱 ,並將最終決定通知至 Kubernetes API Server 進行更新請求
  9. 直至本步驟為止,Pod 已經知道它應該要在哪一個 Worker Node 上面運行,但將通知位於需被分配的 Worker Node 上的 kubelet,並指示這些來自於 Kubernetes API Server 的新增 Pod 請求都應該要被 Kubelet 被遵照執行。然而,該 Worker Node 節點上的 Kubelet 將檢查自身是否已經將要運行的 Pod 運行起來。
  10. 一但 Kubelet 確定 Pod 應該要運行於該節點上之後,就會呼叫 High-Level Contianer Runtime (如: Docker、Containerd、CRI-O 等) 已啟動容器 (Container)。當一切都準備好之後,kubelet 會將其狀態報告回傳至 Kubernetes API Server
  11. 接下來就是依據 Pod Lifecycle - Kubernetes 所述,建立 infra (pause) container 後,開始將Pod Spec 所要求的 Container 一個一個拉取並且啟動,並且與 Kube-proxy 協作將服務轉發出去

Q&A

High-Level Container Runtime 跟 Low-Level Container Runtime 差異是啥?

Low-Level Container Runtime 主要專職運行容器或其特定環境,實際專案如,常見於各大專案的 runc 或者是 crun、runv 等,詳細可參考 opencontainers / runtime-spec - implementations.md

High-Level Container Runtime 主要負責管理各式容器映像檔以及負責跟 Low-Level Container Runtime 管理跟溝通,本身不處理實際運行容器的事情,一般都會有一個 Daemon 和 API 可以去監控底層狀態,譬如說 OpenShift 4 之後所使用的 CRI-O 或者是更多人比較知道 Dockerd 跟被拆分出來的 containerd 都同屬這類

文後感謝

感謝地瓜、Davy 兩位大大的半夜協助和毫不知情照片被我拿來用的 hwchiu

References

Comments