Pod を作ると、その Pod に IP アドレスが付きます。 前の章で手作業したケーブルの接続やアドレスの設定を、起動のたびに誰かが行っています。
このクラスタでその仕事を担当するのは、Cilium というネットワーク用のプログラムです。 この章では、Pod の起動をきっかけに Cilium が呼ばれ、ネットワークを準備する流れを見ます。 その呼び出し方の取り決めを CNI と呼びます。
GOAL この章のゴール
- Pod の起動時にネットワークが準備される流れを説明できる
- ネットワークプラグインごとの接続方式の違いが分かる
- ノードでプラグインの設定と、Pod の経路を確認できる
CNI の 3 つの仕事
Pod に通信できる状態を用意するには、接続口を作り、IP アドレスを設定し、相手まで届く経路を用意します。 これらをネットワークプラグインなどが分担し、コンテナランタイムは CNI の取り決めでプラグインを呼び出します。 CNIシーエヌアイContainer Network Interface。コンテナにネットワークを設定するプラグインの呼び出し方 (環境変数、stdin の JSON、stdout の結果) を定めた仕様と、その仕様に従うプラグイン群。用語集で見る の仕様が決めているのは呼び出し方だけで、Kubernetes が求める仕事は次の 3 つに分かれます。
- ノード間の疎通を担保する:Node A の Pod から Node B の Pod へ直接届くように、VXLAN トンネル、BGP、クラウドの VPC ルートなどで経路を作る。
- Pod に NIC を差す:コンテナランタイムが作った netns に veth を差す。第 2 章で手でやった作業です。
- Pod の IP を配る (IPAM):Pod は頻繁に作られては消えるので、どの IP が使用中かを常駐プログラムが記録し、払い出しと回収をする。IP Address Management の略。
kubeadm init 直後の Node は NotReady で、CNI プラグインを入れると Ready になります。
kubeadm のドキュメントは「Pod 同士が通信するには CNI ベースの Pod network add-on が必要で、入れるまで control-plane node は NotReady のままになる」と書いています。
プラグインの実行モデル
CNI の仕様 (2026 年現在 1.1.0) は、ランタイムがプラグインをどう呼ぶかを次のように定めています。
- ランタイムは「プラグインを呼ぶ前に、コンテナ用の network namespace を作っておかなければならない」。
- 操作は環境変数
CNI_COMMANDで渡す。ADD(ネットワークに追加)、DEL(取り外し)、CHECK(期待どおりか検査)、GC(使われていない資源の掃除)、VERSIONに加え、プラグインの準備状態を聞くSTATUSがある。 - そのほかの環境変数は
CNI_CONTAINERID(コンテナの ID)、CNI_NETNS(netns のパス。たとえば/run/netns/[nsname])、CNI_IFNAME(コンテナ内に作る NIC の名前)、CNI_ARGS(FOO=BAR;ABC=123形式の追加引数)、CNI_PATH(プラグインの実行ファイルを探すパス)。 - 設定は JSON で stdin から渡し、成功したら結果を JSON で stdout に返す (
cniVersion、作ったinterfaces、割り当てたips、routes、dns)。失敗なら stderr にエラーを出し、終了コードは非 0。 - 設定ファイル (conflist) の
plugins配列に複数のプラグインを並べると、ランタイムは順に exec し、前のプラグインの結果をprevResultとして次に渡す (chaining)。DELは逆順。
仕様自体にはファイルの置き場所は書かれていません。
Kubernetes のドキュメントは既定の設定ディレクトリを /etc/cni/net.d、実行ファイルの置き場所を /opt/cni/bin としていて、containerd は設定ディレクトリのファイルを名前順に並べ、既定では最初の 1 つだけを読みます。
Pod が 1 つ起動するときの流れを、ステップごとに見ます。
kubelet: Pod default/web-7c9d が nodeName=このノード kubelet → containerd: RunPodSandbox(web-7c9d) over /run/containerd/containerd.sock
1/7kubelet が自分宛の Pod を見つけ、CRI の RunPodSandbox を containerd に送る
プラグインを exec するのは containerd です。kubelet は CRI で Pod の作成を頼み、結果の IP を containerd から受け取ります。
ノード間をどう繋ぐか
3 つの仕事のうち、CNI プラグインごとに最も性格が出るのが「ノード間の疎通」です。
第 3 章の例をもう一度使います。
Node 1 (10.0.1.11) の Pod 10.244.0.23 が、Node 2 (10.0.1.12) の Pod 10.244.1.5 へ送るとき、ノード間のスイッチやルーターは 10.244.1.5 がどこにあるかを知りません。
この宛先をどう届けるかの違いが、次の 3 方式です。
- オーバーレイ (VXLAN / Geneve):Pod のパケットをまるごと UDP パケット (VXLAN は 8472 番、Geneve は 6081 番) の中身にして、相手ノードの IP 宛てに送る。相手ノードが外箱を外して Pod に渡す。Cilium のドキュメントは「ノード同士が IP/UDP で届きさえすればよく、ノード間のネットワークは Pod CIDR を知らなくてよい」と利点を挙げ、代償として VXLAN で 1 パケットあたり 50 バイトの MTU の減少を挙げている。Flannel のドキュメントが推奨する方式であり、Cilium の既定 (
routing-mode: tunnel) でもある。この演習クラスタもこれです。 - BGP:各ノードが「10.244.1.0/24 は私のところ」と、BGP (ルーター同士が経路を教え合うプロトコル) でスイッチやルーターに伝える。Calico のドキュメントは、ノード同士が BGP で経路を交換する full-mesh を既定とし、オンプレでは ToR ルーターと直接 peer する構成を説明している。包まないので速いが、ノード間のネットワークが Pod CIDR をルーティングできる必要がある (Cilium の native routing の要件)。
- クラウドの VPC ルート:VPC のルートテーブルに「Pod CIDR → そのノード」を書く。Cilium のドキュメントは「ネットワーク上に全 Pod への経路を知るルーターがある」形としてクラウド連携を挙げている。経路を書く役は第 9 章で扱う cloud-controller-manager の Route controller。
もう 1 つ、Pod に VPC のアドレスそのものを配ってしまう方式があります。
Amazon VPC CNI は、EC2 ノードに付いた ENI のセカンダリ IP を Pod に渡します。
AWS のドキュメントは「Pod は VPC ネットワーク上と同じ IP アドレスを持てる」と説明し、ノード上の aws-node (ipamd) が ENI と空き IP をウォームプールとして先に確保し、足りなくなると ENI を追加する、と書いています。
Pod の IP は VPC のアドレスなので、包む必要も経路を教える必要もありません。
代わりに、インスタンスタイプごとの ENI 数と IP 数が Pod 数の上限になり、AWS のドキュメントは上限を (ENI 数 × (ENI あたりの IP 数 - 1)) + 2 としています (-1 は各 ENI の primary IP、+2 はホストのネットワークを使う kube-proxy と VPC CNI 自身の分)。
Pod が消えた IP は 30 秒の冷却期間を置いてから再利用され、その間に各ノードの kube-proxy が書き換え表の更新を終えます。
この方式が第 8 章の「コンテナネイティブ LB」の土台です。
代表的な CNI プラグイン
| 観点 | 方式 | 特徴 |
|---|---|---|
| Flannel | VXLAN (推奨) | ホストごとにサブネットを貸し出す L3 ネットワーク。NetworkPolicy は自前で強制せず、Calico などと組む |
| Calico | BGP / IPIP / VXLAN / eBPF | ノード間の BGP full-mesh が既定。ToR と peer する構成や、BGP が使えない環境向けの IPIP / VXLAN もある |
| Cilium | eBPF (VXLAN / Geneve / native) | kube-proxy の代替、NetworkPolicy、Gateway API まで一式。GKE Dataplane V2 の中身 |
| Amazon VPC CNI | ENI のセカンダリ IP | EKS の EC2 ノードで既定の CNI。Pod が VPC アドレスを持つ |
同じ OSS でも、クラウドで提供される範囲は違う
GKE Dataplane V2 は Cilium を使って Service を実装し、AKS には Azure CNI Powered by Cilium があります。 ただし、マネージドサービスの採用を、上流 Cilium の全機能を自由に使えるという意味に読むことはできません。 たとえば Google は、GKE Dataplane V2 と kube-proxy の間で Service の新機能の対応時期が異なることを明記しています。
EKS でもノードの種類によって違います。 AWS は Hybrid Nodes 向けに Cilium をサポートし、このノードでは Amazon VPC CNI を使えません。 EC2 ノードの既定と Hybrid Nodes の選択肢を混ぜずに確認します。
Calico も eBPF データプレーンを提供しています。 「Calico は BGP、Cilium は eBPF」とだけ分けると、経路を配る方法と、パケットを処理する方法が混ざります。 クラウドの対応範囲、IP の割り当て、経路の配布、Service と NetworkPolicy の実装を分けて比較すると、製品名だけでは見えない違いを確認できます。
ノードの中で読む
ここからはノードの中です。
kubectl debug node/$(kubectl get node -o jsonpath='{.items[0].metadata.name}') -it \
--profile=sysadmin --image=busybox:1.37 -- chroot /host nsenter -t 1 -m -u -i -n -p -- bash抜けるときは exit。デバッグ用 Pod は自動では消えないので、終わったら kubectl delete pod -l app.kubernetes.io/managed-by=kubectl-debug かkubectl get pods で確認して消してください。
まず、containerd が読む conflist と、プラグインの実行ファイルを確認します。
ls -la /etc/cni/net.d/ /opt/cni/bin/
cat /etc/cni/net.d/05-cilium.conflist出力例
/etc/cni/net.d/:
-rw-r--r-- 1 root root ... 05-cilium.conflist
/opt/cni/bin/:
-rwxr-xr-x 1 root root ... cilium-cni
-rwxr-xr-x 1 root root ... loopback
...
{
"cniVersion": "0.3.1",
"name": "cilium",
"plugins": [
{
"type": "cilium-cni",
"enable-debug": false,
"log-file": "/var/run/cilium/cilium-cni.log"
}
]
}plugins に cilium-cni が 1 つあるだけです。
IPAM の設定が書かれていないのは、cilium-cni が cilium-agent に IP を頼むからです。
ファイル名の 05- は、名前順で先に選ばれるようにするための接頭辞です。
次に、cilium-agent が報告する状態を見ます。
cilium コマンドは cilium-agent の Pod の中にあり、ノードのシェルからは kubectl が使えないので、ここは Web ターミナル側で打ちます。
kubectl -n kube-system exec ds/cilium -c cilium-agent -- cilium status | head -20
kubectl -n kube-system exec ds/cilium -c cilium-agent -- cilium status --verbose | grep -A3 "^IPAM"出力例
KVStore: Disabled
Kubernetes: Ok 1.37 (v1.37.1) [linux/amd64]
Kubernetes APIs: ["EndpointSliceOrEndpoints", "cilium/v2::CiliumNetworkPolicy", ...]
KubeProxyReplacement: True [eth0 ...]
Host firewall: Disabled
CNI Chaining: none
CNI Config file: successfully wrote CNI configuration file to /host/etc/cni/net.d/05-cilium.conflist
Cilium: Ok 1.20.2 (v1.20.2-...)
IPAM: IPv4: 9/254 allocated from 10.244.0.0/24
...
IPAM: IPv4: 9/254 allocated from 10.244.0.0/24
Allocated addresses:
10.244.0.23 (default/web-7c9d...)IPAM: … allocated from 10.244.0.0/24 は、このノードの Pod に払い出せる 254 個の IP のうち 9 個が使用中という意味です。
Cilium の IPAM には複数のモードがあり (既定は Cilium 自身が CIDR を管理する cluster-pool)、演習クラスタは ipam: kubernetes です。
Cilium のドキュメントによれば、このモードでは「Kubernetes が各 Node に割り当てた PodCIDR の範囲から IP を払い出す」ので、kube-controller-manager を --allocate-node-cidrs 付きで動かす必要があります。
kubectl get node -o jsonpath='{.items[0].spec.podCIDR}{"\n"}'最後に、ノード間の疎通を担う VXLAN デバイスと、Cilium が置いた経路です。 単一ノードなので相手は居ませんが、トンネルの口は開いています。
ip -d link show cilium_vxlan
ip addr show cilium_host
ip route | grep -E "10\.244|cilium"出力例
5: cilium_vxlan: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 ...
vxlan external id 0 ... dstport 8472 ...
6: cilium_host@cilium_net: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 ...
inet 10.244.0.87/32 scope global cilium_host
10.244.0.0/24 via 10.244.0.87 dev cilium_host proto kernel src 10.244.0.87 mtu 1450cilium_vxlan の dstport 8472 が VXLAN のポート、経路の mtu 1450 が 50 バイト分のカプセル化の跡です。
cilium_host の IP (ここでは 10.244.0.87) は、第 2 章で Pod のデフォルトルートの先に見えた IP です。
Cilium のドキュメントは「Pod の network namespace には、ノードの router IP を指すデフォルトルートがあり、その先は Pod 内で eth0、ホスト側で lxcXXXXXX と名付けられた veth ペア」と説明しています。
Pod から出たパケットは veth のホスト側 (lxc…) の tc ingress hook で eBPF プログラムに拾われ、同じノードの Pod ならそのまま相手の veth へ、別ノードの Pod なら cilium_vxlan で UDP に包んで相手ノードへ送られます。
ip link show type veth | grep -E "^[0-9]+: lxc" | head -5ノードから抜けたら、デバッグ用の Pod を消しておきます。
kubectl delete $(kubectl get pods -o name | grep node-debugger) --wait=falseふりかえり
QCNI プラグインを exec するのは誰?
kubelet は CRI で RunPodSandbox を頼むだけです。containerd が netns を作り、conflist を読み、環境変数と stdin の JSON 付きでプラグインを exec します。CNI の仕様も「ランタイムはプラグインを呼ぶ前に netns を作る」と定めています。
QVXLAN オーバーレイの利点として正しいものは?
Pod のパケットをノード IP 宛ての UDP で包むので、ノード間のネットワークは Pod CIDR の存在を知らなくて済みます。代償はヘッダ分 (VXLAN で 50 バイト) の MTU の減少です。
Qこの演習クラスタで Pod の IP はどの範囲から払い出される?
ipam: kubernetes モードでは、kube-controller-manager が –allocate-node-cidrs で各 Node に配った podCIDR を cilium-agent が使います。cilium status の IPAM 行と kubectl get node の spec.podCIDR が一致します。
参考
-
CNI 仕様 (SPEC v1.1.0: 操作、環境変数、stdin / stdout の JSON、chaining、ランタイムが netns を作る責務) https://www.cni.dev/docs/spec/
-
Kubernetes ドキュメント「Network Plugins」(ランタイムが CNI を読み込む、/etc/cni/net.d と /opt/cni/bin の既定、kubelet からの削除) https://kubernetes.io/docs/concepts/extend-kubernetes/compute-storage-net/network-plugins/
-
Kubernetes ドキュメント「Creating a cluster with kubeadm」(Pod network add-on を入れるまで NotReady) https://kubernetes.io/docs/setup/production-environment/tools/kubeadm/create-cluster-kubeadm/
-
containerd go-cni (設定ファイルを名前順に読む) https://github.com/containerd/go-cni/blob/main/opts.go
-
Cilium ドキュメント「Routing」(Encapsulation と Native Routing、VXLAN 8472 / Geneve 6081、50 バイト、Pod のデフォルトルートと lxc veth) https://docs.cilium.io/en/stable/network/concepts/routing/
-
Cilium ドキュメント「IPAM」「Kubernetes Host Scope」 https://docs.cilium.io/en/stable/network/concepts/ipam/kubernetes/
-
Cilium ドキュメント「eBPF Datapath」(tc ingress hook、veth、cilium_host / cilium_net / cilium_vxlan) https://docs.cilium.io/en/stable/network/ebpf/intro/
-
Cilium ドキュメント「System Requirements」 https://docs.cilium.io/en/stable/operations/system_requirements/
-
Amazon EKS Best Practices「Amazon VPC CNI」(ipamd、ウォームプール、Pod 数の上限、30 秒の冷却) https://docs.aws.amazon.com/eks/latest/best-practices/vpc-cni.html
-
Flannel README と backends (VXLAN 推奨、NetworkPolicy は自前で強制しない) https://github.com/flannel-io/flannel
-
Calico ドキュメント「Configure BGP peering」 https://docs.tigera.io/calico/latest/networking/configuring/bgp
-
GKE ドキュメント「GKE Dataplane V2」(Cilium で実装) https://cloud.google.com/kubernetes-engine/docs/concepts/dataplane-v2
-
CNCF「Cilium」 https://www.cncf.io/projects/cilium/
-
Microsoft「Azure CNI Powered by Cilium」: https://learn.microsoft.com/en-us/azure/aks/azure-cni-powered-by-cilium
-
Calico「Enabling the eBPF data plane」: https://docs.tigera.io/calico/latest/operations/ebpf/enabling-ebpf
-
AWS「Configure CNI for hybrid nodes」: https://docs.aws.amazon.com/eks/latest/userguide/hybrid-nodes-cni.html