4月 292023
 

以前のエントリで「Dockerを利用してWordPressを動作させます。それもブリッジを用いて。」や「AlmaLinux8 の VRF。」なんてのを書いていますが、それの改訂版です。

 
なんか、色々調べてみると Linux 方面では bridge を有効にするには全部で三つの方法があるのだそうな。

  • bridge-utils を使う
  • nmcli を使う
  • ip link set dev を使う

 
以前の経験から、古いアーキテクチャである bridge-utils は既に rpm にもなっていない。かつ、IPv6 通信ができない。と、いうことでもう利用してはいけないのであります。IPv6 通信ができない。と、いうのは上記のリンクにも書いています。

nmcli を利用すると、設定情報は Almalinux8 の場合は /etc/sysconfig/network-scripts/ 配下にファイルとして保存してくれるんだけど、これが Almalinux9 になると、保存先が変わって記載方法も変わるのであまり使いたくない・・。

直感的に『あぁ。設定しているなぁ。』と感じるのは ip link set dev なコマンド達でしょうか。今回はこれをメインで bridge 設定をしていきたいと思います。

 
がっ!!

まず、 VRF の設定をしてルーティングテーブルを複数持つ環境を構築し、Docker コンテナを NAT なしで起動するための bridge 化。そしてっ!! 以前利用していた bridge-utils を捨てたのは Docker コンテナで IPv6 を利用するため。

と、いう、これはまさしく地獄絵図;-P。

 
どんな状態になっているか?と、いうと・・。

$ nmcli dev status
DEVICE           TYPE      STATE            CONNECTION      
eth0             ethernet  接続済み         eth0            
eth1             ethernet  接続済み         eth1            
eth2             ethernet  接続済み         eth2            
eth3             ethernet  接続済み         eth3            
eth4             ethernet  接続済み         eth4            
br0              bridge    接続済み (外部)  br0             
br1              bridge    接続済み (外部)  br1             
br4              bridge    接続済み (外部)  br4             
mgmt0            vrf       接続済み (外部)  mgmt0           
lb0              vrf       管理無し         --              
docker0          bridge    接続済み (外部)  docker0         
docker_gwbridge  bridge    接続済み (外部)  docker_gwbridge 
veth32347c8      ethernet  管理無し         --              
lo               loopback  管理無し         --              

 
多少並び直していますが、インターフェースはこれだけあります。結構多いほうだと思います;-)。

一応、表にしてみました。今回は絵はナシで・・。

物理 IF VRF bridge IPv4 IPv6 説明
eth0 br0 192.168.22.0/24 2001:470:fe36:face::/64 外部からのアクセス用で Docker ホストとコンテナで同一セグメント
eth1 br1 192.168.52.0/24 2001:470:fe36:cafe::/64 DB アクセスなどのバックヤード用で Docker ホストとコンテナで同一セグメント
eth2 192.168.111.0/24 2001:470:fe36:111::/64 同一ネットワーク上の NFS サーバへのアクセス用
eth3 mgmt0 192.168.1.0/24 2001:470:fe36:1::/64 外部からの ssh などのメンテナンス用
eth4 lb0 br4 192.168.202.0/24 ロードバランサ配下のためなし ロードバランサ用閉域セグメントで Docker ホストとコンテナで同一セグメント

 
セグメントはそれぞれ記載しましたが、インターフェースにつける実際のアドレスは 218 です。 IPv4/IPv6 共に 218 を設定しています。

 
Docker ホストにはインターフェースが全部で 5 個。
VRF を設定するルーティングテーブルは全部で 3 個。 eth0,eth1,eth2 が所属する default のルーティングテーブル。 eth3 はメンテナンス用なので mgmt0。 eth4 はロードバランサセグメントなので lb0。

5 個のインターフェースの内、Docker コンテナでも同一セグメントを利用するために bridge が存在していますが、それは 3 個。
Docker コンテナのネットワーク接続は色々なパータンを想定。

  • Service-Segment への接続みのウェブサーバ
  • Service-Segment と Access-Segment への接続する WordPress 用サーバ
  • LB-Segment への接続のみの負荷分散を考慮したウェブサーバ
  • LB-Segment と Access-Segment への接続するウェブアプリケーションサーバ

 
Service-Segment は外部からの直接アクセスを想定。 Access-Segmen はバックヤードで DB 接続や tomcat への接続を想定。 LB-Segment はロードバランサ配下の閉域セグメント。

ロードバランサは上位に apache を用意しましたが、上位のロードバランサからの閉域セグメントとして VRF を設定して、かつ、それを bridge して Docker コンテナに直接アクセスするように設定しました。

それにしても、フツー、ここまでするかねぇ?! みたいな;-)。

 
では、実際に設定を見てい行きましょう。

まず、 /etc/sysconfig/network-scripts/ 配下の ifcfg-eth* のファイルですが、フツーの設定で問題ありません。ルーティングテーブルが存在するインターフェースの設定ファイルのみに GATEWAY= や IPV6_DEFAULTGW= を指定してあげてください。
rule-eth* や route-eth* は今回は用意しませんでした。

 
1. VRF の設定
VRF の設定は /etc/rc.local 内に記載します。

ip link add dev mgmt0 type vrf table 10
ip link set dev mgmt0 up
ip link set dev eth3 master mgmt0
ip link set dev eth3 up
ip route add default via 192.168.1.1 table 10
ip route add ::/0 via 2001:470:fe36:1::1 table 10

ip link add dev lb0 type vrf table 4
ip link set dev lb0 up
ip link set dev eth4 master lb0
ip link set dev eth4 up
ip route add default via 192.168.202.1 table 4

route delete default
route delete default
route delete default
route add default gw 192.168.22.1

route delete -host 192.168.1.1
route delete -host 192.168.202.1

ip route del ::/0 via 2001:470:fe36:1::1
ip route del ::/0 via 2001:470:fe36:face::1
ip route del 2001:470:fe36:1::1/64
route -6 add default gw 2001:470:fe36:face::1

sysctl -w net.ipv4.tcp_l3mdev_accept=1
sysctl -w net.ipv4.udp_l3mdev_accept=1

 
こんな感じでしょうか。/etc/iproute2/rt_tables に table 4 とか 10 の名前を書いても良いですが、数値で管理する場合は不要です。

ルーティング設定を色々いじっているのは、ルーティングテーブルが美しくならないのでコマンドで正しいルーティング情報にしているためです。

こうなっているのが美しいかなぁ・・。と、思っています。

$ ip route show
default via 192.168.22.1 dev eth0 
192.168.22.0/24 dev eth0 proto kernel scope link src 192.168.22.218 metric 100 
192.168.52.0/24 dev eth1 proto kernel scope link src 192.168.52.218 metric 101 
192.168.111.0/24 dev eth2 proto kernel scope link src 192.168.111.218 metric 102

$ ip route show table 10
default via 192.168.1.1 dev eth3 
broadcast 192.168.1.0 dev eth3 proto kernel scope link src 192.168.1.218 
local 192.168.1.218 dev eth3 proto kernel scope host src 192.168.1.218 
broadcast 192.168.1.255 dev eth3 proto kernel scope link src 192.168.1.218 
 
$ ip route show table 4
default via 192.168.202.1 dev eth4
broadcast 192.168.202.0 dev eth4 proto kernel scope link src 192.168.202.218 
local 192.168.202.218 dev eth4 proto kernel scope host src 192.168.202.218 
broadcast 192.168.202.255 dev eth4 proto kernel scope link src 192.168.202.218 

$ ip -6 route show
::1 dev lo proto kernel metric 256 pref medium
2001:470:fe36:111::/64 dev eth2 proto kernel metric 102 pref medium
2001:470:fe36:cafe::/64 dev eth1 proto kernel metric 101 pref medium
2001:470:fe36:face::/64 dev eth0 proto kernel metric 100 pref medium
fe80::/64 dev eth0 proto kernel metric 1024 pref medium
fe80::/64 dev eth1 proto kernel metric 1024 pref medium
fe80::/64 dev eth2 proto kernel metric 1024 pref medium
fe80::/64 dev eth3 proto kernel metric 1024 pref medium
default via 2001:470:fe36:face::1 dev eth0 metric 1 pref medium

$ ip -6 route show table 10
anycast 2001:470:fe36:1:: dev eth3 proto kernel metric 0 pref medium
local 2001:470:fe36:1::218:1 dev eth3 proto kernel metric 0 pref medium
anycast fe80:: dev eth3 proto kernel metric 0 pref medium
local fe80::8785:99b6:f03b:cf5e dev eth3 proto kernel metric 0 pref medium
multicast ff00::/8 dev eth3 proto kernel metric 256 pref medium
default via 2001:470:fe36:1::1 dev eth3 metric 1024 pref medium

$ ip -d link show type vrf
7: mgmt0:  mtu 65575 qdisc noqueue state UP mode DEFAULT group default qlen 1000
    link/ether 56:64:d2:f2:ee:55 brd ff:ff:ff:ff:ff:ff promiscuity 0 minmtu 1280 maxmtu 65575 
    vrf table 10 addrgenmode eui64 numtxqueues 1 numrxqueues 1 gso_max_size 65536 gso_max_segs 65535 
8: lb0:  mtu 65575 qdisc noqueue state UP mode DEFAULT group default qlen 1000
    link/ether 2e:68:9e:22:5f:0e brd ff:ff:ff:ff:ff:ff promiscuity 0 minmtu 1280 maxmtu 65575 
    vrf table 4 addrgenmode eui64 numtxqueues 1 numrxqueues 1 gso_max_size 65536 gso_max_segs 65535 

 
VRF しているインターフェースのルーティング情報が乗っていると削除して、正しいルーティング情報に直すので roure del したり route add しています。上記になっているのが、僕は一番美しいルーティングテーブルだと思っているのであります。
この辺り、nmcli だとちゃんとやってくれるのかなぁ?

 
VRF は eth0,eth1,eth2 の default テーブルと、eth3 の mgmt0 なテーブル、そして、eth4 のロードバランサ用テーブルと、合計三つのデーブルを作成しました。外部からアクセスすると、おのおののインターフェースから入って、入ってきたところから出ていきます。

 
さてと。 VRF で複数のルーティングテーブルがあると、例えば自分のサーバから外部のサーバに ssh したい場合、default のルーティングテーブルから出ていってしまいます。

本来、メンテナンス用の eth3 のインターフェースがあるのですが、VRF しているのでフツーに ssh するとへんなことになります。そんなときはインターフェース指定でコマンドを実行するのが良いですね。 そんなときに利用するのが if vrf exec コマンドです。

$ traceroute -n 192.168.1.217
traceroute to 192.168.1.217 (192.168.1.217), 30 hops max, 60 byte packets
 1  192.168.22.1  0.416 ms  0.460 ms  0.456 ms
 2  192.168.1.217  0.659 ms  0.654 ms *
$ ip vrf exec mgmt0 traceroute -n 192.168.1.217
traceroute to 192.168.1.217 (192.168.1.217), 30 hops max, 60 byte packets
 1  192.168.1.1  0.405 ms  0.323 ms  0.324 ms
 2  192.168.1.217  0.665 ms  0.668 ms  0.687 ms
$ ip vrf exec mgmt0 ssh 192.168.1.217
>

 
上のは default のルーティングテーブルの gateway である 192.168.22.1 経由で出ていきますが、 ip vrf exec “VRF インターフェース名” を付加してコマンドを打つと eth3 の VRF mgmt0 の gateway を経由して通信できます。 FreeBSD 的には setfib コマンドがありますが、それと同等機能ですね。

VRF の設定はこれにて完了。これは以前書いたものとほぼ一緒かな。

 
2. bridge の設定
次に Docker ホストと Docker コンテナの間で同一のセグメントを利用できるようにする bridge の設定をします。一番上に書きましたが「Dockerを利用してWordPressを動作させます。それもブリッジを用いて。」のエントリの改訂版になります。

bridge-utils はもう捨てて ip link set dev コマンドを利用して設定します。

まず、bridge インターフェースを生成するための docker network create コマンドの実行から。

# docker network create Service-Segment \
     -o "com.docker.network.bridge.name"="br0" \
     --driver=bridge \
     --subnet 192.168.22.0/24 \
     --gateway 192.168.22.218 \
     --ipv6 \
     --subnet 2001:470:fe36:face::/64 \
     --gateway 2001:470:fe36:face::218:1

# ifconfig br0 inet6 del fe80::1/64

 
もう bridge-utils を捨てたので docker network create で生成するネットワークは IPv4/IPv6 のデアルスタクに対応にします。コンテナも IPv4/IPv6 のデアルスタク対応です;-)。

docker network create で –ipv6 を指定するとリンクローカルアドレスに fe80::1 が自動的に付加されます。fe80::1 はコンテナが IPv6 を利用したときの gateway になるんだそうな。強引に付加されます。

bridge なネットワークなので複数の Docker ホストがあった場合、全ての Docker ホストがリンクローカルアドレスに fe80::1 を利用すると IPv6 duplicate address fe80::1 てな感じになり、同一セグメント上のサーバ間の通信ができなくなります。なので、ifconfig br0 inet6 del して削除します。そもそも既に由緒正しいリンクローカルアドレスが付加されているので fe80::1 はまるっきり不要です。

次に br0 と物理インターフェース eth0 を bridge します。

# ip link set dev br0 up
# ip link set dev eth0 promisc on
# ip link set dev eth0 up
# ip link set dev eth0 master br0
# ifconfig eth0 0.0.0.0 up
# ifconfig eth0 inet6 del 2001:470:fe36:face::218:1/64 up

# route add default gw 192.168.22.1 dev br0
# ip route del ::/0 via 2001:470:fe36:face::1
# route -6 add default gw 2001:470:fe36:face::1 dev br0

 
上から順に説明すると、

  • docker network create で生成した br0 を UP します
  • eth0 のプロミスキャス・モードを有効化します
  • eth0 を UP します
  • eth0 と br0 をブリッジ化します
  • br0 に IPv4/IPv6 アドレスが付加されるので eth0 側から削除します
  • 最後にルーティング情報を設定します

こんな感じでしょうか。

ロードバランサセグメント用の設定も書いておきます。

# docker network create LB-Segment \
    -o "com.docker.network.bridge.name"="br4" \
    --driver=bridge \
    --subnet 192.168.202.0/24 \
    --gateway 192.168.202.218

# ip link set dev br4 up
# ip link set dev eth4 promisc on
# ip link set dev eth4 up
# ip link set dev eth4 master br4
# ifconfig eth4 0.0.0.0 up

# ip link set dev lb0 up
# ip link set dev br4 master lb0
# ip link set dev br4 up
# ip route add default via 192.168.202.1 table 4

 
こちらは閉域ネットワークです。でもって IPv6 がないのでシンプルです。外部からロードバランサ経由でアクセスがあったものは default gateway に戻っていきます。
ちなみに eth4, br4 の brige インターフェースは lb0 という VRF でもあるわけですが、bridge 生成時に VRF を意識する必要は全くありません。すげ;-)。

ちなみに eth1, br1 の組み合わせも bridge して docker network で Access-Segment として create しますが、今回は割愛します。

 
bridge を終了するコマンドも記載しておきます。

# ip link set eth0 promisc off
# ip link set eth0 down
# ip link set dev eth0 nomaster
# ip link delete br0 type bridge

# docker network rm Service-Segment

# ifconfig eth0 192.168.22.218/24
# ifconfig eth0 inet6 add 2001:470:fe36:face::218:1/64

# route add default gw 192.168.22.1 dev eth0
# ip route del ::/0 via 2001:470:fe36:face::1
# route -6 add default gw 2001:470:fe36:face::1 dev eth0

# ping -q -c 3 192.168.22.1 > /dev/null 2>&1

 
こちらも一応、説明しておきます。

  • eth0 のプロミスキャス・モードをオフにします
  • eth0 を DOWN してから eth0 と br0 の bridge を削除します
  • br0 を削除するので docker network rm を実行します
  • eth0 に IPv4/IPv6 アドレスを付加します
  • ルーティング情報を更新します
  • 最後に ping を打つのは上位のルータが保持している古い MAC アドレスを書き換えるため

こんな感じでしょうか。

これで VRF でルーティングテーブルが個別なネットワークを bridge して Docker コンテナが利用できる環境が整いました。

 
3. IPv4/IPv6 デアルスタク対応コンテナの起動
最後に Docker コンテナの使い方について書いておきます。何回も書いている通り bridge-utils を捨てて ip link set dev コマンドに移行したので Docker コンテナは IPv4/IPv6 のデアルスタクで動作させられます。

起動はこんな感じで。もっと複雑な起動方法については「Dockerを利用してWordPressを動作させます。それもブリッジを用いて。」を参照してください。

$ docker run -d \
 --net=Service-Segment --ip=192.168.22.246 --ip6=2001:470:fe36:face::218:246 \
 -v /opt/docker/contents/web-service01/html:/var/www/html \
 -v /opt/docker/contents/web-service01/conf.d:/etc/httpd/conf.d \
 -v /opt/docker/contents/web-service01/logs:/var/log/httpd \
 --log-driver=syslog --log-opt syslog-facility=local3 --log-opt tag=docker/{{.Name}}/{{.ID}} \
 --name web-service01 \
 web-service01:1 /usr/sbin/httpd -DFOREGROUND
$
$ docker exec -it web-service01 /bin/bash
[root@3383ac655b6b /]# ifconfig eth0
eth0: flags=4163  mtu 1500
        inet 192.168.22.246  netmask 255.255.255.0  broadcast 192.168.22.255
        inet6 2001:470:fe36:face::217:246  prefixlen 64  scopeid 0x0
        inet6 fe80::42:c0ff:fea8:16f6  prefixlen 64  scopeid 0x20
        ether 02:42:c0:a8:16:f6  txqueuelen 0  (Ethernet)
        RX packets 43  bytes 4322 (4.2 KiB)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 9  bytes 806 (806.0 B)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0
[root@3383ac655b6b /]# exit
$

 
Docker ホストと Docker コンテナの間に NAT が入ってない (それはつまりは docker0 インターフェースを利用していない) 純粋な IPv4/IPv6 アドレスを利用したコンテナへのアクセスができるようになりました。 docker0 インターフェースを利用してないので /etc/docker/daemon.json への ipv6 true や fixed-cidr-v6 の設定も不要です。

思う存分 IPv4/IPv6 デアルスタクな Docker コンテナを楽しめる環境が整いました;-)。

IPv4 は NAT だけど IPv6 は腐るほど持っているので、Docker コンテナでも IPv6 が利用できるほうが良いですよねぇ;-)。

ちなみにですが、拾ってきた Docker イメージのうち portainer, registry, docker-registry-frontend などは、根こそぎ IPv6 でのアクセスが可能です;-)。

 
さてと。今回のお題目。

ポリシールーティングではなく VRF してルーティングテーブルをわけてから bridge して Docker ホストと Docker コンテナで同一ネットワークを利用し、更に IPv4/IPv6 のデアルスタクで Docker コンテナを運用する。

と、いうのができました。僕としてはもう、ほぼほぼこれで満足です。最終目的かもしれないです。ふぅ。

まぁ、実装するのは簡単で、これを実運用に持っていくには対障害対応とか色々必要となってくるのですが、色々と設定が必要そうです。

あと、Swarm を利用を利用して、当該 の Docker ホストがダウンしたときはコンテナは移動し、ダウンした Docker ホストのネットワーク環境などを整え終わったら再度サービスに復活させるとかの対応も必要かも。

 
ひとまずこれでヨシとしよう。

ふぅ。楽しかった;-)。

 コメントを書いてください。

HTML タグが利用できます: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>

(必須項目)

(必須項目)

日本語が含まれない投稿は無視されますのでご注意ください。(スパム対策)

このサイトはスパムを低減するために Akismet を使っています。コメントデータの処理方法の詳細はこちらをご覧ください