docker: frrouting/frr nwmode=host

前回の様にdocker用のNWを論理的に用意してfrrを動作させる場合、論理NW内で閉じて遊ぶ分には問題がないが、外部のルータやサーバと通信を行いたい場合に少し面倒になる。

外部RT — Host — frr(container)

Hostを境にサブネットが変わるのでHostでルーティングを設定する必要がある。テンポラリに静的なルーティングを書くのは簡単であるが、Hostの再起動を考慮するのはいささか面倒でHostでfrrを動作させたくなる。一方でfrrをHostにインストールするのもまた面倒な作業である。NWモードをhostにしてfrrコンテナを動かすことで外部RTと同じsubnet上でfrrを動作させることができる。

dockerではコンテナを動作させる際にNWモードを選択できる。3つに限られるわけではないようだが、代表的には次の3つが使用可能である。

network_mode: "bridge"  Hostを境界にサブネットを分割する
network_mode: "host"   HostのNW接続をコンテナが共用する
network_mode: "none"   コンテナはネットワーク接続を利用しない

NWモードをhostとした場合、hostのIPアドレス/IFを共有すると理解していたが実際にはNW周りの諸々、ルーティングテーブルなども共有するようである。Dockerのドキュメントを読むとホストのnetwork namespaceを共用すると記載されている。(https://docs.docker.com/network/host/

この場合のdocker-composeのサンプルは

version: '2.3'
services:
  frr01:
    image: frrouting/frr:multiarch
    container_name: Container_FRR
    init: true
    cap_add:
      - NET_ADMIN
      - SYS_ADMIN
    network_mode: "host"
    command: RUN
    restart: unless-stopped
    volumes:
      - "./files/frr/conf:/etc/frr"
      - "./files/frr/docker-start:/usr/lib/frr/docker-start"

portsやexposeの設定は要らないようだが、Hostと同じポートを使用しようとするとコンテナの起動に失敗すると思われるので注意が必要。また、iptablesやfirewalldを設定するようなコンテナをNWモード=hostで動作させる場合はHost側の設定とコンフリクトしてしまうようなのでコンテナのスタートスクリプトなどからは抜いて置いた方がいい。(ホストのネットワークスタックを使用するので、ルーティングテーブル、ファイアウォール、ネットワークインタフェースあたりは共用している)

コンテナ(frr)でルーティングを設定してみると

# docker exec -it Container_FRR /bin/sh
Container_FRR:~# vtysh

Hello, this is FRRouting (version 7.5-dev_git).
Copyright 1996-2005 Kunihiro Ishiguro, et al.

frr01# conf t
frr01(config)# ip route 8.8.8.8 255.255.255.255 192.168.0.1
frr01(config)# exit
frr01# exit
Container_FRR:~#
Container_FRR:~# route -n
Kernel IP routing table
Destination     Gateway         Genmask         Flags Metric Ref    Use Iface
~省略
8.8.8.8         192.168.0.1    255.255.255.255 UGH   20     0        0 eth0
~省略

Host側でルーティングを確認しても同様の結果を得られる

# route -n
Kernel IP routing table
Destination     Gateway         Genmask         Flags Metric Ref    Use Iface
~省略
8.8.8.8         192.168.0.1    255.255.255.255 UGH   20     0        0 eth0
~省略

writeしてコンテナを終了させるとどうなるか

# docker exec -it Container_FRR /bin/sh
Container_FRR:~# vtysh

Hello, this is FRRouting (version 7.5-dev_git).
Copyright 1996-2005 Kunihiro Ishiguro, et al.

frr01# write
frr01# exit
Container_FRR:~#
Container_FRR:~# route -n
Kernel IP routing table
Destination     Gateway         Genmask         Flags Metric Ref    Use Iface
~省略
8.8.8.8         192.168.0.1    255.255.255.255 UGH   20     0        0 eth0
~省略
# docker-compose down
Removing Container_FRR ... done
# route -n
Kernel IP routing table
Destination     Gateway         Genmask         Flags Metric Ref    Use Iface
~省略
8.8.8.8         192.168.0.1    255.255.255.255 UGH   20     0        0 eth0
~省略

コンテナの終了時にルーティングテーブルを元に戻すなんて動作は無いので追加したルーティングは残ってしまう。動的ルーティングを使う際には注意したほうがいいかもしれない。試しにOSPFでルートを学習させた後にFRRを停止してみたが、隣接機がneighbor downを検知した後も、FRRが動いていたHostのルーティングテーブルにはOSPFのエントリが残ったままであった。

frrで設定したルーティングを手作業でHostのルーティングテーブルから削除する。

# ip route del 8.8.8.8/32
# route -n
~ 8.8.8.8/32が消えることを確認

再びコンテナ(frr)を起動するとどうなるか?

# docker-compose up -d
Creating Container_FRR ... done
#
# route -n
~省略
8.8.8.8         192.168.0.1    255.255.255.255 UGH   20     0        0 eth0
~省略

frrのconfigにルーティングが保存されているため、コンテナ起動により再度ルーティングの設定が行われている。なお、OSPFの場合はFRRのdownで消えていなかったルーティングがFRRの再起動により削除され、neighbor確立後に改めてルーティングが挿入される。