この記事は
下記の記事に触発されて、自分でも試したくなった
Docker上のFRRoutingでOSPFを動かしてみる
元記事ではcompose.yml
を使っていなかったが
また後で再現するときに楽なので、今回はcompose.yml
を作る
前提条件
- Windows 11 Home の Docker Desktop環境で動作
- Docker
- Client Version: 25.0.3
- Server: Docker Desktop 4.27.2 (137060)
- Engine Version: 25.0.3
- Docker Compose version v2.24.5-desktop.1
Docker Desktopは一定規模以上の企業で使う場合、有償となります
構成
- 両端のネットワークにPCが1台ずつ存在する
- 真ん中の19ネットワークにはルータしか存在しない
ネットワーク | PC1 | ルーター1 | ルーター | PC2 |
---|---|---|---|---|
172.18.0.0/16 | alpine1 (.10) | frr1 (.250) | - | - |
172.19.0.0/16 | - | frr1 (.250) | frr2 (.251) | - |
172.20.0.0/16 | - | - | frr2 (.250) | alpine2 (.10) |
docker compose コマンド復習
気づいたらdocker-composeが色々変わっていた件
-
dcoker-compose.yml
はもう古い。compose.yml
を使うべし。 -
compose.yml
の冒頭にバージョンを書くのも、もう古い。- docker docs の関連ページはこちら
- docker compose V2 は
compose.yml
のversion
を無視すると書かれている
Docker Compose Up
compose.yml
が置かれている階層(今回はworkdir
)に移動し、docker compose up
する
-d
オプションをつけて、デタッチしつつ、ログを画面に流す
cd workdir
docker compose up -d && docker compose logs -f
alpineのデフォルトゲートウェイを変更する
alpine1コンテナの中で作業する
cd workdir
docker compose exec alpine1 bin/sh
alpine1のルーティングテーブルを確認すると
デフォルトゲートウェイが172.18.0.1
となっている
Destinationが「0.0.0.0」,Genmask「0.0.0.0」の設定行は,標準のパケット転送先を示しています。この設定は,他の設定に該当しないすべてのパケットに適用されます。この行で設定されているGatewayは,標準で使われるゲートウエイという意味で「デフォルト・ゲートウエイ」と呼ばれます。
https://xtech.nikkei.com/it/article/COLUMN/20080520/303086/
/ # route -n
Kernel IP routing table
Destination Gateway Genmask Flags Metric Ref Use Iface
0.0.0.0 172.18.0.1 0.0.0.0 UG 0 0 0 eth0
172.18.0.0 0.0.0.0 255.255.0.0 U 0 0 0 eth0
既存のルートを削除し、新ルートを追加する
ルーターコンテナのIPアドレスである172.18.0.250
に変更する
route del default gw 172.18.0.1
route add default gw 172.18.0.250
変更が反映されていることを確認する
/ # route -n
Kernel IP routing table
Destination Gateway Genmask Flags Metric Ref Use Iface
0.0.0.0 172.18.0.250 0.0.0.0 UG 0 0 0 eth0
172.18.0.0 0.0.0.0 255.255.0.0 U 0 0 0 eth0
alpine2についても同様の設定を行う
alpine1と通信する相手であるalpine2についても
同様の手順でデフォルトゲートウェイを変更する
ただし、所属するネットワークが違う点に注意する
docker compose exec alpine2 bin/sh
route del default gw 172.20.0.1
route add default gw 172.20.0.250
route -n
Kernel IP routing table
Destination Gateway Genmask Flags Metric Ref Use Iface
0.0.0.0 172.20.0.250 0.0.0.0 UG 0 0 0 eth0
172.20.0.0 0.0.0.0 255.255.0.0 U 0 0 0 eth0
この状態ではまだPINGは通らない
/ # ping 172.20.0.10
PING 172.20.0.10 (172.20.0.10): 56 data bytes
^C
--- 172.20.0.10 ping statistics ---
13 packets transmitted, 0 packets received, 100% packet loss
FRRoutingコンテナ
続いて、FRRoutingコンテナの設定をしていく
aplineは単なる軽量Linuxなので、事前知識は不要だったが
FRRoutingについては中身を知らないと設定できないので
ひとつずつ調べて進める
VTYSHとは何か
自動翻訳によると、以下の定義だそうです
VTYSHはFRRデーモン用のシェルである。VTYSHは各デーモンで定義されたすべてのCLIコマンドを統合し、単一のシェルでユーザーに表示します。これにより、ユーザーは各デーモンにtelnetで接続して個々のシェルを使用する手間を省くことができます。統合は、デーモンからコマンドを抽出し、ビルド時にVTYSHに注入することで達成される。
何も設定しないままvtysh
と入力すると、コンフィグファイルが無いとエラーが出る
/ # vtysh
% Can't open configuration file /etc/frr/vtysh.conf due to 'No such file or directory'.
コンフィグファイルの取り扱い
vtysh.conf
とfrr.conf
は何が違うのだろう
FRRは/etc/frr/frr.confにある単一の設定ファイルを使用する。FRRがinitスクリプトやsystemdを使って起動されると、vtyshが起動され、設定ファイルを読み込み、適切な部分を興味のあるデーモンだけに送信する。実行中の設定更新は、同様にvtyshを使ってこの単一ファイルに永続化される。
この段階で、/etc/frr/
にあるコンフィグファイルはstaticd.conf
とzebra.conf
の2つだけ
/ # ls -la /etc/frr/
total 16
drwxr-xr-x 1 frr frr 4096 Mar 24 08:43 .
drwxr-xr-x 1 root root 4096 Mar 24 08:43 ..
-rw-r--r-- 1 frr frr 3840 Nov 6 2022 daemons
-rw------- 1 frr frr 0 Mar 24 08:43 staticd.conf
-rw------- 1 frr frr 0 Mar 24 08:43 zebra.conf
vtysh.confをつくる
vtysh.conf.sampleをGithubから拾ってくる
service integrated-vtysh-config
の行だけ、行頭の!
を取り外して有効化しておく
vi /etc/frr/vtysh.conf
!
! Sample configuration file for vtysh.
!
service integrated-vtysh-config
!hostname quagga-router
!username root nopassword
!
VTYSHでいろいろコマンド確認
VTYSHの中に入るには、単純にVTYSHと打てばよいらしい
/ # vtysh
Hello, this is FRRouting (version 8.4_git).
Copyright 1996-2005 Kunihiro Ishiguro, et al.
現在の設定を確認する
frr1# show run
Building configuration...
Current configuration:
!
frr version 8.4_git
frr defaults traditional
hostname frr1
domainname
service integrated-vtysh-config
!
end
その他の設定を確認しようとすると、「zebra
が動いてないよ」と怒られる
frr1# show int brief
zebra is not running
frr1# show ip route
zebra is not running
zebra起動までのトラブルシューティング
たしかにdocker compose up
した際のログを確認すると、zebra
の起動に失敗している
これは後続のログのfrr.conf
が無いことが原因なのだろうか
ひとまずvtysh.conf
はつくって、サービスを統合するオプションも有効にしてあるので、コンテナを再起動してみる
frr1 | Failed to start zebra!
frr1 | /etc/frr/frr.conf does not exist; skipping config apply
・・・ダメでした
/etc/frr/
にfrr.conf
を作ってみたけど、それもダメ
frr1 | 2024/03/24 15:18:05 WATCHFRR: [ZG9QC-QRCJZ] failed to mkdir "/var/tmp/frr/watchfrr.11": File exists
frr1 | 2024/03/24 15:18:05 WATCHFRR: [M1DC0-ZDNYJ] crashlog and per-thread log buffering unavailable!
frr1 | 2024/03/24 15:18:05 WATCHFRR: [T83RR-8SM5G] watchfrr 8.4_git starting: vty@0
frr1 | 2024/03/24 15:18:05 WATCHFRR: [ZCJ3S-SPH5S] zebra state -> down : initial connection attempt failed
frr1 | 2024/03/24 15:18:05 WATCHFRR: [ZCJ3S-SPH5S] staticd state -> down : initial connection attempt failed
frr1 | 2024/03/24 15:18:05 WATCHFRR: [YFT0P-5Q5YX] Forked background command [pid 12]: /usr/lib/frr/watchfrr.sh restart all
frr1 | /usr/lib/frr/frrcommon.sh: line 211: kill: (86) - No such process
frr1 | Cannot stop zebra: pid file not found
frr1 | 2024/03/24 15:18:05 ZEBRA: [ZG9QC-QRCJZ] failed to mkdir "/var/tmp/frr/zebra.21": File exists
frr1 | 2024/03/24 15:18:05 ZEBRA: [M1DC0-ZDNYJ] crashlog and per-thread log buffering unavailable!
frr1 | privs_init: initial cap_set_proc failed: Operation not permitted
frr1 | Wanted caps: cap_net_admin,cap_net_raw,cap_sys_admin=p
frr1 | Have caps: cap_chown,cap_dac_override,cap_fowner,cap_fsetid,cap_kill,cap_setgid,cap_setuid,cap_setpcap,cap_net_bind_service,cap_net_raw,cap_sys_chroot,cap_mknod,cap_audit_write,cap_setfcap=p
frr1 | Failed to start zebra!
frr1 | 2024/03/24 15:18:06 STATIC: [ZG9QC-QRCJZ] failed to mkdir "/var/tmp/frr/staticd.24": File exists
frr1 | 2024/03/24 15:18:06 STATIC: [M1DC0-ZDNYJ] crashlog and per-thread log buffering unavailable!
frr1 | 2024/03/24 15:18:06 WATCHFRR: [QDG3Y-BY5TN] staticd state -> up : connect succeeded
frr1 | 2024/03/24 15:19:00 WATCHFRR: [K54RW-4APGS][EC 268435457] startup did not complete within timeout (1/2 daemons running)
frr1 | 2024/03/24 15:19:06 WATCHFRR: [YFT0P-5Q5YX] Forked background command [pid 34]: /usr/lib/frr/watchfrr.sh restart all
frr1 | 2024/03/24 15:19:06 WATCHFRR: [HD38Q-0HBRT][EC 268435457] staticd state -> down : read returned EOF
frr1 | Cannot stop zebra: pid file not found
frr1 | privs_init: initial cap_set_proc failed: Operation not permitted
frr1 | Wanted caps: cap_net_admin,cap_net_raw,cap_sys_admin=p
frr1 | Have caps: cap_chown,cap_dac_override,cap_fowner,cap_fsetid,cap_kill,cap_setgid,cap_setuid,cap_setpcap,cap_net_bind_service,cap_net_raw,cap_sys_chroot,cap_mknod,cap_audit_write,cap_setfcap=p
frr1 | Failed to start zebra!
frr1 | 2024/03/24 15:19:06 WATCHFRR: [QDG3Y-BY5TN] staticd state -> up : connect succeeded
staticd
は動いているっぽいけど、zebra
は起動しておらず
/ # ps
PID USER TIME COMMAND
1 root 0:00 /sbin/tini -- /usr/lib/frr/docker-start
7 root 0:00 {docker-start} /bin/bash /usr/lib/frr/docker-start
11 root 0:00 /usr/lib/frr/watchfrr zebra staticd
27 root 0:00 bin/sh
48 frr 0:00 /usr/lib/frr/staticd -d -F traditional -A 127.0.0.1
51 root 0:00 ps
frr.confが空っぽなのはいかがなものか
vtysh.conf
でコンフィグファイルを統合するように設定したものの
肝心な統合先であるfrr.conf
が当初不在だったのはいかがなものか
service integrated-vtysh-config
vtysh will always write frr.conf.
今、frr.conf
は空っぽであるがそれでよいのか
zebra
が起動しない問題は、frr.conf
とは無関係なのか?
解決
compose.yml
にprivileged: true
を追記したら動きました
zebra
を起動するには特権が必要だったという話
とくにfrr.conf
は関係なかった様子
Compose.ymlを編集した際は、build
オプションをつけないと反映されないので注意
docker compose up --build -d
よくよく見ると、先行記事にも書いてありました
frr1, frr2を作り、それぞれnet1とnet2、net2とnet3に接続します。
--privilegedをつけないと動かないようです。
OSPFを有効にする
vi /etc/frr/daemons
でデーモンを編集し、ospf=yes
とする
/etc/frr # cat daemons
# This file tells the frr package which daemons to start.
#
# Sample configurations for these daemons can be found in
# /usr/share/doc/frr/examples/.
#
# ATTENTION:
#
# When activating a daemon for the first time, a config file, even if it is
# empty, has to be present *and* be owned by the user and group "frr", else
# the daemon will not be started by /etc/init.d/frr. The permissions should
# be u=rw,g=r,o=.
# When using "vtysh" such a config file is also needed. It should be owned by
# group "frrvty" and set to ug=rw,o= though. Check /etc/pam.d/frr, too.
#
# The watchfrr, zebra and staticd daemons are always started.
#
bgpd=no
ospfd=yes
ospf6d=no
ripd=no
ripngd=no
isisd=no
docker compose restart frr1
でコンテナを再起動し
docker compose exec frr1 bin/sh
で再度コンテナに入る
プロセスの一覧を確認すると、ospfd
が動いていることがわかる
/ # ps
PID USER TIME COMMAND
1 root 0:00 /sbin/tini -- /usr/lib/frr/docker-start
7 root 0:00 {docker-start} /bin/bash /usr/lib/frr/docker-start
12 root 0:00 /usr/lib/frr/watchfrr zebra ospfd staticd
26 frr 0:00 /usr/lib/frr/zebra -d -F traditional -A 127.0.0.1 -s 90000000
31 frr 0:00 /usr/lib/frr/ospfd -d -F traditional -A 127.0.0.1
34 frr 0:00 /usr/lib/frr/staticd -d -F traditional -A 127.0.0.1
38 root 0:00 bin/sh
44 root 0:00 ps
OSPFの設定をする(VTYSH)
frr1でVTYSHを起動し、先ほど失敗したコマンドを確認する
まずはインターフェイスの要約
eth0とeth1にそれぞれ.250のIPアドレスが付与されていることがわかる
frra1# show int brief
Interface Status VRF Addresses
--------- ------ --- ---------
eth0 up default 172.18.0.250/16
eth1 up default 172.19.0.250/16
lo up default
つづいてIPルーティングテーブルを表示する
2つのネットワークとダイレクトにつながっていることがわかる
またデフォルトゲートウェイ(0.0.0.0)は172.18.0.1向けということがわかる
frra1# show ip route
Codes: K - kernel route, C - connected, S - static, R - RIP,
O - OSPF, I - IS-IS, B - BGP, E - EIGRP, N - NHRP,
T - Table, v - VNC, V - VNC-Direct, A - Babel, F - PBR,
f - OpenFabric,
> - selected route, * - FIB route, q - queued, r - rejected, b - backup
t - trapped, o - offload failure
K>* 0.0.0.0/0 [0/0] via 172.18.0.1, eth0, 00:03:35
C>* 172.18.0.0/16 is directly connected, eth0, 00:03:35
C>* 172.19.0.0/16 is directly connected, eth1, 00:03:35
設定を投入していく
まずはわけもわからず、先行文献に従って設定を投入してみる
/ # vtysh
frr1# conf t
frr1(config)# interface lo
frr1(config-if)# ip address 1.1.1.1/32
frr1(config-if)# exit
frr1(config)# router ospf
frr1(config-router)# router-info area 0.0.0.0
frr1(config-router)# network 172.18.0.0/16 area 0.0.0.0
frr1(config-router)# network 172.19.0.0/16 area 0.0.0.0
frr1(config-router)# end
設定を確認する
frra1# show run
Building configuration...
Current configuration:
!
frr version 8.4_git
frr defaults traditional
hostname frra1
no ipv6 forwarding
domainname
service integrated-vtysh-config
!
interface lo
ip address 1.1.1.1/32
exit
!
router ospf
network 172.18.0.0/16 area 0.0.0.0
network 172.19.0.0/16 area 0.0.0.0
router-info area
exit
!
end
IPルートが反映されていることを確認する
frra1# show ip route
Codes: K - kernel route, C - connected, S - static, R - RIP,
O - OSPF, I - IS-IS, B - BGP, E - EIGRP, N - NHRP,
T - Table, v - VNC, V - VNC-Direct, A - Babel, F - PBR,
f - OpenFabric,
> - selected route, * - FIB route, q - queued, r - rejected, b - backup
t - trapped, o - offload failure
K>* 0.0.0.0/0 [0/0] via 172.18.0.1, eth0, 00:09:18
C>* 1.1.1.1/32 is directly connected, lo, 00:01:41
O 172.18.0.0/16 [110/10] is directly connected, eth0, weight 1, 00:00:58
C>* 172.18.0.0/16 is directly connected, eth0, 00:09:18
O 172.19.0.0/16 [110/10] is directly connected, eth1, weight 1, 00:00:54
C>* 172.19.0.0/16 is directly connected, eth1, 00:09:18
設定を永続化させる
frra1# write mem
Note: this version of vtysh never writes vtysh.conf
Building Configuration...
Integrated configuration saved to /etc/frr/frr.conf
[OK]
frr.conf
に書き込まれているらしいので、確認してみる
/ # cat /etc/frr/frr.conf
frr version 8.4_git
frr defaults traditional
hostname frra1
no ipv6 forwarding
domainname
service integrated-vtysh-config
!
interface lo
ip address 1.1.1.1/32
exit
!
router ospf
network 172.18.0.0/16 area 0.0.0.0
network 172.19.0.0/16 area 0.0.0.0
router-info area
exit
!
frr2の設定
こちらも見よう見まねで設定
まずはdaemons
ファイルを編集してOSPFを有効にする
ちなみに順序を間違えて先に設定を投入しようとすると、VTYSHのコンフィグで怒られる(ある意味親切ですね)
frr2(config)# route ospf
ospfd is not running
vtyshを起動して、以下のコマンドを打ち込む
frr2# conf t
frr2(config)# interface lo
frr2(config-if)# ip address 2.2.2.2/32
frr2(config-if)# exit
frr2(config)# router ospf
frr2(config-router)# router-info area 0.0.0.0
frr2(config-router)# network 172.19.0.0/16 area 0.0.0.0
frr2(config-router)# network 172.20.0.0/16 area 0.0.0.0
frr2(config-router)# end
こちらも永続化のためにwrite mem
しておく
exit
でvtyshを抜けて、cat /etc/frr/frr.conf
で設定が保存されていることを確認する
/ # cat /etc/frr/frr.conf
frr version 8.4_git
frr defaults traditional
hostname frr2
no ipv6 forwarding
domainname
service integrated-vtysh-config
!
interface lo
ip address 2.2.2.2/32
exit
!
router ospf
network 172.19.0.0/16 area 0.0.0.0
network 172.20.0.0/16 area 0.0.0.0
router-info area
exit
!
OSPF neighborを確認する
frr1, frr2の両方の設定が完了した
frr1から見て、frr2がネイバー関係にあることがわかる
frr1# show ip ospf neighbor
Neighbor ID Pri State Up Time Dead Time Address Interface RXmtL RqstL DBsmL
2.2.2.2 1 Full/Backup 2m33s 35.709s 172.19.0.251 eth1:172.19.0.250 0 0 0
逆もしかり
frr2# show ip ospf neighbor
Neighbor ID Pri State Up Time Dead Time Address Interface RXmtL RqstL DBsmL
1.1.1.1 1 Full/DR 5m18s 31.757s 172.19.0.250 eth0:172.19.0.251 0 0 0
いざ、疎通確認!!
alpine1からalpine2に向かってトレースルート
/ # traceroute -n 172.20.0.10
traceroute to 172.20.0.10 (172.20.0.10), 30 hops max, 46 byte packets
1 172.18.0.250 0.009 ms 0.011 ms 0.012 ms
2 172.19.0.251 0.012 ms 0.014 ms 0.018 ms
3 172.20.0.10 0.010 ms 0.011 ms 0.018 ms
逆方向
/ # traceroute -n 172.18.0.10
traceroute to 172.18.0.10 (172.18.0.10), 30 hops max, 46 byte packets
1 172.20.0.250 0.009 ms 0.007 ms 0.008 ms
2 172.19.0.250 0.016 ms 0.010 ms 0.010 ms
3 172.18.0.10 0.015 ms 0.010 ms 0.008 ms
うまくいきました!
compose.yml
services:
alpine1:
image: alpine
container_name: alpine1
hostname: alpine1
tty: true
stdin_open: true
privileged: true
networks:
net1:
ipv4_address: 172.18.0.10
alpine2:
image: alpine
container_name: alpine2
hostname: alpine2
tty: true
stdin_open: true
privileged: true
networks:
net3:
ipv4_address: 172.20.0.10
frr1:
image: frrouting/frr
container_name: frr1
hostname: frr1
privileged: true
networks:
net1:
ipv4_address: 172.18.0.250
net2:
ipv4_address: 172.19.0.250
frr2:
image: frrouting/frr
container_name: frr2
hostname: frr2
privileged: true
networks:
net2:
ipv4_address: 172.19.0.251
net3:
ipv4_address: 172.20.0.250
networks:
net1:
ipam:
config:
- subnet: 172.18.0.0/16
net2:
ipam:
config:
- subnet: 172.19.0.0/16
net3:
ipam:
config:
- subnet: 172.20.0.0/16
参考