ネットワーク初心者の新卒がDockerでネットワークの勉強をしてみた | サイバーエージェント 公式エンジニアブログ
この記事はCyberAgent エンジニア Advent Calendar 2015の8日目の記事です
タイトルは5日目の「Scala初心者の新卒が頑張ってLispを作ってみた」のパクりです。

こんにちは!サイバーエジェント アドテクスタジオ新卒の黒崎 (@kuro_m88) と申します。
Dynalystというチームに配属され、一人前のサーバサイドエンジニアになるべく修行をしています(`・ω・´)最近はScalaを書くことが多くて、Sparkで大量のログを集計するバッチの開発をしています。ほぼ100%AWSで構成されているプロダクトなので業務でネットワークの運用もしませんし、構成の事を意識する事はあまりありません。
そんな中でネットワークの事が知りたくなったのは、広告の配信に関わる開発をしてみて、広告の配信の仕組み自体も技術的にめちゃくちゃ面白いんですが、それと同時に自分たちのサーバから広告が表示される端末までの間がどうなってるのか気になってしまったのがきっかけです。学んでいくにはやはり手を動かすのが一番ですよねo(((^^)))o

ということで手軽にネットワーク(特にルーティング)について勉強できそうな環境を作ったので、その紹介をします
今回Dockerを使ったのは、気軽に実験しようと思うと実機はもちろん用意できませんし、ノートPCでVMを立てるにしてもメモリの都合上ルータを10台立てるとかは厳しいけど、最近流行りのコンテナならいけそうな気がしたからです。

環境構築
仮想マシンを用意する
Ubuntu15.10を使いました。
私の場合はノートPC上にvagrantでVMを用意しました。
VMに割り当てるリソースは、CPU 1~2コア、メモリは1GBあれば足りると思います。

dockerをインストール
vagrant@vagrant-ubuntu-wily-64:~$ wget -qO- https://get.docker.com/ | sh
バージョンはサーバ、クライアントともに1.9.1でした。

open vSwitchをインストール
open vSwitchをパッケージからインストールします。
vagrant@vagrant-ubuntu-wily-64:~$ sudo aptitude install openvswitch-common openvswitch-switch

バージョンは2.4.0が入りました。

これだけで環境構築は終わりです。簡単ですね!

作りたいネットワーク

こんな感じのネットワークを構築します。

作りたいネットワーク


ルータを用意する

vyOSというオープンソースのルータOSがあって、今回はこれを利用しています。
DockerコンテナはDocker Hubに用意しました
vyOSが動くDockerコンテナの作り方がきになる方はこちらをご覧ください
4台欲しいので、vyOSのコンテナを名前を変えて4個作ります。
NICは手動で追加するので、--net=noneをつけます。

vagrant@vagrant-ubuntu-wily-64:~$ sudo docker run -d --name router1 --net=none --privileged -v /lib/modules:/lib/modules kurochan/vyos:1.1.6 /sbin/init
vagrant@vagrant-ubuntu-wily-64:~$ sudo docker run -d --name router2 --net=none --privileged -v /lib/modules:/lib/modules kurochan/vyos:1.1.6 /sbin/init
vagrant@vagrant-ubuntu-wily-64:~$ sudo docker run -d --name router3 --net=none --privileged -v /lib/modules:/lib/modules kurochan/vyos:1.1.6 /sbin/init
vagrant@vagrant-ubuntu-wily-64:~$ sudo docker run -d --name router4 --net=none --privileged -v /lib/modules:/lib/modules kurochan/vyos:1.1.6 /sbin/init
docker psコマンドを打つとコンテナが4つ立ったのが分かります。
vagrant@vagrant-ubuntu-wily-64:~$ sudo docker ps
CONTAINER ID        IMAGE                 COMMAND             CREATED             STATUS              PORTS               NAMES
99a121b7984a        kurochan/vyos:1.1.6   "/sbin/init"        2 seconds ago       Up 2 seconds                            router4
3599cf751e0c        kurochan/vyos:1.1.6   "/sbin/init"        8 seconds ago       Up 8 seconds                            router3
035e17a8fd43        kurochan/vyos:1.1.6   "/sbin/init"        15 seconds ago      Up 14 seconds                           router2
66bb97a046c5        kurochan/vyos:1.1.6   "/sbin/init"        21 seconds ago      Up 20 seconds                           router1

スイッチを用意する
ルータのNIC同士を接続するために、スイッチを用意します。
物理機材であれば1つのスイッチに接続してVLANでセグメントを分けたりするのかなと思いますが、仮想スイッチなので、セグメントの数だけ用意してしまいます。

vagrant@vagrant-ubuntu-wily-64:~$ sudo ovs-vsctl add-br switch1
vagrant@vagrant-ubuntu-wily-64:~$ sudo ovs-vsctl add-br switch2
vagrant@vagrant-ubuntu-wily-64:~$ sudo ovs-vsctl add-br switch3
vagrant@vagrant-ubuntu-wily-64:~$ sudo ovs-vsctl add-br switch4
ルータ同士を接続する
さて、ルータとスイッチ用意できましたが、まだルータは接続はおろかNICすら持っていないので何もできません。
図にするとこんな感じでしょうか(笑)

現状


ここでopen vSwitchのパッケージに含まれている、ovs-dockerというコマンドを使います。
このコマンド、実体としては約300行程度のシェルスクリプトなのですがとても便利で、Dockerコンテナに対してNICを追加すると同時にIPアドレスとサブネットマスクが設定できて、さらにはvSwitchに接続するところまでできます。
各ルータにスイッチとの接続の設定をしていきます。

vagrant@vagrant-ubuntu-wily-64:~$ sudo ovs-docker add-port switch1 eth0 router1 --ipaddress=10.0.1.1/24
vagrant@vagrant-ubuntu-wily-64:~$ sudo ovs-docker add-port switch2 eth1 router1 --ipaddress=10.0.2.1/24
vagrant@vagrant-ubuntu-wily-64:~$ sudo ovs-docker add-port switch1 eth0 router2 --ipaddress=10.0.1.2/24
vagrant@vagrant-ubuntu-wily-64:~$ sudo ovs-docker add-port switch3 eth1 router2 --ipaddress=10.0.3.1/24
vagrant@vagrant-ubuntu-wily-64:~$ sudo ovs-docker add-port switch2 eth0 router3 --ipaddress=10.0.2.2/24
vagrant@vagrant-ubuntu-wily-64:~$ sudo ovs-docker add-port switch4 eth1 router3 --ipaddress=10.0.4.1/24
vagrant@vagrant-ubuntu-wily-64:~$ sudo ovs-docker add-port switch3 eth0 router4 --ipaddress=10.0.3.2/24
vagrant@vagrant-ubuntu-wily-64:~$ sudo ovs-docker add-port switch4 eth1 router4 --ipaddress=10.0.4.2/24

間違えてNICを追加してしまった場合はdel-portサブコマンドでNICの削除ができます。

pingを打ってみる

router1からrouter2(10.0.1.2)に向けてpingを打つには、

vagrant@vagrant-ubuntu-wily-64:~$ sudo docker exec -it router1 ping -c 2 10.0.1.2
PING 10.0.1.2 (10.0.1.2) 56(84) bytes of data.
64 bytes from 10.0.1.2: icmp_req=1 ttl=64 time=0.227 ms
64 bytes from 10.0.1.2: icmp_req=2 ttl=64 time=0.058 ms

pingが通りましたね!
では、router1からrouter4(10.0.3.2)にpingを打ってみましょう。

vagrant@vagrant-ubuntu-wily-64:~$ sudo docker exec -it router1 ping -c 2 10.0.3.2
connect: Network is unreachable
pingが通りませんね(´・ω・`)

関係をまとめると、以下のようになります。

router1 => router2(10.0.1.2): o
router1 => router3(10.0.2.2): o
router1 => router4(10.0.3.2): x
router2 => router3(10.0.4.1): x
router2 => router4(10.0.3.2): o
router3 => router4(10.0.4.2): o

現状ではお隣さんにしかpingが飛ばないようです

ルーティング
ルータ同士はお互いに経路を知らないため、何かしらの方法で教えてあげないといけません。
はじめてのルーティングと言えばstaticルーティングをしてみるのが一般的だと思うのですが、今回は省略します。
ちなみにstaticルーティングは新卒研修でひたすら入力して散々な目に遭いました…手作業って事故ると大変ですよね(´;ω;`)
今回はOSPFというプロトコルを使うように各ルータに設定を打ち込んでいきます!
ルータはコンテナなのでdocker execコマンドを使って、コンテナ内で直接vbashというシェルを立ち上げることで設定をします。
最後に設定を反映させるためにcommitしてから設定が消えないようにsaveするのが大切です。
router1に設定するときの例です。

vagrant@vagrant-ubuntu-wily-64:~$ sudo docker exec -it router1 /bin/vbash
vbash-4.1# su - vyos
vyos@vyos:~$ configure
[edit]
vyos@vyos# set interfaces loopback lo address 1.1.1.1/32
[edit]
vyos@vyos# set protocols ospf area 0 network 10.0.0.0/16
[edit]
vyos@vyos# set protocols ospf parameters router-id 1.1.1.1
[edit]
vyos@vyos# commit
[edit]
vyos@vyos# save
Saving configuration to '/config/config.boot'...Done
[edit]
vyos@vyos# exit
exit
vyos@vyos:~$ exit
logout
vbash-4.1# exit
exit
以下、同様にして他のルータにも設定を入れますが、長くなってしまうのでsetコマンドの部分だけを列挙します。

router1
set interfaces loopback lo address 1.1.1.1/32
set protocols ospf area 0 network 10.0.0.0/16
set protocols ospf parameters router-id 1.1.1.1

router2
set interfaces loopback lo address 2.2.2.2/32
set protocols ospf area 0 network 10.0.0.0/16
set protocols ospf parameters router-id 2.2.2.2

router3
set interfaces loopback lo address 3.3.3.3/32
set protocols ospf area 0 network 10.0.0.0/16
set protocols ospf parameters router-id 3.3.3.3
router4
set interfaces loopback lo address 4.4.4.4/32
set protocols ospf area 0 network 10.0.0.0/16
set protocols ospf parameters router-id 4.4.4.4

ルーティングテーブルを見てみる

OSPFを使ってルータ同士が経路を交換し始めていれば、ルーティングテーブルになにかが表示されているはずです。
show ip routeコマンドで確認できるようなので、確認してみましょう。
vagrant@vagrant-ubuntu-wily-64:~$ sudo docker exec -it router1 /bin/vbash
vbash-4.1# su - vyos
vyos@vyos:~$ show ip route
WARNING: terminal is not fully functional
Codes: K - kernel route, C - connected, S - static, R - RIP, O - OSPF,
       I - ISIS, B - BGP, > - selected route, * - FIB route

C>* 1.1.1.1/32 is directly connected, lo
O   10.0.1.0/24 [110/10] is directly connected, eth0, 00:05:53
C>* 10.0.1.0/24 is directly connected, eth0
O   10.0.2.0/24 [110/10] is directly connected, eth1, 00:05:53
C>* 10.0.2.0/24 is directly connected, eth1
O>* 10.0.3.0/24 [110/20] via 10.0.1.2, eth0, 00:02:23
O>* 10.0.4.0/24 [110/20] via 10.0.2.2, eth1, 00:01:33
C>* 127.0.0.0/8 is directly connected, lo
それっぽい項目が表示されていますね。

再びpingを打ってみる

さきほどpingを打って返ってこなかったケースで試してみます。
vagrant@vagrant-ubuntu-wily-64:~$ sudo docker exec -it router1 ping -c 2 10.0.3.2
PING 10.0.3.2 (10.0.3.2) 56(84) bytes of data.
64 bytes from 10.0.3.2: icmp_req=1 ttl=63 time=0.363 ms
64 bytes from 10.0.3.2: icmp_req=2 ttl=63 time=0.091 ms
おっ!今度はpingが帰ってきました!
念のためさきほどと同じ組み合わせをやってみましょう。

router1 => router2(10.0.1.2): o
router1 => router3(10.0.2.2): o
router1 => router4(10.0.3.2): o
router2 => router3(10.0.4.1): o
router2 => router4(10.0.3.2): o
router3 => router4(10.0.4.2): o
経路が交換できているため、pingが通りました!


もう一台追加してみる

もう一台ルーターが追加されると、どんな変化があるのか試してみたいと思います。
構成はこんな感じにします。

ルータ追加

以下のコマンドで追加 & 接続ができます。router4のNICも1つ増えます。
vagrant@vagrant-ubuntu-wily-64:~$ sudo docker run -d --name router5 --net=none --privileged -v /lib/modules:/lib/modules kurochan/vyos:1.1.6 /sbin/init
vagrant@vagrant-ubuntu-wily-64:~$ sudo ovs-vsctl add-br switch5
vagrant@vagrant-ubuntu-wily-64:~$ sudo ovs-docker add-port switch5 eth2 router4 --ipaddress=10.0.5.1/24
vagrant@vagrant-ubuntu-wily-64:~$ sudo ovs-docker add-port switch5 eth0 router5 --ipaddress=10.0.5.2/24
コンテナだとすぐ構成が変えられて色々試しやすいですね!

router5
set interfaces loopback lo address 5.5.5.5/32
set protocols ospf area 0 network 10.0.0.0/16
set protocols ospf parameters router-id 5.5.5.5
set protocols ospf default-information originate always
最後に1行だけ他と違う設定を入れてみます。

router1から経路を確認してみると、
vyos@vyos:~$ show ip route
WARNING: terminal is not fully functional
Codes: K - kernel route, C - connected, S - static, R - RIP, O - OSPF,
       I - ISIS, B - BGP, > - selected route, * - FIB route

O>* 0.0.0.0/0 [110/1] via 10.0.1.2, eth0, 00:01:46
  *                   via 10.0.2.2, eth1, 00:01:46
C>* 1.1.1.1/32 is directly connected, lo
O   10.0.1.0/24 [110/10] is directly connected, eth0, 01:07:55
C>* 10.0.1.0/24 is directly connected, eth0
O   10.0.2.0/24 [110/10] is directly connected, eth1, 01:07:55
C>* 10.0.2.0/24 is directly connected, eth1
O>* 10.0.3.0/24 [110/20] via 10.0.1.2, eth0, 01:04:25
O>* 10.0.4.0/24 [110/20] via 10.0.2.2, eth1, 01:03:35
O>* 10.0.5.0/24 [110/30] via 10.0.1.2, eth0, 00:42:41
  *                      via 10.0.2.2, eth1, 00:42:41
C>* 127.0.0.0/8 is directly connected, lo

10.0.5.0/24への経路が増えましたね!
0.0.0.0/0への経路も増えました。デフォルトルートが広報されたようです。
同じ宛先への経路が2つ並んで表示されているのはrouter1からrouter5へのコストが等しい経路が2つあるからです。

デフォルトルートが広報されているので、ルーティングテーブルにマッチしないパケットはすべてrouter5へ吸い込まれます。
router1からネットワーク内に存在しないIPアドレスに向けてtracerouteすると
vyos@vyos:~$ traceroute 10.0.123.123
traceroute to 10.0.123.123 (10.0.123.123), 30 hops max, 60 byte packets
 1  10.0.2.2 (10.0.2.2)  0.072 ms  0.025 ms  0.014 ms
 2  10.0.4.2 (10.0.4.2)  0.055 ms  0.023 ms  0.023 ms
 3  10.0.5.2 (10.0.5.2)  0.047 ms !N  0.033 ms !N *
確かにrouter5へ向かっていますね!

最後に
かなり端折って書いてしまいましたが、Dockerで基礎的なネットワークを構築してみた紹介でした。
本当はBGPというプロトコルの勉強をして記事にしたかったのですが、検証が間に合わず、、、
ここから先は冬休み?の宿題にしたいと思います(´・ω:;.:… 

明日はk_enokiさんです!