← 返回上一頁
Kubernetes

用最低成本固定 Kubernetes Egress 來源 IP:iptables + iproute 實戰方案

本頁目錄

問題背景

大部分使用公有雲的團隊比較關注 Ingress(流量如何進入容器),但對 Egress(流量如何從 Kubernetes 出去)往往不太在意,畢竟能出去就好。

然而在某些場景下,外部服務會限制來源 IP 的白名單(通常是資安要求),這時就需要確保所有從 Kubernetes 叢集送出的封包都使用可控且固定的來源 IP。問題在於:正常情況下封包會經過所在節點的 SNAT 處理,而節點可能會動態增減,來源 IP 自然也無法預測。

Kubernetes 內建並沒有 Egress 的概念,通常需要依靠 Istio 等 Service Mesh 方案來將封包導向特定節點。本文示範一種更輕量的做法:用 Linux 原生的 iptables + iproute 工具,將封包轉發到固定的 Gateway 節點,再由該節點進行 SNAT 轉發。

環境假設

項目 設定
CNI Flannel 或 Canal
Kubernetes 版本 1.24.x
Cluster CIDR 172.16.0.0/14
Private Network CIDR 10.0.0.0/8
Gateway Node 的 flannel.1 IP 172.16.4.0

流量分類

從 Pod 出發的流量,依據目的地可以分為三類:

  • Pod → 172.16.0.0/14:In-cluster 流量(叢集內部通訊)
  • Pod → 10.0.0.0/8:Cluster Egress 流量,目的地在內網
  • Pod → 其他:Cluster Egress 流量,目的地在外網

我們的目標是將「目的地在內網的 Egress 流量」固定為特定的來源 IP。只需要三個步驟即可完成。

步驟一:新增路由表

新增一條靜態路由到自訂路由表 56,作用是:凡是目的地為 10.0.0.0/8 的封包,下一跳必須經過 Gateway Node 的 flannel.1 介面(172.16.4.0):

ip route add 10.0.0.0/8 via 172.16.4.0 dev flannel.1 onlink table 56

其中 onlink 表明此 Gateway 是在鏈路上可直接到達。驗證結果:

$ ip route show table 56
10.0.0.0/8 via 172.16.4.0 dev flannel.1 onlink

步驟二:設定路由規則(Policy Routing)

新增兩條路由規則來區分 in-cluster 流量和 egress 流量。順序很重要 — in-cluster 流量需要有更高的優先權,確保它走預設路由表(main):

# In-cluster 流量:優先權 5000,走 main 路由表(保持預設行為)
ip rule add from 172.16.0.0/14 to 172.16.0.0/14 pref 5000 table main

# Egress 流量:優先權 5566,走自訂路由表 56
ip rule add from 172.16.0.0/14 pref 5566 table 56

驗證結果:

$ ip rule show
0:     from all lookup local
5000:  from 172.16.0.0/14 to 172.16.0.0/14 lookup main
5566:  from 172.16.0.0/14 lookup 56
32766: from all lookup main
32767: from all lookup default

這樣一來,Pod 發出的封包如果目的地也在 Cluster CIDR 內(in-cluster),會優先匹配 pref 5000 走 main 表;其餘來自 Cluster CIDR 的封包則走 table 56,被導向 Gateway Node。

步驟三:調整 iptables POSTROUTING 規則

正常情況下,Flannel CNI 會在 POSTROUTING chain 對 Cluster CIDR 來源的封包做 SNAT(MASQUERADE)。我們需要在 Flannel 的 MASQUERADE 規則之前,為 Egress 流量加一條 RETURN,跳過 SNAT 處理:

iptables -t nat -I POSTROUTING 3 -s 172.16.0.0/14 -d 10.0.0.0/8 -j RETURN

驗證結果:

$ iptables -t nat -L POSTROUTING --line-numbers
Chain POSTROUTING (policy ACCEPT)
num  target          prot opt source          destination
1    KUBE-POSTROUTING  all  --  anywhere        anywhere
2    RETURN          all  --  172.16.0.0/14   10.0.0.0/8        # 新增
3    RETURN          all  --  172.16.0.0/14   172.16.0.0/14     # flanneld masq
4    MASQUERADE      all  --  172.16.0.0/14  !base-address.mcast.net/4
5    RETURN          all  -- !172.16.0.0/14   ...

這樣 Egress 封包在離開節點前就不會被 SNAT,會保持原始的 Pod IP 經過 flannel tunnel 到達 Gateway Node,再由 Gateway Node 統一做 SNAT。

部署建議

以上三個步驟需要在每個需要固定 Egress IP 的節點上執行。手動操作容易出錯,建議的做法是:

  • 將以上步驟寫成 Shell Script 或開發成專用程式
  • 透過 DaemonSet 部署到目標節點上,這樣新加入叢集的節點也會自動套用設定
  • 不需要所有節點都執行 — 可以規劃特定的節點集合來運行這些 DaemonSet
  • 作為 Gateway 的節點本身不需要做這些設定,它只負責接收轉發過來的封包並做 SNAT

方案特點

優勢 限制
不需要 Service Mesh 等重量級方案 僅在 Flannel/Canal CNI 上驗證過
使用 Linux 原生工具,學習成本低 Gateway Node 成為單點,需考慮高可用
透過 DaemonSet 可自動化部署 需要對 Linux 路由有基本理解
對叢集效能影響極小 iptables 規則順序必須正確

這個方案的核心思路是「用 Policy Routing 將特定目的地的封包導向固定節點」,概念簡單但效果顯著。如果你的場景只需要固定內網 Egress IP,這比導入整套 Service Mesh 要省事得多。

分享這篇
X LinkedIn Facebook Hacker News Reddit

發佈留言

發佈留言必須填寫的電子郵件地址不會公開。 必填欄位標示為 *

這個網站採用 Akismet 服務減少垃圾留言。進一步了解 Akismet 如何處理網站訪客的留言資料