K8s on vSphereでPodを好きなネットワークにつなぎたい

- Multus CNIを使うことでPodに複数のインターフェイスをつけれる
- あるいはCiliumでもMulti-homingについてのproposalがある https://github.com/cilium/cilium/issues/20129
- 追加インターフェイスに対するファイアウォールなどをどうするかという問題はまた考えないといけない
- 好きなネットワークにつなぐには、当然そこにnodeがそもそもつながっている必要はある
- 2つ方式が考えられそう
- node用のVMに対しtrunk portにつないだvNICを追加で接続しておく方式
- node上でVLANタグを見てsubinterfaceに割り振るようなことをすればよい
- 標準であるCNIプラグイン
bridge
の VLAN フィルタでも行けるはず - 802.1QのVLANが大前提になってしまうというのはある
- node用のVMに対し必要なポートグループを適宜つなぎに行く方式
- (Cluster APIを使っている前提で)Cluster Autoscalerを使って必要に応じてnodeを追加すればいいのではないか
- CPUリソースなどだけではなく、Podがスケジュールできるかどうかで判断してくれるらしい? https://github.com/kubernetes/autoscaler/blob/4aee897330aa1f5f222bccaa6924ca5bf603357f/cluster-autoscaler/FAQ.md#how-does-scale-up-work
- 接続ネットワークを示すNode labelを準備しておき、PodにはnodeSelectorをつけ、node group (?) を接続先の候補となるネットワーク分用意しておけば、なんとかなる?
- 冗長構成とかのためにPodたくさん配置とかする場合に大変そう…
- 単にnodeにまたがってreplica配置できればいいならいいけど、nodeの乗ってるESXiホストをtopologyKeyにして…とか考えると、事前にESXiホストが何になるかわからないので、autoscaler的には判断がつかなさそう
- CNI的にはtrunk port方式同様
bridge
プラグインとかでなんとかなると思われる
- (Cluster APIを使っている前提で)Cluster Autoscalerを使って必要に応じてnodeを追加すればいいのではないか
- node用のVMに対しtrunk portにつないだvNICを追加で接続しておく方式

node用のVMに対し必要なポートグループを適宜つなぎに行く方式
Whereabouts のようなIPAMを使えば、Podへのアドレスも良い感じに割当てできそうだった。
NetworkPolicy的な部分はなんともならない(CNIとして対応が必要になるところ)ので、firewall pluginなどを参考にプラグインを作るしかない?(ルールをある程度柔軟に受けられるようにするなら、PodのAnnotationや何らかのルールでConfigMapを取ってきて、それに従ったiptablesルールやBPFプログラムを差し込むようにする感じになる?)

cluster autoscaler (CAPI)試してみている。が、うまく行かない。
I0429 17:32:38.643359 1 pre_filtering_processor.go:57] Node ca-test-md-0-5d9cf48855-cq9zk should not be processed by cluster autoscaler (no node group config)
のようなログが出ている。
autodiscovery周りの設定を全部外しても変わらないので、 https://github.com/kubernetes/autoscaler/blob/f87dbe5fc65ae6a6c550b0c22d7910ec192e7cfb/cluster-autoscaler/cloudprovider/clusterapi/clusterapi_controller.go#L232 あたりでちゃんとMachineDeploymentまで辿れなかったのではないかと疑っている

エラーメッセージがわかりにくかったが、MachineDeploymentはちゃんと特定できていて、 https://github.com/kubernetes/autoscaler/blob/f87dbe5fc65ae6a6c550b0c22d7910ec192e7cfb/cluster-autoscaler/cloudprovider/clusterapi/clusterapi_nodegroup.go#L358 で引っかかっているだけだった。ちゃんと annotation (cluster.x-k8s.io/cluster-api-autoscaler-node-group-min-size
cluster.x-k8s.io/cluster-api-autoscaler-node-group-max-size
) をMachineDeploymentにつけたら no node group config
のログは消えた。

一方で、もともとやりたかったことを実現するにはreplicas=0のMachineDeploymentなどを用意してnodeを追加させたい(= scale from zero )わけだが、 これがサポートされるかどうかはprovider次第らしく、CAPVがどうかはわからなかった (issueなどを見る限り見当たらない)

https://github.com/kubernetes-sigs/cluster-api/blob/main/docs/proposals/20210310-opt-in-autoscaling-from-zero.md#implementation-detailsnotesconstraints を見るに、Infrastructure Machine Template (CAPVの場合VsphereMachineTemplate?) のstatusをprovider側でセットするか、MachineDeploymentとかのannotationをユーザー側でセットするかの2パターンがあるらしい。statusはセットされてないのでCAPVはおそらくscale from zeroをサポートしてないようだが、annotationの方法なら行けるはず…
だが、試しに設定してみた限りではうまく行かない。ログを見る限り使ってほしいMachineDeploymentに関するメッセージが何一つとして存在しない。

scale from zeroは https://github.com/kubernetes/autoscaler/commit/1a65fde5402eb0a71d0334b40e1cc226c47e33c4 のあたりで入ってきていて、 cluster-autoscalerの1.25以降ぐらいじゃないと多分使えない。実験が1.23でやっていたのでダメな可能性があるため、1.25.1にしてみる

helm chart都合で1.26.2で試すことになったが、やはり依然としてうまく行かない

annotationでのリソース量の指定が悪かった。以下とかでパースされるので、そこでパース失敗しないようにしないといけない。
(cluster-autoscalerを自前でビルドしてk8sクラスタ上に持っていくのも面倒だったが、配布されてるバイナリはストリップされていたのでデバッグがダルかった。GoReSymとかで処理が入っているか気になる関数のアドレスを特定→gdbをpodが動作するnodeで動かしてattachして問題箇所の候補にbreakpointを仕込んで通過しているか確認→怪しいところはdisassembleしてより細かく処理をみたい場所のアドレスを特定して、breakpointを設置…とやっていって、結局nodegroup内で作成されるnodeが提供するメモリ量を指定するannotationのパース処理でエラーになっているとわかった。パース失敗とかのエラーをcluster-autoscalerがちゃんとログに出してくれないのでわかりにくいというのもある…)

これでreplicas 0なMachineDeploymentをNode Groupとして認識してくれたっぽいが、nodeSelectorでnodeのlabelを指定しているUnschedulableなPod(条件を満たすNodeは存在しないが、追加したreplicas 0のMachineDeploymentがデプロイするnodeはその条件を満たすし、MachineDeploymentには capacity.cluster-autoscaler.kubernetes.io/labels
のannotationを付けそれを指定している) に対して、対応しているNode Groupを見つけられないようで、
I0506 03:59:43.950589 1 scale_up.go:93] Pod testpod-769f847f9f-lknq9 can't be scheduled on MachineDeployment/ca-test/ca-test-md-1, predicate checking error: node(s) didn't match Pod's node affinity/selector; predicateName=NodeAffinity; reasons: node(s) didn't match Pod's node affinity/selector; debugInfo=
のようなログが出ている

MachineDeploymentへのannotationでnodeに付与するlabelを指定する(その値を用いてnodeSelector指定のあるPodをスケジュールできるかシミュレートする)機能は、 1.27.1 からの様子。
Helm chartとして対応するものはなかったが、 values.yaml 内の image.tag
で1.27.1を指定して試したところ、ちゃんとMachineDeploymentのreplicasが書き換えられた!

ただ、作成されたnodeにちゃんと期待するlabelがついていない。
のlabel同期に期待していたがちゃんと機能していない?

label syncはCAPI 1.4以降で、1.3.3というやや古いののままだったので1.4.2に上げたら動くようになった。
ちゃんとnodeSelectorによってスケジュールできなかったPodが動かせるようになった