Claude が書いた記事
ネットワーク屋が降りる Kubernetes:Pod IP は物理 LAN にどう乗っているか
Pod IP / cni0 / veth / VXLAN を、物理 LAN との対比で順に解剖した学習ログ。「Pod は IP を持つが、その IP は物理 LAN には存在しない」を起点に、CNI が物理ネットワークの上にオーバーレイをどう重ねているかを腹落ちさせるまでの記録。
俺の最初の疑問
前回 HBM の記事で LLM 推論サーバーの「サーバー内」を解剖した。次は「そのサーバーの上で動いている Kubernetes」が気になる。
特に俺はネットワーク屋なので、引っかかったのはここ。
Pod には IP がついているらしい。10.244.x.x みたいな。でもうちの社内 LAN は 10.0.x.x やぞ? その Pod IP はどこに居るんや?物理 LAN とどう繋がっとるんや?
腹落ちするまで、k8s の論理階層を一旦上から下まで降りて、ネットワーク側に登り直す経路を取った。
まず一言でいうと
Pod ネットワークは、物理 LAN の上に重ねた「オーバーレイ」。
Pod IP は物理 LAN には存在しない、クラスタ内だけで通用する別レイヤの IP。物理 LAN は、その Pod ネットワークを「運ぶ運送網」として下で動いている。
VPN や VLAN を作ってその上に IP を振っている、と思うと近い。違いは「k8s 用にカスタムされた専用オーバーレイ規格」を使うことと、Pod が短命なので 動的に IP が払い出されては消える こと。
何と比べるとわかるか
普通の物理ネットワークと、k8s の Pod ネットワークは別レイヤに居る:
| 物理 LAN | Pod ネットワーク | |
|---|---|---|
| IP プール | 10.0.x.x (社内 VLAN 等) | 10.244.x.x (CNI が払う) |
| 持ち主 | Node (VM のホスト) | Pod |
| ルータ | 物理ルータ / クラウドの VPC | Linux kernel + CNI プラグイン |
| Node 境界の越え方 | 普通のルーティング | VXLAN カプセル化 or BGP 広告 |
| 寿命 | 半永久 | 数秒〜数日 (Pod の寿命次第) |
物理ネットワークの上に、k8s 用のもう一段のネットワークを乗せている、という二層構造として見ると整理がつく。
何が問題なのか
Pod は 短命で何百〜何千個立つ。それぞれに物理 LAN の IP を払うのは無理がある:
- 物理 LAN の IP プールが枯渇する
- Pod が死ぬ・生まれる頻度が高すぎて、DHCP では追いつかない
- Pod が Node を跨いで移動する場合、物理ルートを毎回書き換える必要が出る
- 物理ネットワーク管理者と k8s 運用者の責任境界が曖昧になる
→ 「クラスタ内専用の IP プールを別に作って、CNI が動的に払う」 という解決策。物理 LAN とは独立した名前空間で IP を回すことで、上の問題を全部回避する。
図で見る
まず k8s の全体像。1 つのクラスタが複数の Node からなり、各 Node の中で Pod が動いている:
次に 1 つの Node の中、Pod がネットワーク的にどう繋がっているか:
最後に、Pod 同士の通信が実際にどう流れるか (同 Node / 異 Node):
混乱しやすいポイント
① IP を持つのは Pod、Container ではない
NET namespace は Pod 単位で分離されている (Container 単位ではない)。同じ Pod 内の Container たちは同じ NET ns に join しているので:
- 全員が同じ Pod IP を持つ
- 互いに
localhostで通信できる - 同じポート番号は Pod 内で 1 個しか使えない
② cni0 は L2 ブリッジだが、自身に IP を持って「L3 入口」も兼ねる
ここが一番引っかかった。「L2 と言ったのに、ゲートウェイ IP を持つって矛盾やん」と。
メンタルモデルは Layer 3 スイッチ。
- 普通の L2 スイッチは MAC を見てフレームを転送するだけ
- L3 スイッチは VLAN ごとに SVI (Switch Virtual Interface) を持ち、SVI に IP を振る
- すると VLAN 内は L2 で完結し、VLAN を跨ぐ通信は SVI 経由で L3 ルーティングできる
cni0 もこれと同じ二役構造:
- L2 機能: 同 Node の Pod 同士の通信を MAC で転送
- L3 入口: cni0 IF に IP (10.244.1.1 等) が振ってあり、その IP 宛のパケットは kernel の routing に渡す
実際の L3 ルーティングは kernel が行うので、「cni0 がルーティングする」というよりは「cni0 はゲートウェイ IP の持ち主、ルーティングは kernel」が正確。
③ Pod の default gateway = cni0 の IP
Pod 内の routing table:
10.244.1.0/24 dev eth0 proto kernel scope link src 10.244.1.7
default via 10.244.1.1 dev eth0
普通の Linux ホストと同じ。Pod から見れば「ゲートウェイは 10.244.1.1 (cni0)」というだけ。ARP で MAC 引いて L2 で送る、いつもの動き。
④ Pod IP は最後まで変わらない (NAT 禁止)
k8s ネットワークの仕様として 「Pod 同士の通信で NAT をしてはいけない」 という鉄則がある。Src=10.244.1.7, Dst=10.244.2.5 のまま、Node を跨いでも IP は変わらない。
これがあるおかげで Pod 側はネットワーク構成を意識せず、ただの IP 通信として書ける。代わりに Node 境界をどう越えるかの責任が CNI 側に乗る。
⑤ Node 越えは CNI ごとに別の戦略
NAT 禁止の制約下で「Pod IP のまま物理 LAN を渡す」方法は複数ある:
| CNI | 戦略 |
|---|---|
| Flannel (VXLAN) | Pod パケットを UDP / 4789 にカプセル化して Node 間で運ぶ。物理 LAN は外側だけ見える |
| Calico (BGP) | 各 Node が自分の Pod CIDR を BGP で物理ルータに広告。物理 LAN がそのまま Pod IP をルーティング |
| Cilium (eBPF) | カーネル内 eBPF プログラムが直接転送。cni0 や iptables を bypass する高速経路を持つ |
⑥ flannel.1 は kernel の中の仮想 IF、flanneld は userspace
Flannel を例にすると、Node 越えで使われる flannel.1 という IF は:
- kernel の中にある
net_device構造体 - vxlan ドライバが挙動を提供 (パケットの encap / decap を実行)
- Pod の NET ns には存在しない、Node の root NET ns に居る
一方、flanneld という userspace デーモンは:
- k8s API を見て各 Node の Pod CIDR を学習
- kernel に「ここを通って」と routing / FDB を入れるだけ
- 毎パケットには介在しない (介在したら遅すぎる)
これが 「ユーザー空間 = 制御平面、kernel = データ平面」 の典型パターン。iptables や conntrack や eBPF も同じ構造。
たとえ話
物流業のコンテナ船で考える。
- 物理 LAN = 海 (世界中の物流網)
- Node = 港 (コンテナを積み下ろしする拠点)
- Pod = 港の倉庫の内線住所 (その港の中でだけ通用するアドレス)
- cni0 = 港の管理事務所兼仕分け場 (同じ港の倉庫同士はここで完結。外への荷物はここから出す)
- Pod IP = 倉庫の内線番号
- 物理ホスト IP = 港の住所
Pod から見ると「他の港の倉庫宛に荷物を送る」のは「宛先 = 別港の倉庫の内線番号 で送るだけ」。実際の運搬方法は知らない。
CNI が裏で:
- Flannel = 港でコンテナ船 (VXLAN) に荷物を積む → 反対側の港で下ろす → 中身の住所は変わらない
- Calico = コンテナ船を使わず、荷物に「あの港のこの倉庫宛」とそのまま書いて海に流す。海の管制官 (物理 LAN のルータ) に「あの港にはこの倉庫番号が居る」と事前に教えてある (= BGP 広告)
どっちも宛先の倉庫の内線番号 (Pod IP) は変わらない。違うのは「港の外をどう運ぶか」の戦略だけ。
ニュースを読むための変換表
| ニュース / ドキュメントの言葉 | つまり何の話 |
|---|---|
| Pod | Container 1 + 個を束ねた k8s の最小実行単位。NET ns を共有する |
| Node | kubelet が動いている OS (VM or ベアメタル)。クラスタの構成員 1 つ |
| Cluster | Node を束ねた 1 つの論理単位 |
| Pod IP / Pod CIDR | クラスタ内専用の Pod 用 IP プール |
| Service IP / ClusterIP | k8s が払う仮想 IP (Pod IP の上にさらに重なる別レイヤ) |
| CNI | k8s と物理ネットワークを繋ぐプラグイン規格 |
| Flannel / Calico / Cilium | 代表的な CNI プラグイン (3 強) |
| VXLAN | UDP / 4789 上で L2 フレームを運ぶオーバーレイ規格 (RFC 7348) |
| iptables / IPVS | kube-proxy が裏で使うパケット転送ルール |
| veth pair | 仮想 NIC の表裏ペア。Pod を Node の bridge に繋ぐケーブル |
| containerd / runc | k8s ノードで動くコンテナランタイム (Docker daemon の後継) |
この記事の続きとして、未来の自分が次に掘るといいトピック。
- Service IP と kube-proxy ── Pod IP の上にもう一段ある仮想 IP (10.96.x.x) と、iptables / IPVS でのロードバランス。ネットワークの次の一段。
- Ingress / LoadBalancer ── 外から Pod へ入る側とクラウド LB 連携。今回の「中の配線」の外向き版。
- CNI の中身比較 (Calico の BGP / Cilium の eBPF) ── プラグインで配線がどう変わるか。物理ネットワーク側への要求の違いが見える。
- DevicePlugin と
nvidia.com/gpu── GPU 8 枚が k8s からどう見えるか。LLM サーバーの記事と直結する入口。 - ネットワーク管理者から見たクラスタ運用 ── BGP オペレータとして何を見るか。物理 LAN 側からの視点。