本文已超過一年。較舊的文章可能包含過時的內容。請檢查頁面中的資訊自發布以來是否已變得不正確。
為什麼 Kubernetes 不使用 libnetwork
Kubernetes 自 1.0 版本發布之前就已經有了非常基本的網路外掛程式形式,大約與 Docker 的 libnetwork 和容器網路模型 (CNM) 同時推出。與 libnetwork 不同,Kubernetes 外掛程式系統仍然保留其「alpha」標 designation。既然 Docker 的網路外掛程式支援已經發布並受到支援,我們收到的一個顯而易見的問題是為什麼 Kubernetes 尚未採用它。畢竟,供應商幾乎肯定會為 Docker 撰寫外掛程式 — 我們最好都使用相同的驅動程式,對吧?
在深入探討之前,重要的是要記住 Kubernetes 是一個支援多個容器執行階段的系統,而 Docker 只是其中之一。設定網路是每個執行階段的一個方面,因此當人們問「Kubernetes 是否會支援 CNM?」時,他們真正的意思是「Kubernetes 是否會在使用 Docker 執行階段時支援 CNM 驅動程式?」如果我們能夠在多個執行階段之間實現通用的網路支援,那就太好了,但這並不是一個明確的目標。
實際上,Kubernetes 並未針對 Docker 執行階段採用 CNM/libnetwork。事實上,我們一直在研究 CoreOS 提出並作為 App Container (appc) 規格一部分的替代容器網路介面 (CNI) 模型。為什麼?這有很多原因,包括技術性和非技術性原因。
首先,Docker 網路驅動程式的設計中存在一些基本假設,這些假設對我們造成了問題。
Docker 有「本機」和「全域」驅動程式的概念。本機驅動程式(例如「bridge」)是以機器為中心的,並且不進行任何跨節點協調。全域驅動程式(例如「overlay」)依賴 libkv(金鑰值儲存抽象)來跨機器協調。這個金鑰值儲存是另一個外掛程式介面,並且非常低階(金鑰和值,沒有語意意義)。若要在 Kubernetes 叢集中執行類似 Docker 的 overlay 驅動程式的東西,我們需要叢集管理員執行一個完全不同的 consul、etcd 或 zookeeper 實例(請參閱 多主機網路),否則我們必須提供我們自己的由 Kubernetes 支援的 libkv 實作。
後者聽起來很有吸引力,我們也嘗試實作它,但 libkv 介面非常低階,而且 schema 是在 Docker 內部定義的。我們必須直接公開我們底層的金鑰值儲存,或者提供金鑰值語意(在我們結構化的 API 之上,而 API 本身也是在金鑰值系統上實作的)。出於效能、擴展性和安全性考量,這兩者都不太有吸引力。最終結果是整個系統會變得更加複雜,而使用 Docker 網路的目標是簡化事情。
對於願意且能夠執行必要基礎架構以滿足 Docker 全域驅動程式並自行設定 Docker 的使用者來說,Docker 網路應該可以「正常運作」。Kubernetes 不會妨礙這種設定,而且無論專案朝哪個方向發展,這個選項都應該可用。但是,對於預設安裝,實際的結論是,這對使用者來說是不必要的負擔,因此我們不能使用 Docker 的全域驅動程式(包括「overlay」),這完全消除了使用 Docker 外掛程式的許多價值。
Docker 的網路模型做出了許多對 Kubernetes 無效的假設。在 Docker 1.8 和 1.9 版本中,它包含一個從根本上有缺陷的「探索」實作,這會導致容器中的 /etc/hosts
檔案損壞 (docker #17190) — 而且這無法輕易關閉。在 1.10 版本中,Docker 計劃 捆綁一個新的 DNS 伺服器,但目前尚不清楚這是否能夠關閉。容器層級的命名不適合 Kubernetes 的抽象 — 我們已經有自己的服務命名、探索和綁定概念,而且我們已經有自己的 DNS schema 和伺服器(基於完善的 SkyDNS)。捆綁的解決方案不足以滿足我們的需求,而且無法停用。
與本機/全域分割正交,Docker 同時具有程序內和程序外(「遠端」)外掛程式。我們研究了是否可以繞過 libnetwork(從而跳過上述問題)並直接驅動 Docker 遠端外掛程式。不幸的是,這意味著我們無法使用任何 Docker 程序內外掛程式,尤其是「bridge」和「overlay」,這再次消除了 libnetwork 的許多實用性。
另一方面,CNI 在哲學上更符合 Kubernetes。它比 CNM 簡單得多,不需要守護程式,而且至少在表面上是跨平台的(CoreOS 的 rkt 容器執行階段支援它)。跨平台意味著有機會啟用在不同執行階段(例如 Docker、Rocket、Hyper)中以相同方式運作的網路設定。它遵循 UNIX 哲學,即把一件事做好。
此外,封裝 CNI 外掛程式並產生更客製化的 CNI 外掛程式非常簡單 — 可以使用簡單的 shell 腳本完成。CNM 在這方面要複雜得多。這使得 CNI 成為快速開發和迭代的有吸引力的選項。早期原型已證明,可以將 kubelet 中幾乎 100% 目前硬式編碼的網路邏輯彈出到外掛程式中。
我們研究了為 Docker 撰寫一個執行 CNI 驅動程式的「bridge」CNM 驅動程式。結果證明這非常複雜。首先,CNM 和 CNI 模型非常不同,因此沒有任何「方法」對齊。我們仍然有上面討論的全域與本機和金鑰值問題。假設這個驅動程式會將自己宣告為本機,我們必須從 Kubernetes 取得關於邏輯網路的資訊。
不幸的是,Docker 驅動程式很難對應到像 Kubernetes 這樣的其他控制平面。具體來說,驅動程式不會被告知容器所連接的網路名稱 — 只是一個 Docker 在內部配置的 ID。這使得驅動程式很難對應回另一個系統中存在的任何網路概念。
網路供應商已向 Docker 開發人員提出了這個問題和其他問題,但通常以「按預期運作」結案 (libnetwork #139, libnetwork #486, libnetwork #514, libnetwork #865, docker #18864),即使它們使非 Docker 第三方系統更難以整合。在整個調查過程中,Docker 已明確表示他們對偏離目前路線或委派控制權的想法不是很開放。這讓我們非常擔憂,因為 Kubernetes 補充了 Docker 並增加了許多功能,但它本身存在於 Docker 之外。
由於所有這些原因,我們選擇投資 CNI 作為 Kubernetes 外掛程式模型。這會有一些不幸的副作用。其中大多數相對較小(例如,docker inspect
不會顯示 IP 位址),但有些很重要。特別是,由 docker run
啟動的容器可能無法與由 Kubernetes 啟動的容器通訊,並且網路整合商如果想要完全與 Kubernetes 整合,則必須提供 CNI 驅動程式。另一方面,Kubernetes 將變得更簡單、更靈活,並且早期引導(例如設定 Docker 以使用我們的橋接器)的許多醜陋之處將會消失。
當我們沿著這條路徑前進時,我們肯定會睜大眼睛和耳朵,尋找更好的整合和簡化方法。如果您對我們如何做到這一點有任何想法,我們非常樂意聽取您的意見 — 請在 slack 上或我們的 network SIG 郵寄清單中找到我們。