MENU

bgplb:Kubernetes 外部路由 EIP Controller 的設計與實作

July 25, 2026 • Read: 22 • Network

bgplb 管理由外部邊緣路由器承載的 Kubernetes 公網 EIP。上游路由維持聚合前綴;叢集透過 WireGuard 與私有 BGP 接入較細的地址池。MetalLB 分配 Service VIP,bgplb 管理隧道、路由、回程策略與故障切換。

本文中的位址均來自 RFC 5737 文件網段。主機名稱、ASN、Registry 與實際部署位置已匿名化。

1. 需求與驗收條件

項目約束驗收條件
地址池粒度邊緣路由器持有聚合前綴,叢集使用 /26/28 等子網整個子網可由同一叢集分配
節點耦合Gateway 節點可能更換,也可能位於 NAT 後方節點主動建立隧道;無需固定節點公網位址
EIP 分配公網位址交付給 LoadBalancer Service支援自動選址與指定地址
路由收斂同一地址池在任一時間只有一個 Active GatewayActive 失效後,完整前綴切換至 Standby
封包尺寸底層網路不保證 1500-byte MTUTCP MSS、PMTUD 與 IPv4 fragmentation 可工作
回程一致性Service 可能把流量轉發至其他節點上的 Pod回應封包仍以 EIP 為來源,經原邊緣出口返回
既有路由外部 Transit BGP 已由邊緣路由器管理Controller 不修改外部 BGP Session
安全節點 Agent 需要操作核心網路狀態權限限制於指定節點;私鑰不進入 CRD 與日誌

2. 部署環境

元件配置
Edge Router宣告聚合公網前綴;提供可達的 UDP Endpoint;安裝叢集子網路由
Kubernetes多個可替換 Gateway Candidate;允許節點位於 NAT 後方
Service Dataplanekube-proxy / IPVS,externalTrafficPolicy: Cluster
Address AllocatorMetalLB IPAddressPool,停用對該池的 L2/BGP Advertisement
TunnelWireGuard over UDP,節點主動連線,Persistent Keepalive 維持 NAT Mapping
Internal RoutingWireGuard 內執行私有 BGP;預設 Hold Time 15 秒
MTUWireGuard 預設 1280,允許範圍 1280–1420

公網聚合前綴只在 Edge Router 對 Transit 宣告。叢集子網由 Active Gateway 經私有 BGP送至 Edge Router。這兩個 BGP 執行個體具有獨立的鄰居、Port 與路由責任。

3. 系統架構

flowchart LR
    C["External Client"] --> T["Transit"]
    T --> E["Edge Router<br/>Aggregate Route"]
    E -->|"WireGuard + Private BGP"| A["Gateway A<br/>Active"]
    E -.->|"Established Standby Tunnel"| B["Gateway B<br/>Standby"]
    A --> K["kube-proxy / IPVS"]
    K --> S["LoadBalancer Service"]
    S --> P["Pod Endpoint"]
元件責任
bgplb-controllerReconcile CRD、選擇 Active Peer、建立 MetalLB Pool、綁定 Service
Node Agent建立 WireGuard、宣告 Active Pool、配置 Source Policy Routing 與 MSS Clamp
Edge Agent接受 WireGuard Peer 與私有 BGP Session、安裝或撤回子網路由
MetalLB從指定 EIPPool 分配 Service VIP
Edge Router維持 Transit BGP 聚合宣告,轉送子網流量

控制面由四個 Cluster-scoped CRD 組成:

flowchart TB
    EG["EdgeGateway"] --> TP["TunnelPeer"]
    EP["EIPPool"] --> MP["MetalLB IPAddressPool"]
    EP --> RA["RouteAdvertisement"]
    TP --> RA
    RA --> NA["Node Agent"]
    RA --> EA["Edge Agent"]
    SV["LoadBalancer Service"] -->|"Pool Annotation"| EP
    MP -->|"Allocated VIP"| SV
  • EdgeGateway:隧道、私有 BGP、Node Selector 與 Standby 數量。
  • TunnelPeer:節點公鑰、Tunnel Address、Handshake、BGP 與 Ready 狀態。
  • EIPPool:聚合前綴、可分配子網、Gateway Reference 與 Namespace Policy。
  • RouteAdvertisement:Active Peer、前綴、Next Hop、MTU 與收斂狀態。

4. 封包處理

4.1 入站與回程

sequenceDiagram
    participant C as External Client
    participant E as Edge Router
    participant G as Active Gateway
    participant K as kube-proxy / IPVS
    participant P as Pod

    C->>E: dst = Service EIP
    E->>G: WireGuard Packet
    G->>K: EIP Packet
    K->>P: DNAT to Endpoint
    P-->>K: Response
    K-->>G: Reverse NAT, src = EIP
    G-->>E: Source Policy, table 10077
    E-->>C: Transit Response

入站封包保持未標記狀態,交由 IPVS 完成 Service DNAT。回應經 Reverse NAT 恢復 EIP 來源位址後,來源前綴規則選擇 bgplb 專用路由表 10077,再送入 WireGuard。

早期版本使用 Connection Mark 決定回程。IPVS 在 DNAT 後可能保留該標記,導致入站封包提前返回隧道。現行實作會清除舊規則,回程判斷只使用 EIP Source Prefix。一般 Pod 與節點流量繼續使用原有 Default Route。

4.2 MTU 與 Fragmentation

資料面同時配置四項措施:

  1. WireGuard Interface MTU 預設為 1280
  2. Edge 與 Node 的 Pool Route 使用 mtu lock 1280
  3. 跨隧道的 TCP SYN 套用 MSS Clamp。
  4. ICMP Fragmentation Needed / Packet Too Big 保持可達。

IPv4 Fragment 在進入 WireGuard 前的單片尺寸只要小於 Tunnel MTU,即可完整傳輸並由 Active Gateway 重組。帶 DF 的大型 UDP 依賴 PMTUD 與應用層重試。IPv6 資料面將在端到端 ICMPv6 Packet Too Big 驗證完成後加入。

4.3 NAT 狀態保存

Node 主動連接 Edge,預設每 25 秒送出 Persistent Keepalive。Agent Reconcile WireGuard 設定時保留核心已學到的 Endpoint 與 Listen Port,避免一次控制面更新清除 NAT Runtime。

5. 地址池與 Service

以下配置建立一個 Cluster-wide /26 EIP Pool。autoAssign: false 要求 Service 明確引用該池;Namespace Policy 限制可使用地址的工作負載範圍。

apiVersion: networking.example.net/v1alpha1
kind: EIPPool
metadata:
  name: external-ip-pool-1
spec:
  aggregate: 192.0.2.0/24
  addresses:
    - 192.0.2.0/26
  gatewayRef: edge-gateway
  autoAssign: false
  advertiseWhenEmpty: true
  serviceAllocation:
    namespaces:
      - ingress-system
    priority: 10

Service 可要求池內的固定 EIP:

apiVersion: v1
kind: Service
metadata:
  name: public-ingress
  namespace: ingress-system
  annotations:
    networking.example.net/eip-pool: external-ip-pool-1
    metallb.io/loadBalancerIPs: 192.0.2.10
spec:
  type: LoadBalancer
  loadBalancerClass: metallb.io/metallb
  externalTrafficPolicy: Cluster
  selector:
    app: public-ingress
  ports:
    - name: https
      protocol: TCP
      port: 443
      targetPort: 8443

Controller 檢查地址是否位於指定 Pool、是否已被佔用,以及 Namespace Policy。通過後,MetalLB 寫入 status.loadBalancer.ingress,bgplb 維持整個 Pool Prefix 的路由。

6. 故障切換

stateDiagram-v2
    [*] --> ActiveHealthy
    ActiveHealthy --> FailureDetected: Heartbeat stale / BGP down
    FailureDetected --> OldRouteWithdrawn: Withdraw prefix
    OldRouteWithdrawn --> PeerUpdated: Remove old AllowedIPs
    PeerUpdated --> StandbyAdvertising: Select ready standby
    StandbyAdvertising --> ActiveHealthy: Install new route

切換單位是完整的 EIPPool。Service VIP 不重新分配。Standby 已建立 WireGuard Session 並維持 NAT Mapping,接管時只更新 Prefix Ownership 與 BGP Advertisement。

目前版本沒有同步 Conntrack。切換期間既有 TCP Connection 可能 Reset;新連線在路由收斂後恢復。實測故障切換的封包中斷約為 3 秒。

7. 驗證結果

測試條件結果
TCP 基本連線外部 Client 經 EIP 存取 Service150 / 150
UDP 基本連線外部 Client 經 EIP 往返150 / 150
IPv4 Fragment4,000-byte UDP,外部 MTU 60010 / 10;觀察 70 個 Fragment;ReasmFails=0
TCP 大型回應8,000-byte Response20 / 20
Active / Standby移除 Active Gateway Candidate約 3 秒恢復
回程路由Pod 位於非 Gateway 節點EIP Source 保持,經 Edge 返回
CIUnit Test + Race、Vet、SAST、gosec、Trivy通過


上線前檢查項目

  • EdgeGatewayEIPPoolRouteAdvertisement 顯示 Ready=True
  • Active TunnelPeer 顯示最新 Handshake 與 BGP=ESTABLISHED
  • MetalLB 生成的 IPAddressPool 與 CRD Prefix 完全一致。
  • bgplb Pool 未被任何 MetalLB L2AdvertisementBGPAdvertisement 選中。
  • Edge Kernel Route 的 Next Hop 指向 Active WireGuard Peer。
  • 從叢集外部執行 TCP、UDP、Fragment、PMTUD 與 Failover 測試。

8. 安全邊界

  • Controller 以非 Root、Read-only Root Filesystem、Default Seccomp 執行。
  • Node Agent 使用 Host Network 並操作 WireGuard、iproute2iptablessysctl;只排程至帶有專用 Label 的 Gateway Candidate。
  • Command Runner 僅允許 ipiptablessysctlwg,不啟動 Shell。
  • Node Private Key 存在專用 Namespace 的 Kubernetes Secret;Edge Private Key 保存在 Root-only File。
  • CRD、Status、Event 與 Log 只包含 Public Key 或 Secret Reference。
  • Controller 只處理明確帶有 Pool Annotation 的 Service。

9. 版本邊界

項目v0.1 狀態
公網地址族IPv4
Pool OwnershipSingle Active / Standby
ECMP未實作
Conntrack Replication未實作
Transit BGP 管理不在 bgplb 範圍內
Service Traffic PolicyCluster
Linux DataplaneWireGuard、Policy Routing、iptables、IPVS

這個版本已完成 EIP 分配、NAT-friendly Tunnel、私有 BGP、Source Policy Return、MTU 保護與 Pool-level Failover。後續工作集中在 IPv6 PMTUD、Conntrack Replication、ECMP 與更細的 Dataplane Telemetry。