Kubernetes 三大介面:CNI、CRI、CSI
說明 Kubernetes 將容器執行、網路與儲存拆成 CRI、CNI、CSI 三個介面的原因,以及 Pod 啟動時三者的串接流程。
在 Kubernetes 剛誕生的時候,它的核心目標就已經很明確:負責調度與管理,但不要自己下去做「跑容器、拉網路線、搬磁碟」這些細部設定。不過問題馬上出現了:
- 容器 Runtime:一開始大家都用 Docker,但後來出現了 containerd、CRI-O,甚至各家雲商還想自己搞一套。如果 K8s 每多一個 Runtime 就得改一次程式,維護團隊鐵定會崩潰。
- 網路:有的環境希望 Pod 直接用 VPC IP,有的環境只能用 Overlay,有人追求簡單,有人追求高效能安全。K8s 不可能只支援一種方案。
- 存儲:磁碟和檔案系統更是百花齊放,EBS、EFS、Ceph、NFS,各家都有自己的 API。要 K8s 全都寫死在核心,根本不可能。
於是,Kubernetes 團隊做了一個聰明的決定,那就是把這三塊最容易變動、最依賴環境的能力切分出來,設計成三個「標準插槽」:
- CRI (Container Runtime Interface):負責跑容器
- CNI (Container Network Interface):負責拉網路
- CSI (Container Storage Interface):負責搬磁碟
Kubernetes 自己只專注在「我要一個 Pod」,至於「Pod 裡的容器怎麼跑、怎麼上網、怎麼掛磁碟」,全都交給這三大介面後面的 plugin 來解決。
這也是為什麼今天我們可以在 AWS 上跑 VPC CNI + EBS CSI,換到自建環境又能用 Cilium + Ceph CSI,而 Kubernetes 本身完全不用修改 —— 真正做到可插拔的雲原生平台。
接下來,我們就來看看這三個 plugins 各自負責的任務細節。
CRI:容器要怎麼被跑起來?
在最初期的 Kubernetes,大家都是用 Docker 來跑容器。但隨著時間發展,Kubernetes 不想被某一個特定 Runtime 綁死 —— 於是設計了 CRI (Container Runtime Interface),讓不同的 Runtime 都能插進來,實現一樣的功能。
CRI 解決的問題是:
- Kubernetes API Server 只會下指令:「我要一個 Pod,裡面有三個容器。」
- 真正要把容器建起來,交給 Runtime(例如 containerd)。
- CRI 定義了一個通用 API,讓 Kubelet 可以跟不同 Runtime 溝通,而不用知道每個 Runtime 的細節。
常見的 Runtime:
- containerd:目前最主流,EKS、GKE 都採用它。
- CRI-O:專門為 Kubernetes 打造,OpenShift 預設使用。
- Docker (dockershim):已經被移除,因為它不是完全符合 CRI 的介面,需要額外 shim。
👉 實際案例:
當我們在 EKS 下 kubectl apply -f nginx.yaml,其實流程是這樣:
- Kubelet 收到「跑一個 nginx Pod」的請求。
- 透過 CRI 呼叫 containerd。
- containerd 去拉 image、建 namespace、建立容器。
- Pod 最後出現在 Node 上。
所以 CRI 是 Kubernetes 跟「容器工人」之間的溝通語言。
CNI:Pod 要怎麼彼此連線?
有了容器,下一個問題是:這些容器要怎麼彼此連線?
光是建立 Pod 還不夠,如果 Pod 之間沒有網路,就像蓋了一堆房子卻沒拉網路線。
這就是 CNI (Container Network Interface) 的角色:它負責在 Pod 啟動的時候,自動幫 Pod 插上網卡、分配 IP、設定路由。
CNI 解決的問題是:
- Pod 要有一個獨立的 IP,才能在叢集內被找到。
- Pod 之間要能互通,還要能跟外部世界交談。
- 網路策略(誰能連誰,誰不能)也需要統一入口控制。
常見的 CNI Plugin:
- Amazon VPC CNI:EKS 預設,Pod 直接拿 VPC IP,看起來就像 EC2。
- Calico:老牌方案,以路由和 iptables 為主,支援 NetworkPolicy。
- Flannel:最簡單的 overlay 網路,用 VXLAN 封包包起來。
- Cilium:新一代解法,基於 eBPF,提供高效能封包處理與進階安全策略。
👉 實際案例:
假設你有兩個 Pod:
- Pod A:
10.0.1.5 - Pod B:
10.0.2.8
當 Pod A 要連 Pod B:
- CNI 在建立 Pod 時,給它一個虛擬網卡與 IP。
- 在 Node 的路由表 / iptables / eBPF rule 中,寫下對應規則。
- 封包就能找到正確的目的地。
如果沒有 CNI,Pod 雖然啟動了,但你會發現它沒有 IP,什麼網路都不能用。
CSI:Pod 的資料要存去哪裡?
最後一個問題是:資料要怎麼存?
容器是短命的,Pod 被刪掉後,裡面的檔案也會消失。但我們不可能讓資料庫每次重啟就歸零,所以需要一個方式讓 Pod 可以掛載持久化儲存。
這就是 CSI (Container Storage Interface)。
CSI 解決的問題是:
- Kubernetes 不自己管理磁碟,而是交給外部 Storage Provider。
- CSI 提供一個標準 API,讓不同儲存系統都能被掛進 Pod。
常見的 CSI Plugin:
- Amazon EBS CSI Driver:適合單一 Node 使用的磁碟(像 MySQL)。
- Amazon EFS CSI Driver:提供共享檔案系統,適合多個 Pod 同時讀寫(像 WordPress)。
- Ceph/Rook:開源分散式儲存,許多私有雲環境會採用。
👉 實際案例:建立一個 PVC 時,會有以下的設定:
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: db-data
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 20Gi
背後 CSI Driver 會幫你在 AWS 建一顆 EBS Volume,並自動掛到運行這個 Pod 的 Node 上,Pod 就能把資料寫進去,即使重啟也不會消失。
總結:Pod 啟動完整流程(CRI × CNI × CSI 串起來)
kubectl apply
│
▼
Kubelet
│
▼
CRI → 建立容器 (containerd / CRI-O)
│
▼
CNI → 分配 IP、插上網卡、設定路由
│
▼
CSI → (若有 PVC) 掛載持久化存儲
│
▼
Pod → 容器可運行、有網路、有存儲
這張圖可以幫助理解:
- CRI 先讓容器活過來。
- CNI 幫容器插上網路線,拿到 IP。
- CSI 幫容器接上磁碟,能持久化資料。
三者合力,才讓 Pod 真正「能工作」。