パケットはどこで掴まれ、どこへ弾かれるのか
一言でいうと
Ciliumは、Kubernetesの意味論(Service、ラベル、ポリシー)をカーネル内のデータ構造へ直接下ろし込みます。そのため、Serviceが増えてもルックアップのコストは増えませんが、すべてが速くなるわけではありません。
なぜ必要なのか
kube-proxyのiptablesモードでは、Serviceへのリクエスト1つが、KUBE-SERVICESチェーンを走査し、KUBE-SVC-*チェーンで確率分岐を経て、KUBE-SEP-*チェーンでDNATされます。ルールの数はServiceの数とバックエンドの数に比例して増え、カーネルはパケットのたびにそのリストをほぼ順番に通り抜けます。
さらにつらいのは更新のコストです。iptablesは、ルールを1つ変えるときも事実上テーブル全体を書き直します。 デプロイが頻繁なクラスターでは、これがCPUのスパイクとルール反映の遅延として現れます。Serviceが数千あるような環境で「Podは起動したのに、まだトラフィックが来ない」という数秒の空白が生じるのは、ここが原因です。
eBPFは、この2つの軸をどちらも定数時間に変えます。ルックアップはハッシュマップの参照なのでServiceの数と無関係ですし、更新はマップのエントリ単位の増分なので、ほかのエントリには触れません。
どう動くのか
外部からNodePortで入ってきたパケットの道のりです。
NIC 수신
-> [XDP 훅] 드라이버 수준. sk_buff 할당 전이라 가장 빠름
-> [tc ingress 훅] 서비스 변환(lb4_services -> lb4_backends), conntrack,
ipcache 룩업으로 목적지 아이덴티티 확인
-> bpf_redirect_peer 호스트 veth 에서 파드 네임스페이스 안 피어로 직행
-> [파드 lxc 인터페이스] 정책 평가: (소스 아이덴티티, 포트, 프로토콜, 방향)
-> 애플리케이션 소켓
押さえるべき点は3つあります。1つ目に、XDPはメモリ割り当ての前の段階なので、DDoSのフィルタリングやNodePortの高速化に使われますが、対応するNICドライバーが必要です。2つ目に、データパスの本体はtcフックで、Serviceの変換・conntrack・ポリシー評価がすべてここで行われます。3つ目に、bpf_redirect_peerは、vethペアを通過するときに発生するソフトIRQの再スケジューリング1サイクルを飛ばして、ネームスペースの境界を一度に越えます。
ポリシーはIPではなくアイデンティティで評価されます。Podのラベルの集合に数字が1つ対応し、その数字がカーネルマップのキーになります。試験にそのまま出る予約アイデンティティは、覚えておいたほうがよいでしょう。
| 番号 | 名前 | 意味 |
|---|---|---|
| 0 | unknown | 不明な送信元 |
| 1 | host | ローカルノード自身 |
| 2 | world | クラスター外部のすべて |
| 3 | unmanaged | Ciliumが管理していないエンドポイント |
| 4 | health | ヘルスチェックのエンドポイント |
| 5 | init | 初期化中のエンドポイント |
| 6 | remote-node | ほかのノード |
| 7 | kube-apiserver | APIサーバー |
| 8 | ingress | Ingress |
役割分担も整理しておきます。Cilium AgentはDaemonSetとしてノードごとに起動し、eBPFプログラムのロード、エンドポイントの管理、ポリシーの適用、conntrackを担当します。Cilium Operatorはクラスターに1つ起動し、クラスター範囲のIPAM、CRDのガベージコレクション、ノードディスカバリーを担当します。Agentはノードのデータパスを直接操作するのでDaemonSetである必要があり、Operatorはクラスター全体の状態を扱うので1つで足ります。
最後に、正直な代償も挙げておきます。eBPFに切り替えればすべてが速くなるという話は、事実ではありません。
- L7ポリシーがかかったトラフィックは、カーネルを出てユーザースペースのEnvoyを経由します。レイテンシが加わります。
- トンネルモード(VXLAN/Geneve)では、パケットごとに約50バイトのカプセル化オーバーヘッドが加わり、MTUが小さくなります。
- eBPFプログラムは、カーネルにロードされる前に検証器を通過する必要があります。終了が保証されないループや不正なメモリアクセスは拒否され、命令数の制限も、プログラムが必ず終わるように強制するための安全装置です。
「どの機能を有効にすると、どんなコストがかかるのか」を知っていることが、運用の核心です。
現場での姿
筆者は、7ノードのホームラボ(cp-1/2/3 + gpu-a/b/c/d)を、Kubernetes v1.34.10、containerd 1.7.27、カーネル6.14、Cilium 1.20.1で運用しています。このクラスターはkubeadm init --skip-phases=addon/kube-proxyで、kube-proxyを最初からインストールせずに構築しました。インストール後に削除するのとは違います。一度でも動いたkube-proxyは、ノードにKUBE-SERVICES / KUBE-SVC-* / KUBE-SEP-*チェーンを刻み込み、DaemonSetを削除してもそのルールが残って、eBPFデータパスと衝突します。
主張ではなく、計測で確認した結果です。
kube-proxy 파드 수: 0
노드의 iptables KUBE- 체인 개수: 0
そして、Serviceはカーネルマップの中に次のように入っていました。
SERVICE ADDRESS BACKEND ADDRESS (REVNAT_ID) (SLOT)
10.96.0.10:53/TCP (1) 10.244.0.150:53/TCP (4) (1)
10.96.0.1:443/TCP (1) 10.0.0.120:6443/TCP (1) (1)
cilium statusはKubeProxyReplacement: TrueとModules Health: OK 92 / Degraded 0を報告し、ノードの状態・コントロールプレーン・CNI・CoreDNS・スケジューリング・Service DNSを含む基本検証の14項目が14/14で合格しました。これらの数字が重要な理由は単純です。eBPFデータパスが動いている証拠は、ドキュメントではなく、チェーンの数とマップのダンプだからです。
次のクイズで確認すること
このモジュールは概念だけを扱います。次のモジュールでアイデンティティモデルとkube-proxyの置き換えを扱いながら、上で見た値をHelm valuesファイルに自分で書き、その状態を検証するスクリプトまで作ります。