BGP Control Plane と Egress Gateway — クラスタの外へつながる道
一言でいうと
BGP Control Planeは、ノードのPodのCIDRとServiceのVIPを、BGPでネイバールーターにアドバタイズして、外からクラスターの中へ入ってくる道を作り、Egress Gatewayは、特定のPodが外へ出ていくときに、決められたノードとIPを経由させます。データパスをプログラミングしないのは、BGP Control Planeです。Egress Gatewayは、選択された外部向けのパケットをゲートウェイノードへ転送し、送信元アドレスをSNATするので、実際のデータパスの動作を変えます。経路を知らせる仕事と、パケットを処理する仕事を、区別する必要があります。
なぜ必要なのか
外のルーターがPodのCIDRへの経路を知らなければ、PodのIPを知っているだけでは到達できません。だからといって、PodのIPが本質的にクラスター内部専用だったり、外部からのアクセスに必ずNATが必要だったりするわけではありません。適切な往復の経路と、データパス・ポリシーがあれば、直接ルーティングできます。BGPを使う環境では、Ciliumが経路をアドバタイズして、外部のルーターがネクストホップを学習できるようにします。ServiceのVIPも、アドレスを割り当てられることと、外部からそのアドレスに到達できることは、別のことです。
反対方向の問題もあります。PodのIPは絶えず変わり、ノードのIPも変わりますが、古いファイアウォールは「このIPから来たものだけを許可する」方式で動作します。ドキュメントは、Egress Gatewayの用途を、まさにこの事例で説明しています。特定のネームスペースのPodが、古いインフラへ出ていくときに、予測可能なIPで出ていくようにすることです。CCAのBGP & External Networkingドメイン(6%)は、この2つの機能の役割と制約を問います。
どう動くのか
BGP Control Planeが行うことと、行わないこと
ドキュメントの最初の1文が、範囲を決めています。BGP Control Planeは、BGPで接続されたルーターに経路をアドバタイズして、Podネットワークとサービスを、クラスターの外から到達できるようにします。そして、「データパスをプログラミングしないので、クラスター内の到達性のために使ってはいけない」と明記しています。有効にする方法はbgpControlPlane.enabled=trueで、agentは、設定されたアドレスファミリーだけをアドバタイズします。IPv4だけを使うよう設定されたagentは、IPv6の経路をアドバタイズできません。
4つのリソース
| リソース | 役割 |
|---|---|
| CiliumBGPClusterConfig | nodeSelectorで選んだノードに適用するBGPインスタンス(localASN)とピア(peerASN、peerAddress、peerConfigRef) |
| CiliumBGPPeerConfig | 複数のピアが共有するセッション設定。タイマー、MD5認証、eBGP multihop、graceful restart、transport、アドレスファミリーとアドバタイズのセレクター |
| CiliumBGPAdvertisement | ルーティングテーブルに入れるプレフィックスの種類と属性(community、localPreference) |
| CiliumBGPNodeConfigOverride | ノードごとに別の値を与えたいときの設定 |
apiVersion: cilium.io/v2
kind: CiliumBGPClusterConfig
metadata: {name: cilium-bgp}
spec:
nodeSelector: {matchLabels: {rack: rack0}}
bgpInstances:
- name: instance-65000
localASN: 65000
peers:
- name: peer-65000-tor1
peerASN: 65000
peerAddress: fd00:10:0:0::1
peerConfigRef: {name: cilium-peer}
---
apiVersion: cilium.io/v2
kind: CiliumBGPAdvertisement
metadata: {name: bgp-advertisements, labels: {advertise: bgp}}
spec:
advertisements:
- advertisementType: PodCIDR
- advertisementType: Service
service: {addresses: [LoadBalancerIP]}
selector: {matchExpressions: [{key: bgp, operator: In, values: [blue]}]}
PeerConfigのfamilies[].advertisementsは、ラベルセレクターです。上の例のadvertise: bgpラベルが、そのセレクターに合致していないと、アドバタイズが実際には出ていきません。リソースをすべて作成したのに何もアドバタイズされない場合は、このラベルを先に見ます。
デフォルトの動作でよく間違えること
ドキュメントから、そのまま3つを移します。1つ目に、BGPインスタンスは、デフォルトでは受信ポートなしで起動します。同じノードでBirdのような別のBGPルーターが動けるようにした設計なので、Ciliumは接続を開始するだけで、受け付けはしません。受け付ける必要があるならlocalPortを指定しますが、ポート179はCAP_NET_BIND_SERVICEが必要です。2つ目に、タイマーのデフォルト値は、connectRetryが120秒、holdが90秒、keepaliveが30秒で、データセンターでは、hold 9秒・keepalive 3秒のように下げるよう勧めています。3つ目に、graceful restartを有効にすると、agentが再起動してもピアが経路をすぐに取り下げないので、データパスが転送を続けます。デフォルトのRestartTimeは120秒です。
何をアドバタイズするのか
PodCIDRのアドバタイズは、そのノードに割り当てられたPodのCIDRをアドバタイズするのであり、全体の範囲をアドバタイズするのではありません。これは、KubernetesまたはClusterPoolのIPAMでだけ動作し、MultiPool IPAMでは、CiliumPodIPPoolタイプで、プールをセレクターで選んでアドバタイズします。ほかのIPAMでは、PodCIDRタイプは何の効果もありません。Serviceのアドバタイズは、service.addressesにLoadBalancerIP・ClusterIP・ExternalIPを選んで入れ、VIPは正確な/32または/128の経路で出ていきます。同じVIPを複数のノードがアドバタイズすると、上流のルーターがECMPで負荷を分けますが、ルーターのECMP経路数の上限を超える可能性があるので、ネットワーク担当者とあらかじめ確認するようにという警告が付いています。セッションの状態は、cilium bgp peersで確認します。
Egress Gateway: 出ていく道を固定する
Egress Gatewayは、Podから特定のクラスター外部のCIDRへ向かうIPv4・IPv6の接続を、指定したゲートウェイノードに送り、そのノードの予測可能なIPでマスカレードします。有効にするには、egressGateway.enabled=trueとあわせて、BPFマスカレードとkube-proxyの置き換えが、どちらも有効になっている必要があります。ポリシーリソースは、クラスタースコープのCiliumEgressGatewayPolicyで、selectors[].podSelectorで元のPodを(ネームスペースはio.kubernetes.pod.namespaceラベルで)、destinationCIDRsで宛先を、excludedCIDRsで例外を記述します。内部のクラスターIP(Pod・ノード・APIサーバー)は、宛先の範囲に含まれていても、SNATの対象から除外されます。
制約も明確です。新しいPodには、ポリシーが適用されるまでに遅延があり、その間はPodのIPやノードのIPで出ていくことがあります。また、Cluster Meshと一緒に使えず、アイデンティティの保存をkvstoreにしたモードやCiliumEndpointSliceとも互換性がありません。
現場での姿
BGPセッションがEstablishedだからといって、Serviceへの到達まで証明されたわけではありません。まず、ピアのアドレス・AS・接続状態を確認し、次に、受信ルーティングテーブル(RIB)に宛先のプレフィックスがあるかを見ます。経路があるなら、選択されたネクストホップと、カーネルの転送経路(FIB)を確認し、実際のリクエストへとつなげていきます。経路がなければ、アドバタイズのセレクターとアドレスの割り当てから、経路があるのにリクエストが失敗するなら、Serviceのバックエンド・データパス・ポリシー・戻りの経路を調べます。1つのtimeoutだけを見て、認証の失敗やポリシーによるブロックと断定してはいけません。
Egress Gatewayは、有効にした直後に、「一部のリクエストが依然としてノードのIPで出ていく」という報告が来ますが、新しく起動したPodにポリシーが付くまでの遅延が原因であることが多いです。ファイアウォール側でゲートウェイのIPだけを許可していたなら、その短い区間のリクエストが拒否されます。
実測で切り分けた5つの状態
LabHubの2026-09-12の個人VMでの探査は、Cilium 1.20.1とFRR 8.4.4を接続して、次の状態を比較しました。FRRは、外部ルーターの役割のネットワークネームスペースに置き、デフォルト経路は入れませんでした。ここで経路がないというのは、実験の宛先への経路がないという意味であり、ルーターの接続ネットワークの経路まですべてないという意味ではありません。
| 状態 | ピア | 宛先のRIB | 宛先のFIB | 実際のPod HTTP |
|---|---|---|---|---|
| ピア設定前 | Idle | なし | なし | 接続失敗 |
| ピアだけ接続 | Established | なし | なし | 接続失敗 |
| PodCIDRをアドバタイズ、FRRのbgp no-ribを維持 | Established | あり | なし | 接続失敗 |
| FRRのno bgp no-ribを適用 | Established | あり | あり | 200 |
| PodCIDRのアドバタイズを取り下げた後 | Established | なし | なし | 接続失敗 |
bgp no-ribは、この探査で、BGPの学習とカーネル経路のインストールを意図的に分離したFRRの設定です。Ciliumのアドバタイズを有効にすれば、FRRの転送設定まで自動的に解決されるという意味ではありません。割り当てられたノードのPodCIDRは10.42.0.0/24であり、全体の設定プールである10.42.0.0/16ではありませんでした。失敗時のcurlの終了コードは7、出力は000でした。000は、サーバーが返したHTTPステータスコードではなく、この結果をNetworkPolicyのtimeoutによるブロックに置き換えて説明してもいけません。観測地点とコマンドの終了コードまで残してこそ、ほかの失敗と区別できます。
その後の別の比較では、選択したServiceのVIPの/32だけをアドバタイズして、アドバタイズしていない別のVIPとPodへの直接アクセスが失敗するかを確認しました。同じVMで作ったルーターのネームスペースは、独立した外部のマシンとは違って、socket-LBの影響を受けることがありました。Podの経路を一時的に入れると、アドバタイズしていないVIPまで成功し、比較がぼやけました。最終的に、socketLB.hostNamespaceOnly=trueが実行中のagentに適用されたかを確認したあと、Podの経路とデフォルト経路なしで、選択したVIPだけが200であることを確認しました。この設定を、あらゆる運用障害の万能な解決策として使うのではなく、実験環境が測定対象を変えていないかを点検する事例として読む必要があります。
この表は、開発用の探査の結果であり、学習者がプラットフォームで新しいBGPラボを完走したという証拠ではありません。後続のラボは、比較の状態を切り分けて、最後のステップのあとでも全体の再採点ができるように構成します。公式の動作は、FRR 8.4 BGPとCilium socket-LBのバイパスで確認できます。
次のクイズで確認すること
Gateway APIのリソースの役割とトラフィック分割のフィールド、Ciliumがほかのコントローラーと異なる点(eBPFによる横取り、ingressアイデンティティ)、ノードあたりのEnvoyの構造、WireGuardの鍵の配布とポート、BGPの4つのリソースの役割、PodCIDRのアドバタイズの範囲とVIPの経路の長さ、受信ポートのデフォルト値、Egress Gatewayの前提条件を問います。参考: Cilium BGP Control Plane、BGP Control Plane Resources、Egress Gateway。