寫這篇想要分享的重點:
搞懂 Kubernetes 底層如何透過 CRI(Container Runtime Interface)操作容器引擎,並釐清為什麼 Docker 會從 K8s 的核心運作流程中「被排除掉」,以及工程師在前線要如何用 CLI 工具進行除錯。這篇想要講什麼:
- Container Runtime 演進史:從 Docker 到 CRI、CRI-O 與 containerd。
- 為什麼 K8s 要移除 Dockershim?這對維運會有影響嗎?
- 維運CLI 工具比較:
crictlvs.nerdctlvs.ctr。為何要寫這篇:
前兩天我們了解了 Control Plane 與 Worker Node 的組件分工。今天我們要深入 Worker Node 的底層,去看看kubelet到底是如何指揮 Container Runtime 把容器跑起來的。
Container Runtime: 容器執行環境 / 容器運行時
CRI (Container Runtime Interface): 容器執行環境介面 / 容器運行時介面
OCI (Open Container Initiative): 開放容器倡議 / 開放容器標準
Deprecate / Remove: 棄用 / 移除
Socket: UNIX Socket 檔 / 虛擬通訊插座
在 K8s 剛誕生時,Docker 稱霸了整個容器界。但隨著 K8s 發展,社群希望能夠支援更多不同的容器技術(例如 CoreOS 的 rkt),因而產出了標準化的介面(interface)。
+-----------------------------------------------------------------------+
| kubelet |
+-----------------------------------------------------------------------+
|
| (CRI 介面規範)
v
+-----------------------------------------------------------------------+
| containerd |
| (負責管理 Image 拉取、網路配置、Lifecycle) |
+-----------------------------------------------------------------------+
|
| (OCI 介面規範)
v
+-----------------------------------------------------------------------+
| runc |
| (真正向 Linux Kernel 申請 cgroups / namespace 生成 Container) |
+-----------------------------------------------------------------------+
Low-Level Runtime(如 runc):
cgroups 與 namespaces,真正建立並隔離出容器環境。它只關心怎麼把容器 Run 起來,完全不管 Image 是從哪裡下載的。High-Level Runtime(如 containerd、CRI-O):
runc 來真正啟動容器。為什麼 Kubernetes 不把容器引擎、網路外掛跟儲存系統直接寫在裡面?
因為 K8s 採用了 「聲明式介面(Interface)」 設計。這就像在主機板上插顯示卡、記憶體一樣,只要硬體廠商遵守 PCIe 介面規範,不管你是 NVIDIA 還是 AMD 都可以直接插上去使用。
在 K8s 裡面,最關鍵的介面規範有以下四大核心:
+-----------------------------------------------------------------------------------+
| Kubernetes |
+-----------------------------------------------------------------------------------+
| (標準化規範) | (標準化規範) | (標準化規範)
v v v
+--------------+ +--------------+ +--------------+
| CRI | | CNI | | CSI |
| (容器執行介面) | | (網路外掛介面) | | (儲存設備介面) |
+--------------+ +--------------+ +--------------+
| | |
v (實例) v (實例) v (實例)
containerd / CRI-O Calico / Flannel AWS EBS / GCP PD
|
| (呼叫 OCI 標準)
v
+--------------+
| OCI | ---> 向 Linux Kernel 申請資源 (runc / crun)
| (底層容器標準) |
+--------------+
kubelet 與底層容器執行引擎(Container Runtime)溝通的標準語言。Calico(支援強大 NetworkPolicy)、Flannel(極簡輕量)、Cilium(基於 eBPF 高性能網路)。runc)」。整理一下:
| 規範名稱 | 全名 | 負責管什麼? | 常見實作 |
|---|---|---|---|
| CRI | Container Runtime Interface | 生命週期:Pod 內容器的建立、啟動、停止 | containerd、CRI-O |
| CNI | Container Network Interface | 網路連線:Pod IP 分配、跨節點網路打通 | Calico、Flannel、Cilium |
| CSI | Container Storage Interface | 資料持久化:掛載硬碟、雲端儲存空間 | AWS EBS Driver、Longhorn |
| OCI | Open Container Initiative | 底層規格:標準容器格式與 Linux 内核隔離 | runc、crun |
雖然看起來很抽象難懂, 不過理解越多K8S之後就會越來越理解他們為何存在, 因為東西太多了就是要先設好規範才方便抽換/開發新組件
許多人在 K8s 1.24 推出時被「K8s 拔掉 Docker」的標題嚇到,以為以後不能用 Docker 包 Image 了。這完全是個誤解!
dockershim 的轉接器(Adapter),幫 kubelet 把 CRI 指令翻譯成 Docker API,Docker 再去呼叫 containerd。智慧工廠比喻:
廠長(kubelet)只會官方語言(CRI)。但 Docker 這台舊機台語言不通,工廠只好硬塞一個翻譯機(dockershim)才能操作。最後 K8s 團隊發現:「欸?Docker 內部明明也是用containerd在做事,那我幹嘛不直接跳過 Docker,讓kubelet直接與containerd溝通就好了?」
docker build 包 Image,只要符合 OCI 規範,containerd 都能跑。docker ps 去看 K8s 的容器了,必須改用針對 CRI 的工具。crictl vs. nerdctl vs. ctr當 SSH 進到 Worker Node 進行現場除錯時,你會發現下 docker ps 什麼都看不到,這時你有以下三款工具可以使用:
crictl(K8s 官方維運除錯)crictl pods),而不是只看到一堆亂亂的 Container。crictl ps
crictl pods
crictl logs <container-id>
crictl exec -it <container-id> sh
nerdctl(Docker 指令無痛轉移工具)docker CLI 一模一樣的使用體驗。nerdctl run、nerdctl build,連語法參數都跟 Docker 幾乎一樣。如果你習慣 Docker 的操作,在 Worker Node 上裝 nerdctl 會非常順手。--namespace k8s.io 參數(例如 nerdctl -n k8s.io ps)。ctr(containerd 原生工具)containerd 附帶的原始 CLI。containerd 外掛或極度底層除錯時才會用到。整理一下:
| 工具名稱 | 主要用途 | 是否懂 K8s Pod 概念? | 適合情境 |
|---|---|---|---|
crictl |
K8s 官方 CRI 除錯工具 | YES(支援 crictl pods) |
日常 K8s Node 故障除錯用 |
nerdctl |
Docker 風格的替代工具 | NO(需指定 -n k8s.io 命名空間) |
習慣 Docker 指令、需在節點上 Build Image |
ctr |
containerd 原生開發工具 |
NO | 極底層 Runtime 除錯(平時少用) |
本篇總結:
Kubernetes 的強大源於 高度解耦與標準化的 Interface 設計(CRI, OCI, etc...)。
移除 Dockershim 並非棄用 Docker 產出的 Image,而是讓 kubelet 能夠透過 CRI 介面直接與 containerd溝通,省去翻譯轉接工作;維運時,運用 crictl 與 nerdctl 迅速進行除錯。
下一篇預告:
釐清了 Container Runtime 的底層機制與介面規範後,明天 Day 04 我們將回歸工作負載核心,探討 【核心】最小部署單位:Pod 的概念與 YAML 撰寫基礎!我們將說明Pod 的共享資源與生命週期狀態(Pending/Running/CrashLoopBackOff),並掌握K8S四個基本 YAML 結構與快速生成YAML技巧!
containerd 負責拉取 Image 與網路配置,再指派 runc 向 Linux Kernel 申請資源生成容器。docker build 打包與運行,應用程式開發者完全不受影響。crictl(直觀看 Pod / 專為 CRI 設計);習慣 Docker 指令可用 nerdctl -n k8s.io;ctr 則留給極底層除錯。