CUBE SUGAR CONTAINER

技術系のこと書きます。

Linux でリンクアグリゲーション (LAG) を試してみる

リンクアグリゲーションは、複数のネットワークインターフェイスを束ねて扱う技術の総称。 たとえば、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 を使ってネットワークを構築していく。 今回は ns1ns2bridge という名前で Network Namespace を用意してつないでいく。 物理構成としては [ns1]=[bridge]-[ns2] という感じ。 ns1bridge の間が 2 本のネットワークインターフェイスでつながる。

$ sudo ip netns add ns1
$ sudo ip netns add ns2
$ sudo ip netns add bridge

続いて Virtual Ethernet Device を追加する。 ここで ns1 に所属させる Virtual Ethernet Device は ns1-veth0ns1-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

次は ns1ns1-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

では ns1ns1-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) の実装であるボンディングを試してみた。 ボンディングを使うことで、ネットワークインターフェイスを束ねて扱うことができる。