Network Namespace(netns)を使ってネットワークを構築する必要がでてきた。 しかし、色々調べているとnetnsを使ってネットワークを構築するにはシェルスクリプトを使うしかなくてだる~っとなっていた。 さすがにそれは可搬性が低いので、他のツールはないか調査してみた。
- netplan
- 一番良さそうだったのだが、netplanが依存しているsystemd-networkdをnetns上でも動かさないと使えなく、大掛かりだったのでミスマッチ
- systemd-networkd
- 同上
- mininet
- netnsを使ったネットワークを環境するという目的ではマッチしていたのだが、Open vSwitchを使ったりしてミスマッチだった
- NetworkManager
- 未調査
結果として、私の要件に合うものがなかったのでnetnsplanというツールを自作した。
netnsplanはnetnsを使ったネットワークを簡単に構築するためのツールである。 名前からもわかるとおりnetplanに似せてつくっている。 これは単に私がnetplanに慣れているからという理由である。
使用例
netnsplanではデフォルトで /etc/netnsplan/*.yaml
を見に行く。
複数ファイルがある場合は1つのYAMLにマージして、結果を適用する。
ここではサンプルとして /etc/netnsplan/example.yamlを用意してこれを適用してみる。
# /etc/netnsplan/example.yaml netns: example: ethernets: eth1: addresses: - 192.168.20.1/24 route: - to: default via: 192.168.20.254 post-script: | sysctl --system iptables-restore /etc/iptables/rules.v4
この例ではexampleという名前でnetnsを作成し、そこにeth1を紐づけする。 eth1には192.168.20.1/24というIPを設定し、デフォルトゲートウェイとして192.168.20.254を設定する。 そして、post-scriptとしてsysctlとiptables-restoreを実行している。 post-scriptはnetnsplan特有の機能で、netnsの設定が終わったあとに任意のスクリプトを実行する。 実行されるスクリプトはnetns上で実行されるので、サンプルのようにsysctlやiptablesを設定したりするのに使える。
設定ファイルが作成できたら、netnsplan apply
を実行する。
$ sudo netnsplan apply INFO create netns name="example" INFO link up name="lo" netns="example" INFO set netns name="eth1" netns="example" INFO link up name="eth1" netns="example" INFO add addresses name="eth1" address="192.168.20.1/24" netns="example" INFO run post script netns="example" script="sysctl --system\niptables -nvL\n"
applyが成功すると記述したとおりの設定が行われているはず。
$ ip netns example (id: 1) $ sudo ip netns exec example ip a show dev eth1 3: eth1: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc fq_codel state DOWN group default qlen 1000 link/ether XX:XX:XX:XX:XX:XX brd ff:ff:ff:ff:ff:ff inet 192.168.20.1/24 scope global eth1 valid_lft forever preferred_lft forever
applyは複数回実行でき、すでに設定されている項目はスキップされる。 ただし、post-scriptは実行されない。 post-scriptも実行したい場合は--always-run-post-script(-R)をつける必要がある。
$ sudo netnsplan apply WARN netns is already exists name="example" # -Rをつけるとpost-scriptが実行される $ sudo ./netnsplan apply -R WARN netns is already exists name="example" INFO run post script netns="example" script="sysctl --system\niptables -nvL\n"
destroyを実行するとnetnsの設定を破棄し元に戻る。
$ sudo ./netnsplan destroy INFO delete netns name="example" $ ip netns $
対応しているデバイスタイプ
使用例ではethernetsのみを紹介したが、netnsplanでは現在以下のタイプに対応している。
- dummyデバイス(dummy-devices)
- vethデバイス(veth-devices)
他のデバイスタイプにも対応したいので乞うご期待…
インストール方法
netnsplanは今のところamd64/arm64なLinux向けのバイナリを提供している。 netnsはLinuxでしか使えないので、他OSでのサポートはしていない。 また、debやrpmも用意しているのでDebian系やRedHat系のディストリであれば簡単にインストールできるはず。
これらのパッケージが簡単に作れるgoreleaserが便利すぎる…!!!!!!
Tips1: /etc配下のファイルをnetnsごとに切り替える
/etc/netns/<netns名>
の配下にファイルを配置すると、ip netns execしたときに/etc配下のファイルを読み替えてくれる。
例えば、 /etc/netns/sample/hostsにファイルを置くとip netns exec cat /etc/hostsしたときにそのファイルに置き換えられる。
$ cat /etc/hosts 127.0.0.1 localhost ::1 localhost ip6-localhost ip6-loopback ff02::1 ip6-allnodes ff02::2 ip6-allrouters 127.0.1.1 host-machine $ cat /etc/netns/example/hosts 127.0.1.1 example-node $ sudo ip netns exec example cat /etc/hosts 127.0.1.1 example-node
なお、この機能はip netns execしたときのみに有効になるので、例えばsystemdから起動した場合は読み替えがおこなれないので注意が必要だ(一敗)。
Tips2: systemd経由で起動する
netns上で特定のサービスを起動したいとなったときに、そのサービスが起動する前にnetnsplanを実行しないとならない。 その場合、netnsplanもsystemdから起動し、対象のサービスをnetns上で実行するように変更する必要がある。 その方法について解説する。
まずnetnsplanをsystemdのサービス化する。
以下のファイルを /etc/systemd/system/netnsplan.service
として保存する。
[Unit] Description = Configure netns network After = network.target [Service] Type = oneshot ExecStart = /usr/sbin/netnsplan apply ExecReload = /usr/sbin/netnsplan apply ExecStop = /usr/sbin/netnsplan destroy RemainAfterExit = yes [Install] WantedBy = multi-user.target
保存したらnetnsplanを起動する。
$ sudo systemctl daemon-reload
$ sudo systemctl enable --now netnsplan
つぎにnetnsで動かしたいサービスの設定を変更する ここでは対象のサービス名をexample.servicceとする。
$ sudo systemctl edit example.service # 以下の内容を書き込む [Unit] After = After = netnsplan.service [Service] NetworkNamespacePath=/run/netns/vessel
Unit > Afterにnetnsplan.servieを設定することで、netnsplan起動後にexample.serviceを起動するように指定している。 また、Service > NetworkNamespacePathで使用するnetnsを指定することができる。 設定を変更したら、example.serviceを再起動するとnetns配下で起動するようになるはず。
$ sudo systemctl restart example.service
(この内容をREADMEに書き忘れていたのであとで追記しよう)
Tips3: netns配下の物理デバイス(ethernets)がdestroy後に消失する
apply/destroyを繰り返していると、稀に物理デバイス(ethernets)で設定したデバイスが消失することがある…。 これについてip-netnsのmanには以下の記載がある。
It is possible to lose the physical device when it was moved to netns and then this netns was deleted with a running process: 物理デバイスを失う可能性があります。それがnetnsに移動され、その後、実行中のプロセスでこのnetnsが削除された場合:
$ ip netns add net0 $ ip link set dev eth0 netns net0 $ ip netns exec net0 SOME_PROCESS_IN_BACKGROUND $ ip netns del net0
and eth0 will appear in the default netns only after SOME_PROCESS_IN_BACKGROUND will exit or will be killed. To prevent this the processes running in net0 should be killed before deleting the netns: そして、SOME_PROCESS_IN_BACKGROUNDが終了またはキルされた後にのみ、eth0はデフォルトのnetnsに表示されます。これを防ぐために、netnsを削除する前にnet0で実行中のプロセスをキルする必要があります:
netnsplan destroyではこの問題を回避するためにnetns上で動いているプロセスが存在した場合エラーになるようになっている。
$ sudo ./netnsplan destroy INFO delete netns name="example" Error: netns example has running processes: 519572
しかし、プロセスをすべて終了してdestroyをしたが、一度だけ消失が発生したことがある…なんで… 消失が発生したらOSを再起動するしかない、つらい。 この問題の解決の方法を知っている人がいたら教えてください…