Kubernetes 為什麼移除 Docker?
2020 年 12 月 2 日,Kubernetes 官方部落格刊登了一篇由十位社群維護者聯名發表的文章:Don’t Panic: Kubernetes and Docker。
同日發布的 Dockershim Deprecation FAQ 正式預告:kubelet 內建的 Docker 轉譯層 dockershim 即將退場。
這場宣告引發了廣泛恐慌,社群一度以為手上的 Docker 映像檔將無法在叢集運作。
但 Docker 建置的產物本質上符合開放容器倡議(OCI)規格,任何相容 CRI 的 runtime 都能直接拉取執行,既有的 Dockerfile 與映像檔完全不必重寫。
NOTE
延伸閱讀:OCI 入門:容器世界的共同語言
2022 年 5 月 3 日,隨著 Kubernetes 1.24 正式發布,dockershim 徹底從程式碼庫中移除,生態平穩過渡。
四年後回頭看,這起事件確立了 containerd 成為多數節點的預設 runtime,也是 Kubernetes 將 Docker 專屬程式碼移出核心、回歸開放標準介面的關鍵案例。
CRI 與 dockershim 為何存在
早期特殊整合的沉重代價
Kubernetes 並非誕生於容器標準完備的時代。2014 年 6 月 6 日,專案核心程式碼首次推上 GitHub(見官方十週年回顧)。
當時 Docker 佔據市場壟斷地位,Kubernetes 選擇直接支援 Docker 是最務實的捷徑。
NOTE
這種硬編碼(hardcoded)的整合方式迅速演變成技術負債。Google 工程師 Yu-Ju Hong 在 2016 年 12 月 19 日的 CRI 官方公告中承認了當時的困境:
「Docker 與 rkt 都透過內部且不穩定的介面,直接且深入焊死在 kubelet 原始程式碼裡。這種深度整合需要對 kubelet 內部有深入理解,為社群帶來可觀維護負擔,也讓其他 runtime 幾乎無法加入支援。」
CRI 規格、暫時性墊片與冗長呼叫鏈
為了解耦,Kubernetes 隨 1.5 版本推出了容器 runtime 介面(Container Runtime Interface,CRI)。
這是一組以 Protobuf 與 gRPC 定義的開放標準,讓 kubelet 無需重新編譯即可驅動任何實作 CRI 的 runtime。
問題在於 Docker 本身並未原生實作 CRI。官方在 2020 年問答集中直言:
「Docker 本身至今未實作 CRI,問題由此而生。」
為了相容既有使用者,社群只得在 kubelet 內建一個名為 dockershim 的暫時性薄轉譯層,代替 Docker 接收並轉發 CRI 呼叫。
舊呼叫鏈(dockershim):
kubelet --> dockershim(內建墊片) --> Docker Engine
--> containerd --> runc --> 容器
新呼叫鏈(原生 CRI):
kubelet --(CRI gRPC)--> containerd / CRI-O
--> runc --> 容器
官方文章 Don’t Panic 解釋,Docker 好用在於加了許多便利使用者操作的介面,但 Kubernetes 作為系統並不需要這些人性化功能,叢集只需要純粹的容器生命週期管理。
dockershim 的存在只是為了穿過繁複的介面層去摸到底層真正的 containerd。
多過一層轉譯,kubelet 就得多處理一組 API 錯誤碼與版本對齊。這條呼叫鏈只要存在一天,kubelet 核心程式碼就無法擺脫對 Docker 特定行為的相容邏輯。
標準化的兩條伏線:rkt 的落幕與 containerd 的獨立
rkt 與標準化競賽的代價
移除 dockershim 是 Kubernetes 逐步清理外部 runtime 特例的最後一步。
要理解這條路徑,必須回顧曾被視為 Docker 最強對手的 CoreOS rkt。
rkt 主打 Pod 原生架構並積極對齊早期 OCI 規格,2017 年初與 containerd 同批進入 CNCF 孵化(見 OCI 官方部落格)。社群甚至為其打造了獨立轉譯器 rktlet。
然而隨 2018 年 Red Hat 收購 CoreOS,開發資源全面轉向 CRI-O 與 Podman,rkt 的社群活躍度迅速下滑,其 GitHub 儲存庫最終於 2020 年 2 月 24 日正式封存。
rkt 的衰退展現了殘酷的現實:即使架構貼近標準,runtime 專案若失去商業與社群支持,依然會被邊緣化。
containerd 捐贈 CNCF:解耦的先決條件
移除 dockershim 之所以可行,先決條件在於容器核心引擎早已不再是 Docker 公司的私有財產。
2016 年底,Docker 宣布將核心 runtime 獨立為開源專案 containerd(見 SD Times 報導)。
隨後專案於 2017 年 3 月 29 日正式捐贈給 CNCF 進入孵化階段。
containerd 關鍵里程碑:
+-- 2016 年底:Docker 宣布獨立 containerd 並表明捐贈意向
+-- 2017-03-29:正式獲准加入 CNCF(孵化層級)
+-- 2017-12-05:發布 containerd 1.0 版本
+-- 2018-05-24:containerd 1.1 內建 CRI 外掛並達到
正式發布(GA)
+-- 2019-02-28:通過各項考核,正式自 CNCF 畢業
將 containerd 獨立並捐贈給 CNCF,把上層開發者工具與底層容器引擎分家。
隨後 2018 年 5 月 24 日,containerd 1.1 內建 CRI 外掛正式發布,更讓 kubelet 有了中立且穩定的介面能直接操作引擎,不再需要繞經 Docker。
為什麼必須移除:技術障礙與組織誘因
維護負擔與現代核心特性的阻礙
外界常把「追求效能」當成移除 dockershim 的主因。
改用原生 CRI 確實「效能嚴格來說更好、開銷更少」,但歷次官方公告表明,「維護成本」才是核心動機。
2020 年 12 月問答集直言:「維護 dockershim 已成為 Kubernetes 維護者沉重的負擔。」
更關鍵的是架構層面的障礙。官方在 2022 年 2 月更新的問答集中指明,cgroups v2 與使用者命名空間(user namespaces)等現代特性正在新的 CRI runtime 中實作。
在 cgroups v2 方面,Docker 長期偏好 cgroupfs 驅動,對 cgroups v2 支援起步較晚。若 kubelet 與 Docker 的驅動不一致,節點在面對記憶體壓力時會陷入資源記帳衝突,甚至導致 Pod 無法重建。
在使用者命名空間方面,CRI 要求能針對個別 Pod 或容器設定獨立的 UID/GID 映射以實現無根容器;而 Docker daemon 當時主要依賴全域的 --userns-remap,無法滿足多租戶 Pod 的隔離需求。
這兩項能力都需要深入操作行程邊界,卡在中間的 dockershim 形成了無法跨越的結構性障礙,移除它才能讓節點層技術繼續推進。
產品定位與維護成本的錯位
技術債的深層原因往往源自組織架構與定位分歧。dockershim 自 2016 年起就是由 Kubernetes 社群自願維護的內建程式碼。
Docker Engine 的核心是開發者體驗、單機 CLI、Compose 與本地工具鏈;維護一組 gRPC CRI 介面對 Docker 自身產品價值有限。
結果變成了 Docker 公司坐享 Kubernetes 預設整合帶來的市場聲量,相容轉譯的工程成本卻全由開源社群背負。
當社群不再願意在 kubelet 核心內無償承擔這層維護負擔時,移除 dockershim 只是時間問題。
遠超規範標準的治理緩衝期
儘管決定移除,Kubernetes 社群仍給予了極為充足的過渡跑道。
對照 Kubernetes API 棄用政策,Beta 介面棄用後需維持 9 個月或 3 個版本。
dockershim 雖屬內部元件,獲得的寬限期卻遠高於官方標準:
棄用至移除時間線:
+-- 2020-12-02:發布 Don't Panic 與問答集宣導
+-- 2020-12-08:Kubernetes 1.20 發布,正式宣布棄用
+-- 2020-12-29:開立 KEP-2221 追蹤移除里程碑
+-- 2022-01-07:官方確認 1.24 移除,
承諾 1.23 支援一年
+-- 2022-05-03:Kubernetes 1.24 正式發布並移除 dockershim
時程最初規劃於 1.22(最快 1.23),社群隨後主動推遲至 1.24 以留給生態充裕準備時間。
從宣布棄用到正式移除歷經 17 個月、橫跨 4 個版本。
透過 KEP-2221 正式追蹤,重大變更循公開透明的提案機制落實推進,展現了成熟的治理水準。
移除過程:溝通、接手與正式發布
九篇官方文章接力釋疑
為了化解「Kubernetes 不再支援 Docker」的恐慌,官方在 17 個月內密集發布了七篇專題專文與兩次版本公告。
官方宣導節奏層層推進:從 2020 年底預告的 Don’t Panic、倒數階段催促檢查的 Are you ready for dockershim removal,到發布前夕的提醒 Is Your Cluster Ready for v1.24?,以及正式移除當天 Kat Cosgrove 撰寫的歷史脈絡總結。
社群文章反覆澄清映像相容性、提供除錯對照表與遷移指南。「提前一年半、多波次宣導」成為後續變更的標準劇本。
Mirantis 接手:cri-dockerd 的長尾出口
對於強依賴 Docker Engine 特性的環境,社群給出的替代方案是移至核心外獨立維護。
當時剛自 Docker 業務重組中收購其企業業務的 Mirantis,為了滿足既有客戶的相容需求,在棄用宣布兩天後於 2020 年 12 月 4 日公開宣布接手,與 Docker 合作在核心外維護獨立墊片 cri-dockerd。
此舉讓需要 Docker Engine 的企業擁有合規的外部解法,官方 Container Runtimes 文件至今仍將其列為合法選項。
1.24 正式發布與網路管線責任劃分
2022 年 5 月 3 日,Kubernetes 1.24 正式發布,dockershim 正式自 kubelet 原始程式碼中刪除。
隨 dockershim 移除的還有 kubelet 內部的網路外掛呼叫管線(即 --network-plugin=cni 等參數)。
在舊架構下,kubelet 透過 dockershim 直接叫用 CNI 外掛;而在原生 CRI 架構下,呼叫 CNI 的職責移交給了 runtime 本身(由 containerd 的 cri 外掛或 CRI-O 讀取 /etc/cni/net.d)。
這一變更釐清了元件邊界,kubelet 得以專注於 Pod 規格排程,不再插手底層網路二進位檔的直接執行。
移除之後的連鎖反應
三大託管雲的收斂歷程
三大雲端供應商的推進節奏,清晰展現了生態向 containerd 的收斂:
| 公有雲端服務 | containerd 預設版本 | 遷移策略與時程 |
|---|---|---|
| Azure AKS | Kubernetes 1.19+ | 行動最早,早於官方宣布棄用前即預設使用 containerd |
| AWS EKS | Kubernetes 1.24+ | 1.23 版前維持 Docker Engine,1.24 起全面改為預設 containerd |
| Google Cloud GKE | Kubernetes 1.24+ | 1.24 起停用 Docker 映像,2023 年 9 月 1 日自動遷移殘留節點集區 |
三大公有雲的平滑升級讓絕大多數託管使用者無感完成切換。
containerd 隨後步入成熟,於 2024 年底推出 2.0 LTS 版本,發布週期與 Kubernetes 深度同步;CRI-O 則持續維持穩固的替代方案地位。
邊界周邊生態的連鎖衝擊
抽換 runtime 不只是置換二進位檔,節點周邊的維運生態也跟著連帶洗牌。
首先是 cgroup 驅動的強制收斂。官方在容器 runtime 文件中提出明確警告:
「kubelet 與容器 runtime 必須使用相同的 cgroup 驅動程式並有一致的設定,這一點至關重要。」
切換至 containerd 時,業界標準做法是在 /etc/containerd/config.toml 中設定 SystemdCgroup = true,徹底終結 cgroupfs 與 systemd 的雙重管理混亂。
其次是叢集內 CI/CD 建置模式解耦。以往在 Pod 內掛載宿主機 /var/run/docker.sock 的 DooD(Docker-outside-of-Docker)模式隨 dockerd 退場而失效,掛載直接跳出找不到檔案的錯誤訊息。
WARNING
升級 1.24 前先盤點建置流程:掛載 /var/run/docker.sock 的 DooD 式 CI Job 會隨 dockerd 退場立即失效;請改用 Kaniko、Buildah 等 daemonless 工具,讓建置流程不再依賴宿主機的 dockerd 常駐行程。
最後是觀測與日誌管線的重構。日誌儲存路徑從 Docker 時代的 /var/lib/docker/containers/<id>/<id>-json.log,轉換為 CRI 標準的 /var/log/pods/。直接讀取 Docker API 的監控 DaemonSet 也全面轉向相容 CRI 端點。
平台架構演進的四點啟示
dockershim 的退場為雲原生基礎設施的架構演進留下了清晰的工程經驗:
- 內建特例收斂為標準介面:「內建特例 → 標準介面 → 移除特例」成為經典劇本。隨後的雲端整合亦歷經 KEP-2395,將廠商程式碼自核心剝離,由獨立的 Cloud Controller Manager(CCM)承接。
- 長緩衝期是工程治理的防線:給予 17 個月跑道並以九篇官方文章反覆釋疑。重大架構調整必須給予周邊生態足夠的緩衝,才能確保各家發行版與工具鏈同步跟進。
- 退場通道交由市場承接:社群並未封殺特定技術,而是透過標準介面讓商業夥伴維護 cri-dockerd,兼顧主流標準收斂與長尾相容需求。
- 標準介面帶來的解耦紅利:擺脫了 Docker 的具體實作細節後,kubelet 與 CRI runtime 的職責分工更加純粹。這為日後節點引入 WASM(如 runwasi)與微虛擬機器(如 Kata Containers)等新型 runtime 提供了清晰的擴充邊界。