この記事では、Kubernetes の CNI である Cilium について取り上げていますが、Linux Bridge 越しの環境であれば、その他のソフトウェアでも発生しうるトラブルでした 。
もし同じような症状でお困りの方がいらっしゃれば、ぜひ一度読んでみてください!
忙しい人のための結論
Proxmoxなどのブリッジ越しに通信をしていたのがダメでした。
multicast snoopingをオフにしましょう。
# 一時的にオフにする
echo 0 > /sys/class/net/vmbr0/bridge/multicast_snooping
# 永続的にオフにする
echo "net.bridge.bridge-br0.multicast_snooping=0" | sudo tee /etc/sysctl.d/bridge_no_snoop.conf
ことの始まり
こちらのQiitaの記事で、Cilium の L2 Announcement を試しました。
先の記事を書いたときから起こっていた問題だったのですが、 Proxmox VM 上に Kubernetes の Node を立てると NDP がドロップする という事象が発生していました。
先の記事はローカルでも検証しており、ProxmoxVE でのみ発生している問題であると考え、公開に踏み切ったのですが、本番環境では IPv4 でのみ運用されてきました。
検証結果
ICMP6 や NDP パケットを監視するなどして、様々な検証を行った結果、どうやら Linux Bridge が悪い ということがわかりました。
まさかの、Proxmox VE のネットワークが NDP のパケットをドロップしていたのです...
NDP の仕組み
NDP というのは、まず全体に 対象の要請ノードマルチキャストアドレス宛に Neighbor Solicitation を送信します 。
この要請ノードマルチキャストアドレスとは、NDP で「この IP 持っているホスト誰やねん」と呼びかけるためのマルチキャストアドレス(グループに入っているホスト全体に送信するためのアドレス)です。
例えば、2001:1:1:1::1111:1111 (サーバ A としましょう)にアクセスしたい場合、ff02::1:FF00: に右端24ビットをつけた ff02::1:ff11:1111 宛に「この IP 持ってるホスト誰やねん」と聞きに行きます。
そうすると、2001:1:1:1::1111:1111 のサーバー A が「俺だよ!MAC アドレスはこれだよ!」と応答するわけですね。
これで通信ができますね。
さて、さっきの例では、ff02::1:ff11:1111 を例にしていましたね。これは、IP アドレスの形式です。
つまり、このアドレスに対応した MAC アドレス 33:33:FF:11:11:11 も存在する のです。
(ここでは、MAC アドレスを導出する方法は割愛します。)
今回使用した Cilium の L2 Announcement は、eBPF を用いて L2 でこれを受信する ように設定しています。
これが落とし穴でした。
MLD Snooping
今回の本命はここです。
多くのマルチキャストルーターやそのような機能を持つブリッジなどには、MLD Snooping という機能がついています。
MLD Snoopingとは、端的にいうと、必要なホストにだけマルチキャストの通信を割り振るというものです。
NDP の仕組みでは、ルーターがマルチキャストアドレスのグループを管理しているのではなく、ホストがそのパケットをドロップするかどうかの取捨選択をしている ので、ルーターは無差別にすべてのホストにパケットを送信します。
このままでは、すべてのホストにパケットが送信されるので、帯域幅を占領してしまうことになります。あまり望ましい状況ではないどころか、DOS の温床になる可能性すらあります。
そこで登場するのが MLD Snooping です。
この機能は、どのホストがどのマルチキャストグループに参加しているかを ICMPv6 のメッセージタイプ (Multicast Listener Query) を用いて管理しています。
最終的な原因
根本的な原因としては、Cilium は L2 で受信するための設定をホスト側で行っていたのみであり、Proxmox VE の Linux Bridge の MLD の Multicast Listener Query を送信していなかった のです。
このため、Proxmox VE の Linux Bridge は Kubernetes の Node が該当のマルチキャストグループに参加していないとして、Neighbor Solicitation を転送しなかったのです。
まとめと対応策
Proxmox VE などのブリッジ越しに通信をしていたのがダメでした。
MLD Snooping を無効化しましょう。
# 一時的にオフにする
echo 0 > /sys/class/net/vmbr0/bridge/multicast_snooping
# 永続的にオフにする
echo "net.bridge.bridge-br0.multicast_snooping=0" | sudo tee /etc/sysctl.d/bridge_no_snoop.conf
ただし、これでは先述した通り、通信量が多すぎるので、やはり L2 で固定した場合でも MLD の Multicast Listener Query を送信するようにコードを記述する必要があります。
今回のケースでは、Cilium に Issue を提出したところ、翌々日にはメンテナーの方が PR を立ててくださっていました。
修正される日も近いのではないでしょうか。
Proxmox VE 上で Kubernetes を利用されている方も多いので、誰かのトラブルシューティングの支えになれば幸いです。
参考文献
最後とはなりますが、この記事は下記文献を参考にして記述しました。
ありがとうございました。
この記事のように、当サークルでは、積極的にコンテナ技術を採用しています。
これを読んで「さらに学んでみたい」、「K8s も気になる!」という方、当サークルに参加してみませんか?
当サークルは初心者から上級者まで老若男女誰でも参加できるサークルです。
また、サーバーリソース等の無料支援も行っております!
まずは Discord サーバーへ参加してみてください!
みなさんの参加をお待ちしております!
▶ 公式サイト
▶ Discord サーバー