本文已發布超過一年。較舊的文章可能包含過時的內容。請檢查頁面中的資訊自發布以來是否已變得不正確。

Kubernetes 的 IPTables 鏈不是 API

某些 Kubernetes 组件(例如 kubelet 和 kube-proxy)會建立 iptables 鏈和規則,作為其運作的一部分。這些鏈從未打算成為任何 Kubernetes API/ABI 保證的一部分,但某些外部组件仍然使用其中一些鏈(特别是使用 KUBE-MARK-MASQ 將封包標記為需要偽裝)。

作為 v1.25 版本的一部分,SIG Network 明確聲明:Kubernetes 建立的 iptables 鏈(除了一個例外)僅供 Kubernetes 自身内部使用,第三方组件不應假設 Kubernetes 會建立任何特定的 iptables 鏈,或者如果這些鏈確實存在,則這些鏈將包含任何特定的規則。

然後,在未來的版本中,作為 KEP-3178 的一部分,我們將開始逐步淘汰 Kubernetes 自身不再需要的某些鏈。使用 KUBE-MARK-MASQKUBE-MARK-DROP 或其他 Kubernetes 產生的 iptables 鏈的 Kubernetes 外部组件,現在應開始遷移並遠離它們。

背景

除了各種特定於服務的 iptables 鏈之外,kube-proxy 還建立某些通用 iptables 鏈,它將其用作服務代理的一部分。過去,kubelet 也將 iptables 用於某些功能(例如為 Pod 設定 hostPort 映射),因此它也冗餘地建立了一些相同的鏈。

但是,隨著 Kubernetes 1.24 中 dockershim 的移除,kubelet 現在永遠不再為自己的目的使用任何 iptables 規則;它過去用於 iptables 的事情現在始終是容器運行時或網路外掛程式的責任,並且 kubelet 沒有理由建立任何 iptables 規則。

同時,儘管 iptables 仍然是 Linux 上預設的 kube-proxy 後端,但它不太可能永遠保持預設,因為相關的命令行工具和核心 API 本質上已被棄用,並且不再接收改進。(RHEL 9 記錄警告,如果您使用 iptables API,即使透過 iptables-nft。)

儘管截至 Kubernetes 1.25,iptables kube-proxy 仍然很受歡迎,並且 kubelet 繼續建立它歷史上建立的 iptables 規則(儘管不再使用它們),但第三方軟體不能假設核心 Kubernetes 组件將來會繼續建立這些規則。

即將到來的變更

從現在起幾個版本開始,kubelet 將不再在 nat 表中建立以下 iptables 鏈

  • KUBE-MARK-DROP
  • KUBE-MARK-MASQ
  • KUBE-POSTROUTING

此外,filter 表中的 KUBE-FIREWALL 鏈將不再具有目前與 KUBE-MARK-DROP 關聯的功能(並且它最終可能會完全消失)。

此變更將透過 IPTablesOwnershipCleanup 功能閘道逐步引入。該功能閘道在 Kubernetes 1.25 中可用,並且可以手動啟用以進行測試。目前的計畫是它將在 Kubernetes 1.27 中預設啟用,儘管這可能會延遲到稍後的版本。(它不會早於 Kubernetes 1.27 發生。)

如果您使用 Kubernetes 的 iptables 鏈該怎麼辦

(儘管以下討論重點是仍然基於 iptables 的短期修復,但您可能也應該開始考慮最終遷移到 nftables 或另一個 API)。

如果您使用 KUBE-MARK-MASQ...

如果您正在使用 KUBE-MARK-MASQ 鏈來導致封包被偽裝,您有兩個選擇:(1)重寫您的規則以直接使用 -j MASQUERADE,(2)建立您自己的替代「標記以進行偽裝」鏈。

kube-proxy 使用 KUBE-MARK-MASQ 的原因是,在許多情況下,它需要在封包上同時調用 -j DNAT-j MASQUERADE,但在 iptables 中無法同時執行這兩者;DNAT 必須從 PREROUTING(或 OUTPUT)鏈調用(因為它可能會更改封包將路由到的位置),而 MASQUERADE 必須從 POSTROUTING 調用(因為它選擇的偽裝來源 IP 取決於最終的路由決策是什麼)。

理論上,kube-proxy 可以有一組規則來匹配 PREROUTING/OUTPUT 中的封包並調用 -j DNAT,然後有第二組規則來匹配 POSTROUTING 中的相同封包並調用 -j MASQUERADE。但是,為了提高效率,它只在 PREROUTING/OUTPUT 期間匹配它們一次,此時它調用 -j DNAT,然後調用 -j KUBE-MARK-MASQ 以在核心封包標記上設定一個位,以提醒自己。然後稍後,在 POSTROUTING 期間,它有一個單一規則,可以匹配所有先前標記的封包,並在它們上調用 -j MASQUERADE

如果您有大量規則需要像 kube-proxy 一樣對相同的封包應用 DNAT 和偽裝,那麼您可能需要類似的安排。但在許多情況下,使用 KUBE-MARK-MASQ 的组件只是因為它們複製了 kube-proxy 的行為,而沒有理解 kube-proxy 這樣做的原因。許多這些组件可以很容易地重寫為僅使用單獨的 DNAT 和偽裝規則。(在未發生 DNAT 的情況下,更沒有理由使用 KUBE-MARK-MASQ;只需將您的規則從 PREROUTING 移至 POSTROUTING 並直接調用 -j MASQUERADE。)

如果您使用 KUBE-MARK-DROP...

KUBE-MARK-DROP 的基本原理與 KUBE-MARK-MASQ 的基本原理類似:kube-proxy 希望在 nat KUBE-SERVICES 鏈中的其他決策旁邊做出封包丟棄決策,但您只能從 filter 表中調用 -j DROP。因此,它使用 KUBE-MARK-DROP 來標記封包以便稍後丟棄。

一般而言,消除對 KUBE-MARK-DROP 的依賴性的方法與消除對 KUBE-MARK-MASQ 的依賴性的方法相同。在 kube-proxy 的情況下,實際上很容易將 nat 表中 KUBE-MARK-DROP 的使用替換為 filter 表中對 DROP 的直接調用,因為 DNAT 規則和丟棄規則之間沒有複雜的交互,因此丟棄規則可以簡單地從 nat 移至 filter

在更複雜的情況下,可能需要在 natfilter 中「重新匹配」相同的封包。

如果您使用 Kubelet 的 iptables 規則來判斷 iptables-legacyiptables-nft...

從容器内部操作主機網路命名空間 iptables 規則的组件需要某種方法來判斷主機是使用舊的 iptables-legacy 二進制檔案還是較新的 iptables-nft 二進制檔案(它們與底層不同的核心 API 通訊)。

iptables-wrappers 模組提供了一種讓此類组件自動偵測系統 iptables 模式的方法,但在過去,它透過假設 Kubelet 會在任何容器啟動之前建立「一堆」iptables 規則來做到這一點,因此它可以透過查看哪種模式定義了更多規則來猜測主機檔案系統中 iptables 二進制檔案正在使用的模式。

在未來的版本中,Kubelet 將不再建立許多 iptables 規則,因此基於計算規則數量的啟發式方法可能會失敗。

但是,從 1.24 開始,Kubelet 始終在使用的任何 iptables 子系統的 mangle 表中建立一個名為 KUBE-IPTABLES-HINT 的鏈。组件現在可以尋找此特定鏈,以了解 Kubelet(以及,大概系統的其餘部分)正在使用哪個 iptables 子系統。

(此外,自 Kubernetes 1.17 以來,kubelet 已在 mangle 表中建立一個名為 KUBE-KUBELET-CANARY 的鏈。雖然此鏈將來可能會消失,但它當然仍會存在於舊版本中,因此在任何最新版本的 Kubernetes 中,至少 KUBE-IPTABLES-HINTKUBE-KUBELET-CANARY 之一將會存在。)

iptables-wrappers 套件已 已使用此新啟發式方法更新,因此如果您之前使用過它,則可以使用更新的版本重新建構您的容器映像。

延伸閱讀

清理 iptables 鏈所有權並棄用舊鏈的專案由 KEP-3178 追蹤。