リンクアグリゲーションは、複数のネットワークインターフェイスを束ねて扱う技術の総称。 たとえば、2 本のイーサネットを束ねて冗長化することで 1 本に障害が起こってもサービスを提供し続けることができる。 あるいは、フレームをロードバランスすることで単一のイーサネットよりもスループットを向上させる用途でも用いられる。 今回は Linux のリンクアグリゲーションの実装であるボンディング (Bonding) を Network Namespace と共に使ってみよう。
使った環境は次のとおり。
$ cat /etc/lsb-release DISTRIB_ID=Ubuntu DISTRIB_RELEASE=22.04 DISTRIB_CODENAME=jammy DISTRIB_DESCRIPTION="Ubuntu 22.04.1 LTS" $ uname -srm Linux 5.15.0-56-generic aarch64 $ ip -V ip utility, iproute2-5.15.0, libbpf 0.5.0
もくじ
下準備
まずは利用するパッケージをインストールしておく。
$ sudo apt-get update $ sudo apt-get install iproute2 tcpdump iputils-ping
ネットワークを構築する
ここからは Network Namespace と Virtual Ethernet Device を使ってネットワークを構築していく。
今回は ns1
と ns2
と bridge
という名前で Network Namespace を用意してつないでいく。
物理構成としては [ns1]=[bridge]-[ns2]
という感じ。
ns1
と bridge
の間が 2 本のネットワークインターフェイスでつながる。
$ sudo ip netns add ns1 $ sudo ip netns add ns2 $ sudo ip netns add bridge
続いて Virtual Ethernet Device を追加する。
ここで ns1
に所属させる Virtual Ethernet Device は ns1-veth0
と ns1-veth1
という 2 つを用意する。
これらをボンディングで冗長化する。
$ sudo ip link add ns1-veth0 type veth peer name ns1-veth0-br0 $ sudo ip link add ns1-veth1 type veth peer name ns1-veth1-br0 $ sudo ip link add ns2-veth0 type veth peer name ns2-br0
それぞれのネットワークインターフェイスを Network Namespace に所属させていく。
$ sudo ip link set ns1-veth0 netns ns1 $ sudo ip link set ns1-veth1 netns ns1 $ sudo ip link set ns2-veth0 netns ns2 $ sudo ip link set ns1-veth0-br0 netns bridge $ sudo ip link set ns1-veth1-br0 netns bridge $ sudo ip link set ns2-br0 netns bridge
ns1
側のネットワークインターフェイスの設定をする。
まずは ns1-bond0
という名前でボンディング用のインターフェイスを用意する。
miimon というのは MII (Media Independent Interface) で何 ms ごとにインターフェイスの状態を監視するかを表している。
その後ろの mode active-backup はボンディングのやり方で、active-backup だと 1 つのインターフェイスだけがアクティブになって通信に使われる。
$ sudo ip netns exec ns1 ip link add ns1-bond0 type bond miimon 100 mode active-backup
ボンディング用のインターフェイスに 2 つの Virtual Network Device を所属させる。
$ sudo ip netns exec ns1 ip link set ns1-veth0 master ns1-bond0 $ sudo ip netns exec ns1 ip link set ns1-veth1 master ns1-bond0 $ sudo ip netns exec ns1 ip link set ns1-veth0 up $ sudo ip netns exec ns1 ip link set ns1-veth1 up
ボンディング用のインターフェイスの状態を UP に設定して、IP アドレスを付与する。 これで 2 つの Virtual Ethernet Device を冗長化した形でボンディング用のインターフェイスが使えるようになった。
$ sudo ip netns exec ns1 ip link set ns1-bond0 up $ sudo ip netns exec ns1 ip address add 192.0.2.1/24 dev ns1-bond0
次は ns2
の方のインターフェイスを設定する。
こちらは特に説明すべきことはない。
$ sudo ip netns exec ns2 ip link set ns2-veth0 up $ sudo ip netns exec ns2 ip address add 192.0.2.2/24 dev ns2-veth0
次に bridge
のインターフェイスを設定する。
Linux ブリッジを用意して、それぞれのネットワークインターフェイスをつないで使える状態にする。
$ sudo ip netns exec bridge ip link set ns1-veth0-br0 up $ sudo ip netns exec bridge ip link set ns1-veth1-br0 up $ sudo ip netns exec bridge ip link set ns2-br0 up $ sudo ip netns exec bridge ip link add dev br0 type bridge $ sudo ip netns exec bridge ip link set br0 up $ sudo ip netns exec bridge ip link set ns1-veth0-br0 master br0 $ sudo ip netns exec bridge ip link set ns1-veth1-br0 master br0 $ sudo ip netns exec bridge ip link set ns2-br0 master br0
動作を確認する
さて、これですべての準備が整った。
ns1
から ns2
の IP アドレスに向かって ping を打ってみよう。
うまくいけば、ちゃんと応答が返ってくるはず。
$ sudo ip netns exec ns1 ping 192.0.2.2 -I 192.0.2.1 PING 192.0.2.2 (192.0.2.2) from 192.0.2.1 : 56(84) bytes of data. 64 bytes from 192.0.2.2: icmp_seq=1 ttl=64 time=0.110 ms 64 bytes from 192.0.2.2: icmp_seq=2 ttl=64 time=0.157 ms 64 bytes from 192.0.2.2: icmp_seq=3 ttl=64 time=0.175 ms
通信がどのように流れているかを観察してみよう。
まずは ns1
のボンディング用インターフェイス ns1-bond0
から。
ちゃんと ICMP echo request / reply がやり取りされている。
$ sudo ip netns exec ns1 tcpdump -tnl -i ns1-bond0 icmp tcpdump: verbose output suppressed, use -v[v]... for full protocol decode listening on ns1-bond0, link-type EN10MB (Ethernet), snapshot length 262144 bytes IP 192.0.2.1 > 192.0.2.2: ICMP echo request, id 56648, seq 13, length 64 IP 192.0.2.2 > 192.0.2.1: ICMP echo reply, id 56648, seq 13, length 64 IP 192.0.2.1 > 192.0.2.2: ICMP echo request, id 56648, seq 14, length 64 IP 192.0.2.2 > 192.0.2.1: ICMP echo reply, id 56648, seq 14, length 64 IP 192.0.2.1 > 192.0.2.2: ICMP echo request, id 56648, seq 15, length 64 IP 192.0.2.2 > 192.0.2.1: ICMP echo reply, id 56648, seq 15, length 64
次は ns1
の ns1-veth0
を観察してみよう。
このインターフェイスは ns1-bond0
によって束ねられている。
こちらも ICMP echo request / reply がやり取りされているようだ。
$ sudo ip netns exec ns1 tcpdump -tnl -i ns1-veth0 icmp tcpdump: verbose output suppressed, use -v[v]... for full protocol decode listening on ns1-veth0, link-type EN10MB (Ethernet), snapshot length 262144 bytes IP 192.0.2.1 > 192.0.2.2: ICMP echo request, id 56648, seq 35, length 64 IP 192.0.2.2 > 192.0.2.1: ICMP echo reply, id 56648, seq 35, length 64 IP 192.0.2.1 > 192.0.2.2: ICMP echo request, id 56648, seq 36, length 64 IP 192.0.2.2 > 192.0.2.1: ICMP echo reply, id 56648, seq 36, length 64 IP 192.0.2.1 > 192.0.2.2: ICMP echo request, id 56648, seq 37, length 64 IP 192.0.2.2 > 192.0.2.1: ICMP echo reply, id 56648, seq 37, length 64
では ns1
の ns1-veth1
はどうだろうか。
このインターフェースも ns1-bond0
によって束ねられている。
しかし、このインターフェイスにはパケットが流れてこない。
これはボンディングのモードが active-backup になっているため。
つまり、こちらのインターフェイスがバックアップになっている。
$ sudo ip netns exec ns1 tcpdump -tnl -i ns1-veth1 icmp tcpdump: verbose output suppressed, use -v[v]... for full protocol decode listening on ns1-veth1, link-type EN10MB (Ethernet), snapshot length 262144 bytes
試しにアクティブになっている方のインターフェイスをダウンさせてみよう。
bridge
の方に所属している ns1-veth0-br0
の状態を DOWN に設定する。
$ sudo ip netns exec bridge ip link set ns1-veth0-br0 down
すると、バックアップになっていた ns1-veth1
の方にパケットが流れ始める。
障害を検知してこちらがマスターに昇格したようだ。
$ sudo ip netns exec ns1 tcpdump -tnl -i ns1-veth1 icmp tcpdump: verbose output suppressed, use -v[v]... for full protocol decode listening on ns1-veth1, link-type EN10MB (Ethernet), snapshot length 262144 bytes IP 192.0.2.1 > 192.0.2.2: ICMP echo request, id 56648, seq 82, length 64 IP 192.0.2.2 > 192.0.2.1: ICMP echo reply, id 56648, seq 82, length 64 IP 192.0.2.1 > 192.0.2.2: ICMP echo request, id 56648, seq 83, length 64 IP 192.0.2.2 > 192.0.2.1: ICMP echo reply, id 56648, seq 83, length 64 IP 192.0.2.1 > 192.0.2.2: ICMP echo request, id 56648, seq 84, length 64 IP 192.0.2.2 > 192.0.2.1: ICMP echo reply, id 56648, seq 84, length 64
試しにダウンしていたインターフェイスの状態を UP に戻してみよう。
$ sudo ip netns exec bridge ip link set ns1-veth0-br0 up
それでも、ns1-veth1
の方にパケットが流れ続ける。
元のマスターとバックアップの状態に自動で戻らないのは、フラッピングを防ぐ上で合理的といえる。
$ sudo ip netns exec ns1 tcpdump -tnl -i ns1-veth1 icmp tcpdump: verbose output suppressed, use -v[v]... for full protocol decode listening on ns1-veth1, link-type EN10MB (Ethernet), snapshot length 262144 bytes ... IP 192.0.2.1 > 192.0.2.2: ICMP echo request, id 56648, seq 108, length 64 IP 192.0.2.2 > 192.0.2.1: ICMP echo reply, id 56648, seq 108, length 64 IP 192.0.2.1 > 192.0.2.2: ICMP echo request, id 56648, seq 109, length 64 IP 192.0.2.2 > 192.0.2.1: ICMP echo reply, id 56648, seq 109, length 64 IP 192.0.2.1 > 192.0.2.2: ICMP echo request, id 56648, seq 110, length 64 IP 192.0.2.2 > 192.0.2.1: ICMP echo reply, id 56648, seq 110, length 64
いじょう。
まとめ
今回は Linux におけるリンクアグリゲーション (LAG) の実装であるボンディングを試してみた。 ボンディングを使うことで、ネットワークインターフェイスを束ねて扱うことができる。