〔CKS 筆記整理〕Kubernetes RuntimeClass:從 CRI 演進到選擇各場景 RuntimeClas

更新 發佈閱讀 13 分鐘

隨著 Kubernetes 在企業中的滲透率越來越高,我們對於「容器執行環境 (Container Runtime)」的需求也從單純的「跑起來就好」,演變為對安全性隔離性與特殊硬體支援的極致追求。

在早期的 Kubernetes 中,我們習慣了一種預設模式:所有 Pod 都共享宿主機的核心 (Shared Kernel),並由同一個 Runtime (通常是 Docker 或 runc) 負責執行。這種「一體適用 (One size fits all)」的模式雖然簡單高效,但在面對多租戶隔離或高敏感資料處理時,卻顯得功能不足。

這篇文章將帶您回顧 Kubernetes CRI 的演進歷史,理解為何我們需要 RuntimeClass,並深入解析如何在生產環境中正確配置與管理多租戶與多元業務場景

1. Kubernetes CRI 演進史:從單體到解耦

要真正理解 RuntimeClass 的配置邏輯(特別是為什麼要去改 containerd 的設定檔),我們必須先回顧 Kubernetes 如何與底層容器溝通的歷史。這是一部從「Hardcoded到「Interface」的演進過程。

1.1 早期架構:Docker 獨大 (Pre-v1.5)

在 Kubernetes v1.5 之前的早期版本,並沒有所謂的 CRI (Container Runtime Interface)。當時 Kubelet 的程式碼中直接包含了處理 Docker 的邏輯,Kubelet 直接呼叫 Docker API 來建立容器。

這種設計導致了兩個嚴重問題:

  • 耦合過深:每次 Docker 更新 API,Kubernetes 就必須跟著修改程式碼並重新編譯。
  • 擴充困難:當 CoreOS 推出了 rkt 容器引擎時,K8s 為了支援它,被迫在 Kubelet 裡寫了一堆 if runtime == 'rkt' 的邏輯,導致 Kubelet 變得極度臃腫且難以維護。

1.2 CRI 的誕生與 RuntimeClass 的引入

為了切斷 Kubelet 與特定 Runtime 的強耦合,Kubernetes 在 v1.5 提出了 CRI (Container Runtime Interface)。Kubelet 轉變為一個 gRPC Client,只負責發送標準指令(如 CreateContainer),任何廠商只要實作了 CRI 的 gRPC Server (稱為 CRI Shim),就能被 Kubernetes 使用。

隨著 CRI 生態成熟,除了標準的 runc,市場上出現了強調安全隔離的 gVisor (Google)Kata Containers (基於 VM)。為了讓使用者能在同一個叢集中混合使用這些不同的 Runtime,Kubernetes 在 v1.12 引入了 RuntimeClass API 物件。這讓 Kubelet 透過 CRI 傳遞請求時,能夠帶上一個 runtime_handler 字串,明確告知底層:「這個 Pod 請幫我用 gVisor 啟動」。

1.3 棄用 Dockershim

Kubernetes 在 v1.24 正式移除了 Dockershim(那個為了相容 Docker 而存在的轉接頭)。

現在的架構變得更加簡潔:我們直接使用 containerdCRI-O 作為 CRI Runtime。

  • 路徑變更:Kubelet -> containerd (內建 CRI plugin) -> runc/gVisor。
  • 維運影響:因為少了一層 Docker Daemon,我們在除錯時不再使用 docker ps,而是使用 crictl ps 指令;Log 路徑與 Socket 位置也都回歸 CRI 標準。

2. 核心概念:為什麼需要 RuntimeClass?

理解了歷史背景後,我們回到 RuntimeClass 本身。它是 Kubernetes 用來選擇容器執行環境的標準 API 物件。

2.1 架構定位

  • 層級:RuntimeClass 屬於 Cluster-Level 資源,不分 Namespace。
  • 作用範圍:設定於 PodSpec 的最頂層 (spec.runtimeClassName)。

其核心架構考量在於:一個 Pod 內的所有 Container (包含 Sidecar) 必須共享同一個 Network、IPC 和 PID Namespace (Sandbox)。因此,我們無法讓同一個 Pod 裡的 Container A 跑在 gVisor,而 Container B 跑在 runc,它們必須共用同一個隔離邊界。

2.2 依據業務場景選擇 Runtime

在現代叢集中,我們通常會依據業務需求來選擇不同的 Runtime:

  1. 一般微服務 (Native/runc): 標準容器。行程直接跑在 Host Kernel 上,利用 cgroups/namespaces 隔離。
  • 優點:效能最好,啟動速度快 (毫秒級)。
  • 適用場景:90% 的一般應用、內部微服務、高吞吐量 API。
  1. 多租戶與敏感數據 (Sandboxed/gVisor): Google 開發的 gVisor 在 User Space 模擬 Kernel Syscall,攔截並過濾系統呼叫。
  • 優點:隔離性強,攻擊面小,防止容器逃逸。
  • 適用場景:多租戶環境 (SaaS)、執行未受信任程式碼 (如 Serverless Function, User Upload Scripts)。
  1. 強隔離與硬體虛擬化 (MicroVM/Kata Containers): 每個 Pod 跑在一個極輕量的虛擬機 (QEMU/Firecracker) 裡,擁有獨立的 Kernel。
  • 優點:隔離性最強 (硬體級虛擬化),完全阻隔租戶間的影響。
  • 適用場景:金融級安全需求、完全隔離的租戶環境、需要特定 Kernel 版本的應用。
  1. AI 與高效能運算 (Hardware Accelerated/NVIDIA): 專為 GPU 存取優化的 Runtime (如 nvidia-container-runtime)。
  • 優點:優化 GPU Pass-through 與 CUDA 驅動的掛載流程,確保容器能直接且高效地存取底層硬體加速器。
  • 適用場景:AI 模型訓練/推論、HPC 科學運算。

3. 實戰配置流程 (Configuration Workflow)

接下來進到實作的部分,要讓 RuntimeClass 運作,僅僅在 Pod YAML 寫一行是不夠的。這涉及到 CRI (Containerd)、K8s API 與 Pod 三層的配合。

步驟一:底層 CRI 配置 (Node 層級)

這是最容易被忽略的一步。您必須先在 Node 的 containerd 設定檔中註冊 Runtime Handler。

強烈建議**不要「手寫」整個檔案,**漏掉關鍵設定(如 SystemdCgroup = true),容易導致 Kubelet 無法啟動。

標準做法: 先執行指令生成完整的預設設定檔,再針對 runtimes 區塊進行修改。

containerd config default > /etc/containerd/config.toml

vim /etc/containerd/config.toml

配置範例 (TOML 片段)

[plugins."io.containerd.grpc.v1.cri".containerd.runtimes]
# 定義一個名為 "runsc" (gVisor) 的 handler
# 這裡的 key "runsc" 將對應到 RuntimeClass 物件中的 handler 欄位
[plugins."io.containerd.grpc.v1.cri".containerd.runtimes.runsc]
runtime_type = "io.containerd.runsc.v1"

設定完成後,重啟 containerd 服務。

sudo systemctl restart containerd

步驟二:定義 RuntimeClass 物件 (Cluster 層級)

告訴 K8s 有這個 Runtime 存在,並映射到 CRI 的 Handler。

基本配置 (gVisor 範例)

# 幫已完成步驟一的 Node 打上標籤
kubectl label node worker-node-01 sandbox.gvisor.io=true

# check
kubectl get nodes --show-labels
apiVersion: node.k8s.io/v1
kind: RuntimeClass
metadata:
name: gvisor # 這是開發者在 Pod YAML 裡引用的名稱
handler: runsc # 對應到底層 CRI config 裡的 handler 名稱

# 關鍵設定:自動排程
scheduling:
nodeSelector:
sandbox.gvisor.io: "true"
# 強制 Pod 只能排程到有標籤 (sandbox.gvisor.io: "true") 的節點

# Pod Overhead (資源開銷設定)
# 對於 Kata Containers 或 gVisor 這類 Runtime,啟動每個 Pod 都會額外消耗記憶體(例如 QEMU VM 本身可能需要 100MB+)。如果 K8s Scheduler 不知道這筆開銷,可能會將過多的 Pod 排程到節點上,導致節點 OOM
# 這會讓 K8s Scheduler 在計算資源時,自動扣除這部分的量,確保節點穩定。
overhead:
podFixed:
memory: "120Mi"
cpu: "250m"

步驟三:Pod 使用 (Application 層級)

開發者只需指定 Class Name,無須關心底層節點配置。

apiVersion: v1
kind: Pod
metadata:
name: sensitive-app
spec:
runtimeClassName: gvisor # <--- 關鍵設定,參照步驟二的 metadata.name
containers:
- name: app
image: my-app:latest

4. 常見 QA :

  1. Pod 處於 Failed 狀態
  • 原因:Pod 指定了 runtimeClassName: gvisor,但該節點上的 CRI (containerd) 設定檔中沒有配置對應的 runsc handler。
  • 排查方式:檢查 /etc/containerd/config.toml 並確認 handler 名稱一致。
  1. Pod 卡在 Pending
  • 原因:RuntimeClass 物件中設定了 scheduling.nodeSelector,但叢集中沒有任何節點擁有該標籤。
  • 排查方式:使用 kubectl label nodes <node-name> sandbox.gvisor.io=true 為安裝了 gVisor 的節點打上標籤。

5. 總結

RuntimeClass 是 Kubernetes 應對複雜運算需求與多租戶場景的重要拼圖。透過理解 CRI 的演進與 RuntimeClass 的配置邏輯,維運團隊可以提供一個既安全又靈活的基礎設施平台,讓一般應用跑在高效的 runc 上,同時讓敏感應用或 AI 工作負載無縫地運行在 gVisor、Kata 或 NVIDIA 的專屬環境中。

留言
avatar-img
留言分享你的想法!
avatar-img
Marcos的方格子
24會員
51內容數
歡迎來到「Marcos的方格子」!目前在「Marcos談科技」撰寫在職涯上學習到的知識,在「Marcos談書」分享我在日常的閱讀和心得,歡迎您的到來!!
Marcos的方格子的其他內容
2025/12/11
這篇文章將帶您從最上層的「多租戶治理策略」開始,一路向下挖掘,直到最底層的 Container Runtime 與 Linux Kernel Cgroups 是如何協同運作,來確保這些限制生效的。
Thumbnail
2025/12/11
這篇文章將帶您從最上層的「多租戶治理策略」開始,一路向下挖掘,直到最底層的 Container Runtime 與 Linux Kernel Cgroups 是如何協同運作,來確保這些限制生效的。
Thumbnail
2025/12/10
這篇文章將帶您深入探討 Kubernetes 的流量入口架構,剖析 Ingress 的設計哲學與限制,並解析下一代標準 Gateway API 如何透過「角色導向」的設計解決當前的 Ingress 架構瓶頸。
Thumbnail
2025/12/10
這篇文章將帶您深入探討 Kubernetes 的流量入口架構,剖析 Ingress 的設計哲學與限制,並解析下一代標準 Gateway API 如何透過「角色導向」的設計解決當前的 Ingress 架構瓶頸。
Thumbnail
2025/12/08
探索 API Server 稽核日誌 (Audit Logging) 如何提供安全 (Security)、合規 (Compliance) 和事件響應 (Incident Response) 所需的關鍵鑑識證據。
Thumbnail
2025/12/08
探索 API Server 稽核日誌 (Audit Logging) 如何提供安全 (Security)、合規 (Compliance) 和事件響應 (Incident Response) 所需的關鍵鑑識證據。
Thumbnail
看更多
你可能也想看
Thumbnail
十一月底正好要去斯里蘭卡,之前趁雙十一時就把旅行必備東西陸續買齊。 現在我依然在斯里蘭卡的旅行路上,邊當旅人邊推薦旅行好物給你們!(這篇記得收藏起來喔!)
Thumbnail
十一月底正好要去斯里蘭卡,之前趁雙十一時就把旅行必備東西陸續買齊。 現在我依然在斯里蘭卡的旅行路上,邊當旅人邊推薦旅行好物給你們!(這篇記得收藏起來喔!)
Thumbnail
本篇文章將教你如何在Kubernetes cluster內部署一個MongoDB,包括取得Manifests、建立Volume、部署實務、基本操作和結論。透過操作演示,讓你瞭解在實務上如何成功建立MongoDB,並進行基本操作。
Thumbnail
本篇文章將教你如何在Kubernetes cluster內部署一個MongoDB,包括取得Manifests、建立Volume、部署實務、基本操作和結論。透過操作演示,讓你瞭解在實務上如何成功建立MongoDB,並進行基本操作。
Thumbnail
本文闡述了Kubernetes內部網路通訊的基本概念,從容器到服務的溝通流程,並討論了Kubernetes使用的各種技術。重要的是,管理Kubernetes叢集時理解這些基本概念是極其重要的。
Thumbnail
本文闡述了Kubernetes內部網路通訊的基本概念,從容器到服務的溝通流程,並討論了Kubernetes使用的各種技術。重要的是,管理Kubernetes叢集時理解這些基本概念是極其重要的。
Thumbnail
本篇說明如何利用Kubernetes特色,將PostgreSQL DB以HA的架構來提供服務,並說明相關的實作流程與說明。
Thumbnail
本篇說明如何利用Kubernetes特色,將PostgreSQL DB以HA的架構來提供服務,並說明相關的實作流程與說明。
Thumbnail
本文將演示在安裝完 Kubernetes Cluster 後的基本元件安裝,包括 Calico/Calicoctl、Metric Server 和 Dashboard UI 的安裝方法以及相關問題與解決方式。
Thumbnail
本文將演示在安裝完 Kubernetes Cluster 後的基本元件安裝,包括 Calico/Calicoctl、Metric Server 和 Dashboard UI 的安裝方法以及相關問題與解決方式。
Thumbnail
這篇文章教你如何搭建Kubernetes Cluster,包括節點安裝前設定、軟體套件安裝、Control-Plane部署和加入運算節點等步驟。在建置之後,作者會分享一些基礎服務的安裝。希望這篇文章對你有所幫助。
Thumbnail
這篇文章教你如何搭建Kubernetes Cluster,包括節點安裝前設定、軟體套件安裝、Control-Plane部署和加入運算節點等步驟。在建置之後,作者會分享一些基礎服務的安裝。希望這篇文章對你有所幫助。
Thumbnail
本文章將說明如果您想要從頭建置一組具有Loadbalancer HA架構的Kubernetes Cluster時,你可能會需要做的事前準備工作。
Thumbnail
本文章將說明如果您想要從頭建置一組具有Loadbalancer HA架構的Kubernetes Cluster時,你可能會需要做的事前準備工作。
Thumbnail
本文將說明在安裝完Kubernetes Cluster之後,接下來必須要進行的CNI Plugin安裝建置方式,同時也透過這篇文章進行基本的CNI說明與比較。 1. Container Network Interface (CNI)
Thumbnail
本文將說明在安裝完Kubernetes Cluster之後,接下來必須要進行的CNI Plugin安裝建置方式,同時也透過這篇文章進行基本的CNI說明與比較。 1. Container Network Interface (CNI)
追蹤感興趣的內容從 Google News 追蹤更多 vocus 的最新精選內容追蹤 Google News