kube-proxyのないクラスタを宣言する
目標
kube-proxyを置き換えるCiliumの設定をvaluesファイルで正確に宣言し、実際のワークロードとServiceでバックエンドが認識される過程を確認し、「本当にkube-proxyがないのか」を自分で証明するスクリプトを作ります。
なぜ重要なのか
kubeProxyReplacement: trueの1行で終わりそうに見えますが、現場ではそうはいきません。k8sServiceHostを抜くと、Ciliumが起動中にAPIサーバーを見つけられず、クラスター全体が立ち上がりません。また、既存のクラスターでkube-proxyを削除しながら切り替えると、ノードに残ったKUBE-チェーンがeBPFの経路と衝突します。そのため、実務の正攻法は最初からインストールしないことであり、それが実際に守られているかは、チェーンの数を数えて確認する必要があります。
このラボでは、その過程を3つに分けて身に付けます。valuesファイルの作成は「何を有効にするのか」を、実際のリソースのapplyは「ラベルとセレクターがどのようにバックエンドを作るのか」を、検証スクリプトは「どのように証明するのか」を扱います。3つ目が特に重要です。ドキュメントに書かれた機能と、このノードで実際に動作していることは、別の命題だからです。
Cilium Helm valuesとスクリプトは/root/cca-config/の下に作成し、Kubernetesの基本リソースは実際にapplyします。
ステップ
- ネームスペース
cca-netを作成してください。 /root/cca-config/cilium-values.yamlを作成し、最上位にkubeProxyReplacement: true、k8sServiceHost: 10.0.0.120、k8sServicePort: 6443、そしてipam.mode: kubernetesを書いてください。- 同じファイルに続けて、
routingMode: tunnel、tunnelProtocol: vxlan、bpf.masquerade: true、l7Proxy: true、encryption.enabled: true、encryption.type: wireguardを追加してください。 - ノード
lab-node-0とlab-node-1にラベルcca.homelab/tier=gpuを、ノードlab-node-2にラベルcca.homelab/tier=cpuを付けてください。 - ネームスペース
cca-netにDeploymentwebをレプリカ3でデプロイしてください。Podのラベルはapp=web、イメージはnginx:1.27-alpine、PodスペックのnodeSelectorはcca.homelab/tier: gpuです。 - 同じネームスペースにService
webを作成してください。タイプはClusterIP、portは80、targetPortは8080、selectorはapp=webです。 /root/cca-config/verify-kubeproxy-free.shを作成し、実行権限を付与してください。このスクリプトは、(1)kubectlでkube-systemのkube-proxy Podの数を数え、(2)iptables-saveの出力からKUBE-チェーンの数を数え、(3)ciliumの状態でKubeProxyReplacementの値を確認する必要があります。数が0でなければ、メッセージを出力してexit 1で終了させてください。
参考
- valuesファイルの入れ子は、
ipam.modeのようにドットで書くのではなく、実際のYAMLの階層で書いてください。ipam:の下にmode: kubernetesと書きます。 - ラベルのキーにスラッシュが含まれていても、
kubectl label node lab-node-0 cca.homelab/tier=gpuのようにそのまま書けば問題ありません。 - PodがPendingの場合は、
kubectl describe podのイベントでnodeSelectorの不一致を確認してください。 - よくある間違い1: Serviceの
targetPortを抜かして、portと同じ値にしてしまうこと。コンテナがリッスンするポートとServiceのポートは別物です。 - よくある間違い2: 検証スクリプトが数を出力するだけで、判定をしないこと。0と比較して失敗にしてこそ検証です。
ラボ用のネームスペースを作成する
ネームスペースcca-netを作成してください。
最も単純な最初のステップです。名前だけを正確に合わせてください。
kube-proxy置き換えの中核となる値を書く
/root/cca-config/cilium-values.yamlを作成し、最上位にkubeProxyReplacement: true、k8sServiceHost: 10.0.0.120、k8sServicePort: 6443、そしてipam.mode: kubernetesを書いてください。
kube-proxyがなければ、APIサーバーのVIPを解決する主体はCilium自身ですが、ブートストラップの時点ではまだ起動していません。そのため、実際のアドレスを別途伝える必要があります。IPAMは、ノードのPodCIDRをそのまま使うモードを選んでください。
データパスのオプションを埋める
同じファイルに続けて、routingMode: tunnel、tunnelProtocol: vxlan、bpf.masquerade: true、l7Proxy: true、encryption.enabled: true、encryption.type: wireguardを追加してください。
同じファイルに続けて書きます。アンダーレイがPodCIDRの経路を知らないホームラボなのでカプセル化が必要で、SNATもiptablesではなくeBPFに任せる必要があります。HTTPメソッド単位のポリシーを使うには、有効にしなければならないスイッチがもう1つあります。
ノードに性能区分のラベルを付ける
ノードlab-node-0とlab-node-1にラベルcca.homelab/tier=gpuを、ノードlab-node-2にラベルcca.homelab/tier=cpuを付けてください。
Kubernetesから見ると、すべてのGPUはただのGPU 1個です。意味のある配置をするには、人間がラベルで意味を埋め込む必要があります。ラベルのキーにスラッシュが含まれている点に注意してください。
配置制約を付けたワークロードをデプロイする
ネームスペースcca-netにDeployment webをレプリカ3でデプロイしてください。Podのラベルはapp=web、イメージはnginx:1.27-alpine、PodスペックのnodeSelectorはcca.homelab/tier: gpuです。
PodテンプレートにnodeSelectorを入れます。前のステップで付けたラベルのキーと値が正確に同じである必要があり、食い違うとPodはPendingのままになります。
Serviceでまとめてバックエンドを確認する
同じネームスペースにService webを作成してください。タイプはClusterIP、portは80、targetPortは8080、selectorはapp=webです。
Serviceのポートとコンテナのポートは異なることがあります。selectorがPodのラベルと食い違うと、Serviceは作成されますが、バックエンドが0個になります。この状態が、kube-proxyでもeBPFでもトラフィックが届かない最も一般的な原因です。
検証スクリプトを作成する
/root/cca-config/verify-kubeproxy-free.shを作成し、実行権限を付与してください。このスクリプトは、(1) kubectlでkube-systemのkube-proxy Podの数を数え、(2) iptables-saveの出力からKUBE-チェーンの数を数え、(3) ciliumの状態でKubeProxyReplacementの値を確認する必要があります。数が0でなければ、メッセージを出力してexit 1で終了させてください。
主張ではなく、計測でなければなりません。kube-proxy Podの数とノードのKUBE-チェーンの数を数えて0であることを確認し、Cilium側の状態もあわせて確認する必要があります。失敗したら0以外の終了コードを返し、ファイルに実行権限も付与してください。