kubeadm × ovn-kubernetesのクラスタでService CIDR問題にハマった

Table of Contents

はじめに

今回は、自宅サーバーで構築している kubeadm × ovn-kubernetes のKubernetesクラスタで、kubevirtを導入しようとした際に遭遇したService CIDRに関する問題とその解決方法についてお話しします!

kubeadm × ovn-kubernetesを構築した話はこちらの記事で紹介しているので、興味があればこちらもぜひ読んでみてください。

問題の発見

ovn-kubernetesで構築したKubernetesクラスタにkubevirtを導入しようとしたところ、virt-operatorで以下のようなエラーが出始めました。

Internal error occurred: failed calling webhook "virtualmachineclusterinstancetype-validator.instancetype.kubevirt.io": failed to call webhook: Post "https://virt-api.kubevirt.svc:443/virtualmachineclusterinstancetypes-validate?timeout=10s": context deadline exceeded

どうやらvirt-operatorがVirtualMachineClusterInstanceTypeリソースを作成しようとして、validation webhookがタイムアウトか何かで失敗しているようです。 validation webhookを叩くのはkube-apiserverだと思ったので、kube-apiserver側のログも確認してみました。

kube-apiserver-home-k8s-master01 kube-apiserver W0902 13:48:03.160803 1 dispatcher.go:217] Failed calling webhook, failing closed virtualmachineclusterinstancetype-validator.instancetype.kubevirt.io: failed calling webhook "virtualmachineclusterinstancetype-validator.instancetype.kubevirt.io": failed to call webhook: Post "https://virt-api.kubevirt.svc:443/virtualmachineclusterinstancetypes-validate?timeout=10s": context deadline exceeded

やはりリソース作成のリクエストを受けたkube-apiserver側でvalidation webhookを叩くのに失敗していそうでした。

原因調査と解決方法

通信テストと原因の絞り込み

普通にvirt-apiのServiceのドメインにアクセスしているように見えるのに、なぜアクセスできないのでしょうか?

まずはkube-apiserverと同じkube-system namespaceにPodを作成してアクセステストをしてみました。

# curl -vk https://kubevirt-operator-webhook.kubevirt.svc:443/kubevirt-validate-update
...
< HTTP/1.1 400 Bad Request
< Date: Tue, 02 Sep 2025 13:51:54 GMT
< Content-Length: 0
<
* Connection #0 to host kubevirt-operator-webhook.kubevirt.svc left intact

400ですが普通にアクセスできていそうです。kube-apiserver Podと何が違うんでしょうか?

改めてkube-apiserver Podのspecを確認するとhostNetwork: trueになっていました。これは怪しそうです。

試しに hostNetwork: true のPodを作成して再度試してみると、案の定アクセスできませんでした。

root@home-k8s-worker02:/# getent hosts virt-api.kubevirt.svc
10.108.245.218  virt-api.kubevirt.svc.cluster.local

root@home-k8s-worker02:/# curl --connect-timeout 3 https://virt-api.kubevirt.svc:443/virtualmachineclusterinstancetypes-validate
curl: (28) Failed to connect to virt-api.kubevirt.svc port 443 after 3002 ms: Timeout was reached

アドレスは引けているのにアクセスできません。

ルーティングテーブルの調査

hostNetwork: true がダメだということはNodeのネットワーク設定が悪そうなので、Nodeのルーティングテーブルを確認してみました。

ubuntu@home-k8s-master01:~$ ip r
default via 192.168.1.1 dev breth0 proto static
10.96.0.0/16 via 169.254.0.4 dev breth0 src 169.254.0.2 mtu 1400
10.244.0.0/24 dev ovn-k8s-mp0 proto kernel scope link src 10.244.0.2
10.244.0.0/16 via 10.244.0.1 dev ovn-k8s-mp0
169.254.0.0/17 dev breth0 proto kernel scope link src 169.254.0.2
169.254.0.1 dev breth0 src 192.168.1.101
169.254.0.3 via 10.244.0.1 dev ovn-k8s-mp0
192.168.1.0/24 dev breth0 proto kernel scope link src 192.168.1.101

一見普通ですが、よくよく見てみると怪しいところがありました。

10.96.0.0/16 via 169.254.0.4 dev breth0 src 169.254.0.2 mtu 1400
10.244.0.0/24 dev ovn-k8s-mp0 proto kernel scope link src 10.244.0.2
10.244.0.0/16 via 10.244.0.1 dev ovn-k8s-mp0

これらはそれぞれService network、Pod network(Node/クラスタ全体)宛てのルートっぽいですが、Service networkのCIDRが 10.96.0.0/16 になっています。

確かkubeadmのデフォルトService networkは 10.96.0.0/12 だったような気がしますが…

根本原因の特定

実際にServiceのアドレスを確認してみると:

$ k get svc -n kubevirt -owide
NAME                          TYPE        CLUSTER-IP       EXTERNAL-IP   PORT(S)   AGE    SELECTOR
kubevirt-operator-webhook     ClusterIP   10.111.35.104    <none>        443/TCP   3d1h   kubevirt.io=virt-operator
kubevirt-prometheus-metrics   ClusterIP   None             <none>        443/TCP   3d1h   prometheus.kubevirt.io=true
virt-api                      ClusterIP   10.108.245.218   <none>        443/TCP   3d1h   kubevirt.io=virt-api
virt-exportproxy              ClusterIP   10.106.110.67    <none>        443/TCP   3d1h   kubevirt.io=virt-exportproxy

やっぱりServiceのアドレスが 10.96.0.0/16 の範囲に収まっていないようです。

kubeadmの公式ドキュメントを確認すると:

–service-cidr string Default: “10.96.0.0/12”

Use alternative range of IP address for service VIPs.

やっぱりそうでした! 宛先のvirt-api Serviceのアドレスが 10.108.245.218 で、10.96.0.0/16 のルートにマッチしないために通信できていないようです。

解決方法

kubeadmのデフォルトService CIDRが 10.96.0.0/12 なのに対し、ovn-kubernetesが 10.96.0.0/16 でルートを設定していたことが原因でした。 そこで、kubeadm init 時にクラスタのService CIDRを明示的に 10.96.0.0/16 に指定することにしました。

kubeadm init \
...
--pod-network-cidr 10.244.0.0/16
--service-cidr 10.96.0.0/16

解決後の確認

修正後、Serviceのアドレスが指定したCIDRに入っていることを確認しました:

$ k get svc -n kubevirt -owide
NAME                          TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)   AGE   SELECTOR
kubevirt-operator-webhook     ClusterIP   10.96.224.153   <none>        443/TCP   72s   kubevirt.io=virt-operator
kubevirt-prometheus-metrics   ClusterIP   None            <none>        443/TCP   72s   prometheus.kubevirt.io=true
virt-api                      ClusterIP   10.96.195.9     <none>        443/TCP   72s   kubevirt.io=virt-api
virt-exportproxy              ClusterIP   10.96.139.38    <none>        443/TCP   72s   kubevirt.io=virt-exportproxy

そして無事にvalidation webhookを叩けるようになり、kubevirtのデプロイができました!

$ kubectl get kubevirt.kubevirt.io/kubevirt -n kubevirt -o=jsonpath="{.status.phase}"
Deployed

まとめ

今回の問題は、kubeadmとovn-kubernetesのデフォルト設定の微妙な不整合が原因でした。 あんまりkubeadmでovn-kubernetesを使ってる人いないんですかね…?

マイナーなアドオンやプラグインを使う場合は、こういう変なところでつまづくことがありますが、それがまたトラブルシューティングの面白さでもありますね! kubernetes本体(kube-apiserverなど)やネットワークアドオンの設定について理解が深まってよかったです。

最後まで読んでいただき、ありがとうございました。