Claude が書いた記事
ネットワーク屋が見る AKS:LB は入口やなく出口、AGC は Pod IP へ直接届く
Application Gateway for Containers (AGC) を入口に AKS の north-south を解剖した学習ログ。「VMSS があるから LB が受け口やろ」という思い込みを、宛先 IP 中心の見方へ切り替えるまでの記録。テスト環境で見える Standard LB は入口でなく出口 (egress) で、AGC は Pod IP へ直接届く。
俺の最初の疑問
前回 Pod ネットワーク で、Pod 同士の横の通信 (east-west) が物理 LAN の上のオーバーレイに乗っている、まで降りた。
俺はネットワークの技術サポートをやっている。次に知りたいのは縦の通信 (north-south)── 外のインターネットから、中の Pod まで、リクエストがどう届くか。その入口として Azure の Application Gateway for Containers (AGC) を選んだ。
ここで一番引っかかったのは、自分の手癖から来る三段論法だった。
AKS (Azure Kubernetes Service) には VMSS (複数の VM) がある。VM があれば前段に Load Balancer がある。だから その LB が AGC からの受け口やろ?
テスト環境を見ると、確かに Standard Load Balancer がいる。これがほんまに AGC の入口なんか? ここを潰すのが今回の目的。
まず一言でいうと
AGC は、受け口に Load Balancer を置かない。宛先 = Pod の IP を直接指して届ける。
普通の Application Gateway なら、バックエンドプールに VM の IP を手で登録する。AGC では、そのバックエンドが Pod、つまり秒単位で増減し IP も変わる相手になる。それでも AGC は前段に LB を挟まず、今いる Pod の IP を宛先にして直接つなぐ。
公式 (AGIC のドキュメント、AGC はその後継) も言い切っている。
… eliminate the need for another load balancer or public IP address in front of the AKS cluster … communicates with pods by using their private IP address directly and doesn’t require NodePort or KubeProxy services.
じゃあテスト環境で見えていたあの LB は何やったのか。 ── あれは「入口」やなく「出口」だった。詳しくは後述。
何と比べるとわかるか
宛先 IP (dst) で見ると、似て見える経路がきれいに分かれる。
| 入口の種類 | 宛先 IP (dst) | 経路 | 層 |
|---|---|---|---|
type: LoadBalancer Service (public / internal) | LB の VIP | LB → ノードの NodePort → kube-proxy → Pod | L4 |
| AGC | Pod IP | AGC → VNet ルーティング → Pod が載ったノード → veth → Pod | L7 |
ルータで例えると、AGC の中も コントロールプレーンと データプレーン に分かれている、と見ると整理がつく。
- コントロールプレーン=どう運ぶか「決める」係。ルータでいう CPU が経路表を作る部分。AGC では ALB Controller (AKS 内で動く Pod) が、設定を組んで Azure 側へ伝える。
- データプレーン=実際にパケットを「運ぶ」係。ルータでいう ASIC。AGC では Microsoft 管理の proxy 群が、受けたリクエストを Pod へ流す。
これは前回記事で掴んだ「flanneld (制御) / kernel (データ)」の型と同じ。スケールが VNet になっただけ。
何が問題なのか
Pod は短命で、増減も移動も激しい。そこへ「前段に L4 LB を置いて、ノードの NodePort 経由で kube-proxy が振り分ける」という古典的な経路を取ると、余計なホップが挟まる。AGC が Pod へ直接届けるのは、このホップを減らすため。
ただ、本当の引っかかりは技術というより見方の側にあった。ネットワーク屋は「VM の塊の前には LB がいる」と体に染みついている。だから「LB が入口」と思い込む。これを 宛先 IP で考える見方 に切り替えると、「AGC の入口に LB はいらない」が腹に落ちる。
図で見る
まず、どこまでが AKS で、どこからが Azure リソースか。混乱の元は、コントローラだけが AKS の「中」にいて、操作する相手は「外」にいることだった。
次に、パケットの話に入る前に住所の地図を頭に置く。VNet・subnet・ノード・Pod CIDR がどこにいるか。Pod IP (10.10.x.x) が VNet (192.168.x.x) とは別レンジ=overlay だ、というのがここで見える。
そのうえで、AGC から Pod まで 1 つのパケットがどう届くか。上のマップの Pod「10.10.2.2」を宛先に、Azure CNI Overlay で外箱を VXLAN とした図解で追う (後述の通り基盤の encap 詳細は非公開なので、これは理解用の模型)。
混乱しやすいポイント
① 見えていた LB は「入口」やなく「出口」
AKS がデフォルトで作る Standard Load Balancer の本職は egress (送信側の SNAT) だった。
By default, AKS creates a Standard Load Balancer to be set up and used for egress.
しかもこの LB すら必須ではない。outboundType を NAT Gateway や userDefinedRouting に変えれば消せる。「VMSS があるから入口の LB が要る」という必然はなかった。
② type: LoadBalancer は LB の別の顔
type: LoadBalancer の Service を作ると、public でも internal (azure-load-balancer-internal アノテーション) でも、L4 の Azure LB ができる。ただしそれは dst = その LB の VIP 宛で、AGC (dst = Pod IP) とは宛先が別物。同じクラスタに並んでいても干渉しない。
③ AGC は overlay を「学習」していない
「どの Pod か」と「どう届くか」は別の係が担う。
- どの Pod か (コントロールプレーン):ALB Controller が k8s API の Service / EndpointSlice を watch し、生きた Pod IP のリストを AGC へ push する。
- どう届くか (ルーティング):Azure の SDN がもともと持っている。ノード作成時に Pod CIDR を
/24ずつ各ノードへ配り、routing domainを作る。AGC をデプロイすると、その routing domain を AGC の subnet へ 延伸 (join) する。AGC は SDN の知識を相続するだけで、自分でルートを学習しない。
ダメ押しに、overlay の外から Pod IP には普通は届かない、と公式も書いている。AGC が届けられるのは、subnet を routing domain に入れてもらう特権があるから。
④ TLS を終端するのは誰か
k8s には「TLS を終端する組み込み部品」はない。Service も kube-proxy も L4 で、TLS の中身は見ない。
- クライアント側の TLS は AGC が終端する。
- AGC ↔ Pod の区間は、デフォルトは平文 HTTP。End-to-End SSL を設定したときだけ AGC が Pod へ張り直し、終端するのは Pod の中のプロセス (アプリのコンテナ、またはサイドカーの proxy)。
⑤ CNI Overlay の Pod IP は VNet の外 (ここは一部、基盤構造からの推定)
Azure CNI Overlay では、Pod IP は VNet に存在しない別レンジ (顧客アドレス=CA)。基盤は VM 間通信と同じく、その CA を物理ホストのアドレス (PA) の外箱で包んで運ぶ。Node の VNet IP は、AGC → Pod のどのヘッダにも出てこない (外箱は PA、内側は Pod IP)。
なお、公式が明言しているのは「CNI レベルの encapsulation は不要」「routing domain が Azure networking stack に作られる」まで。外箱が VXLAN かどうか、PA の振り方といった基盤内部は非公開で、ここは Azure SDN (VL2 / VFP) の一般構造からの推定として持っておく。
たとえ話
宅配で考える。
- Pod IP = 届け先の番地 (10.10.2.2)。ただしこの番地は、別の市 (別クラスタ) にも同じものがある。
- routing domain ID (VXLAN なら VNI) = 「市名」。番地だけでは家が決まらないので、市名で空間を区切る。
- PA (物理ホストのアドレス) = 担当の配送センターの場所。外箱の宛先はこれ。
- Node 内の veth ルート = 配送センター内で「この棚」と仕分ける最後の一手。
つまり 10.10.2.2 単体では家は決まらない。「市名 (VNI) + 配送センター (PA) + 番地の担当ノード + ノード内の棚 (veth)」の 4 つが揃って、初めて唯一の Pod に着く。
ニュースを読むための変換表
| ドキュメントの言葉 | つまり何の話? |
|---|---|
| Application Gateway for Containers (AGC) | AKS 用の L7 ロードバランサー。Pod へ直接届ける。AGIC の後継 |
| ALB Controller | AKS の中で動く Pod。k8s を見て AGC (外の Azure リソース) を設定するコントロールプレーン |
| データプレーン / コントロールプレーン | 「運ぶ係」と「決める係」。機能でシステムを輪切りにした担当区分 |
| frontend / association | AGC の入口 FQDN / AGC のデータプレーンを VNet の委任 subnet に生やす接続点 |
| EndpointSlice | あるサービスの「今生きている Pod IP の名簿」 |
| routing domain | 独立したルーティングテーブル=アドレスの名前空間 (VRF の親戚) |
| Azure CNI Overlay | Pod に VNet 外の IP を配るモデル。基盤が routing domain でルーティング |
| outboundType | クラスタの egress 方式 (loadBalancer / NAT gateway / UDR / none) |
| End-to-End SSL | AGC が Pod へも HTTPS で張り直す設定。終端は Pod 内のプロセス |
- サイドカーが TLS を終端する仕組み (サービスメッシュ) ── End-to-End SSL の終端役になる Envoy などの proxy。AGC↔Pod 区間の TLS の続き。
- Gateway API の書き方 (GatewayClass / Gateway / HTTPRoute) ── 今回「コントロールプレーンが読む設定」と呼んだものの実体。実際に手で書くとコントロールプレーンの動きが見える。
- Workload Identity と OIDC ── ALB Controller が ARM を叩く権限をどう得るか。中 (k8s) の Pod が外 (Azure) を操作する認証の道。
- flat CNI と CNI Overlay の使い分け ── flat なら Pod が VNet の実 IP を持つので NVA / Firewall を AGC↔Pod 間に挟める。Overlay では挟めない。実務の判断軸。
- Azure SDN (VL2 / VFP) の CA → PA マッピング ── 今回「推定」とした基盤の中身。アンダーレイ側からこの routing domain を見直す。