構成
手元環境では NanoPi R5S(LANが3ポートあるシングルボードコンピュータ)にUbuntuを入れてVPN装置として使っています。LANポートが3つあるのでデスクにあるPCを接続するスイッチングハブ代わりにも使っています。
ルーター ---+--- NanoPi R5S -------- デスクトップPC
|
+------- PC-B
|
+------- PC-C
|
+------- PC-D
|
なにをしていたかというと。。。
現在はBSP(メーカーが用意した)カーネル、バージョンは5.10を使っていいます。この機種への対応がメインラインカーネルに入ったので、kernel6.6を試していると少し速くなっている気がします。そこで更新前の状態を記録しておこうとDockerを起動するとデスクトップPCがIPv6で通信できなくなっていることに気づきました。
普段はDocker(コンテナは起動せずデーモンのみ)が起動しているだけでネットワークが5%程遅くなるので停止していましたが、kernel6.6ならこの落ち込みがなくなるかもと期待しての計測でした。。。
なにがおこった?
デスクトップPCがIPv6経路を見失っています。ルーターを問い合わせるパケットに対して返事がありません。この現象はマルチキャストがブロックされています。再起動すると通信可能になり、Dockerを起動すると通信不可です。
違いを見ていくと、どうやらDocker起動時にブリッジフィルター(br_netfilter)モジュールがロードされるようです。当初はiptablesにルールを書いたり、sysctlでカーネルパラメタを書き換えてマルチキャストパケットをブロックしないようにしていましたが、現在の設定では systemd-networkd に以下の設定を書いて許可しています。
[Bridge]
MulticastSnooping=false
VLANFiltering=false
この設定を書くと、br_netfilterがロードされないのでルーター要求トラフィックがブロックされることはありません。今思うとDockerを停止したのでモジュールがロードされなくなっただけかもしれません。
そういうことか
ここで一つ理解が深まりました。Dockerが起動していても br_netfilter をアンロードすればパフォーマンスは戻ります。Dockerを起動する・しないで5%の差がでていたのはブリッジフィルターが稼働しているか否かの問題だったようです。
さて、いずれにせよスイッチングハブとして利用する場合にはブリッジフィルタは外しておきたいです。/etc/docker/daemon.json あたりで制御できないかとオプションを見てもそれらしいものはありません。
そこで、Dockerのソースを確認すると、、、
if config.EnableIPTables || config.EnableIP6Tables {
if _, err := os.Stat("/proc/sys/net/bridge"); err != nil {
if out, err := exec.Command("modprobe", "-va", "bridge", "br_netfilter").CombinedOutput(); err != nil {
log.G(context.TODO()).Warnf("Running modprobe bridge br_netfilter failed with message: %s, error: %v", out, err)
}
}
}
iptables or ip6tables が有効になるとbr_netfilterをロードするようになっているようです。
この二つのいずれかを使いたい場合(というかDockerを使う場合は両方使いたい)、モジュールのロードを抑止するオプションは無さそうです。
となると、どうするか。。。
- Docker起動後にモジュールをアンロードする
- そもそもモジュールのロードを止められないか?
1は systemd を使えば簡単にできます。Unitの処理順序にAfter=docker.service
と書いて、ExecStart=rmmod br_netfilter
としておけば良いでしょう。
理想的には2ですが、、、なんと可能でした。
/etc/modprobe.d 以下に標準でも blacklist-*.conf というファイルがいくつかあるので同様に記述します。
blacklist br_netfilter
でも、、、ロードされます。この記述では手動でロードするのは抑止できないようです。更に、
install br_netfilter /bin/true
こういう書き方ができるそうです。install はモジュールをロードする方法を別途指示する記述です。br_netfilterをロードするには /bin/true を実行しなさいという意味になるので、mobprobe br_netfilter を実行すると、「ok!」と元気よく空返事をしてくれます。万事okです。
これで Docker を起動していてもIPv6が阻害されることもパフォーマンスが下がることもなくなりました。フィルタモジュールをロードするだけで5%も差がでるのは、CPUパフォーマンスが低いからなのか、そもそもハードウェアオフロードできていたものがソフトウェア処理に変わるからなのか、、そのあたりはまた別の機会に。