【連載】世界一わかりみが深いコンテナ & Docker入門 〜 その5:Dockerのネットワークってどうなってるの? 〜

コンテナ・Docker
◆ Live配信スケジュール ◆
サイオステクノロジーでは、Microsoft MVPの武井による「わかりみの深いシリーズ」など、定期的なLive配信を行っています。
⇒ 詳細スケジュールはこちらから
⇒ 見逃してしまった方はYoutubeチャンネルをご覧ください
【4/18開催】VSCode Dev Containersで楽々開発環境構築祭り〜Python/Reactなどなど〜
Visual Studio Codeの拡張機能であるDev Containersを使ってReactとかPythonとかSpring Bootとかの開発環境をラクチンで構築する方法を紹介するイベントです。
https://tech-lab.connpass.com/event/311864/

こんにちは、サイオステクノロジー武井です。いよいよ佳境に入ってきた連載「世界一わかりみが深いコンテナ & Docker入門 」ですが、今回はDockerのネットワークです。

全7回シリーズでお届けする予定で、今回は第5回目となります。

  1. その1:コンテナってなに?
  2. その2:Dockerってなに?
  3. その3:Dockerfileってなに?
  4. その4:docker-composeってなに?
  5. 今回はこちら → その5:Dockerのネットワークってどうなってるの?
  6. その6:Dockerのファイルシステムってどうなってるの?
  7. その7:実践!!Dockerでアプリケーション開発!!(執筆中)

Dockerのネットワークってどうなってるの?

今までDockerfileやdocker-composeでコンテナを作成した際、コンテナ間で通信できたり、コンテナから外部のネットワークへ通信できたりしましたが、今回はそのメカニズムについて説明します。

Dockerのネットワークは、大別しますと、none、host、bridgeという3つの構成を取ることができます。ただし、まず基本を理解するためには、最もよく使われる方式を説明するのが一番かと思いますので、Dockerのデフォルトネットワークであり最もよく使われているbridgeを説明します。

で、Dockerのネットワーク構成ですが、コンテナを作成すると、PCの中には以下のような構成が出来上がります。

コンテナには仮想NICがついています。そして、それが仮想スイッチに接続されています。そして仮想スイッチにはもう一つの仮想NICが接続されていて、OSがもつルーティングの機能により、仮想NICあてのパケットは物理NICにルーティングされます。そして、インターネットなどの外部ネットワークに出ていきます。

「仮想スイッチ」「仮想NIC」などの言葉が出てきますが、これはLinuxのOSが仮想的なNICやスイッチを作り出す機能を持っています。便利ですよね。これらの仮想なんたらによって、L2の通信に必要なEthernetプロトコルをPCの内部でしゃべることができます。こういう便利な機能があるので、コンテナ側で稼働するOSは、物理サーバーで動作してたときと全く同じように振る舞うことができます。

ちょっとまだしっくりこない人もいるかもしません。特に仮想スイッチに接続されている仮想NICで仮想NIC単体であって、どのコンテナにも接続されていない宙ぶらりんなNICがあります。私は最初この意味がわかりませんでした。

こういう場合は、従来のテクノロジーで実現した場合と比較してみるのがいいと思います。

ということで、先のネットワーク構成を物理サーバーで実現した場合と比較してみることにしました(図がちっちゃいのでクリックして拡大して見て下さい)。

 

上が先程ご紹介したDockerのネットワーク構成図、下がそれを物理環境に置き直した図です。上図の上半分「Dockerで構築した環境」にある「コンテナ1」「コンテナ2」が、それぞれ下半分「物理環境に置き直した図」の「サーバ1」「サーバ2」に相当します。仮想スイッチは物理スイッチに、仮想NICは物理NICに相当します。

つまり、物理環境でいうところの、物理スイッチにサーバーが2台つながっていて、さらに物理スイッチからPCの2つあるNICの方の片方につながっていて、PC内でルーティングして、もう一方の物理NIC経由でインターネットに出ていくのですが、これをPCの内部で仮想的にエミュレートしたのが「Dockerで構築した環境」なのです。

いかがでしょうか?なんとなく雰囲気がつかめてきましたでしょうか?私はこのように考えると、とても頭がスッキリしました。

どうやって実現しているの?

では、Dockerで利用される仮想的なネットワークを作成するためには、3つの技術が必要になります。順に紹介していきます。

Network Namespace

ルーティングテーブルなどネットワークを動作させるのに必要な機能が完全に分離された環境を作るためのものです。

なかなかに説明だけではわかりにくいので図を交えてみます。このようなややこしいことを理解するためには、「それがない世界」と「それがある世界」で比較してみるのがいいと思います。ということで、「Network Namespaceがない世界」と「Network Namespaceがある世界」を比べてみたいと思います。

まずは、「Network Namespaceがない世界」です。そう言えば、Dockerのネットワークでは、複数のネットワークインターフェースをPC内に作りたいのでした。では仮想NICを作ってみましょう(仮想NICの作り方は後ほどご説明します)。

Network Namespaceがない世界では、仮想NICはLinuxのルーティング機能を使って、ルーティングされてしまいます。それでは困ります。

 

だって、最終的に作りたいのは以下のようなネットワークです。以下のネットワークでは、コンテナ内の仮想NICは仮想スイッチに接続します。ルーティングされて物理NICにパケットが届くようなことがあってはいけないのです。

 

なので、以下のようにNetwork Namespaceで区切ると独立したネットワーク空間が作成できます。

Network Namespaceはip netnsコマンドで作成することができます。カーネルが持っている機能です。特に深いことは考えずに、Linuxのカーネルはこういう機能を持っているっていう理解で十分と思います。そして、後ほど実際にNetwork Namespaceを作ってみます。実際に手を動かすと、より理解が深まりますので、いまのところぼやっとした理解でも大丈夫ですので、ご安心下さい。

仮想スイッチ

PC内で物理のL2スイッチと同じように振る舞うのが仮想スイッチと言われるものです。これもカーネルが持っている機能で、ip linkコマンドで作成することができます。

この仮想スイッチは物理のL2スイッチと同じようにEthernetのプロトコルをしゃべることができます。なので、コンテナの中にあるOSは、物理サーバーの中で稼働するときと同じように振る舞うことができるのです。コンテナのためのネットワークのために新たなプロトコルで通信する必要はありません。物理環境で動いていたときのものをPCの中でエミュレートしているので、コンテナの中にあるOSは、その稼働環境が物理環境なのかコンテナなのかを意識する必要はありません。

仮想ネットワークインターフェース

先程の図の中で、「仮想NIC」と書いてあったものです。物理ネットワークで動いているEthernetを仮想的に再現するためには、先程ご紹介した仮想スイッチだけではなく、仮想NICも必要です。これもLinuxのカーネルが持っている機能で作ることができます。これもip link addコマンドで作成することができます。

実際にネットワークを作ってみよう!!

では、先程ご紹介した3つの技術「Network Namespace」「仮想スイッチ」「仮想ネットワークインターフェース」を使って、ネットワークを作ってみましょう。実際にDockerでネットワークを作成するときも、これらの技術が使われていますが、Dockerコマンドを使うと、Dockerが勝手に裏側でやってくれるので、普段は意識しなくてもよいです。でも、Dockerのネットワークへの理解を深めるためには、やっぱり、低レベル(?)のコマンドを使ってやってみるのが一番です。では、やってみましょう。

まず、ゴールを確認します。最終的に作りたいのは以下のようなネットワークです。

 

では、順を追って構築してみます。最初は何にもありません。

Network Namespaceの作成

まずNetwork Namespaceを2つ作ります。Network Namespace周りのコマンドは、ip netnsというのが基本で、Network Namespaceを追加するためには、ip netns addというコマンドを実行します。書式は以下のとおりです。

ip netns add [Network Namespace名]

ということで、ns1、ns2という名前のNetwork Namespaceを作成します。

# ip netns add ns1
# ip netns add ns2

できあがったNamespaceの中でコマンドを実行して、中の状態がどの様になっているかを見てみます。書式は以下のとおりです。

ip netns exec [Network Namespace名] [コマンド]

Network Namespaceの中で、ip lコマンドを実行してインターフェースの状態を確認します。

# ip netns exec ns1 ip l
1: lo: <LOOPBACK> mtu 65536 qdisc noop state DOWN mode DEFAULT group default qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00

 

Loopbackインターフェースしかないようです。しかもDownしていますので、Upしましょう。

# ip netns exec ns1 ip link set lo up
# ip netns exec ns2 ip link set lo up

 

ということで現在の状態は以下の通りとなりました。ns1、ns2というNetwork Namespaceの中にLoopbackインターフェースが2つある状態です。

仮想スイッチの作成

仮想スイッチを作成します。これは特に難しいことはありません。コマンドの書式は以下のとおりです。

ip link add [仮想スイッチ名] type bridge

 

では、早速作ってみましょう。

# ip link add br0 type bridge

 

できているかどうか確認してみます。

# ip l
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN mode DEFAULT group default qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP mode DEFAULT group default qlen 1000
    link/ether 00:1c:42:d6:34:95 brd ff:ff:ff:ff:ff:ff
18: br0: <BROADCAST,MULTICAST> mtu 1500 qdisc noop state DOWN mode DEFAULT group default qlen 1000
    link/ether fe:39:fb:4e:ea:a7 brd ff:ff:ff:ff:ff:ff

 

問題なくできてますね。そして今の状態は以下の図のとおりです。

仮想ネットワークインターフェースの作成

仮想ネットワークインターフェース(以下仮想NIC)をします。先ほど作成したNetwork NamespaceはLoopbackインターフェースしかないですし、今後作成する仮想スイッチにもネットワークインターフェースが必要です。

ということで、今回のゴールをまず確認します。今回のゴールは下図の赤枠の部分の仮想NICを作ることです。

では作ってみます。仮想NICは必ずペアで作成します。コマンドの書式は以下のとおりです。

ip link add name veth [仮想NIC名] type veth peer name [対向の仮想NIC名]

今回は以下の名前の仮想NICを作成します。

ns-veth1
先程作成したNetwork Namespaceであるns1の方に接続される仮想NIC
br-veth1ns-veth1とペアで作成され、仮想スイッチに接続される仮想NIC
ns-veth2
先程作成したNetwork Namespaceであるns2の方に接続される仮想NIC
br-veth2
ns-veth2とペアで作成され、仮想スイッチに接続される仮想NIC
rt-veth
物理NICとルーティングされる仮想NIC
br-veth3
rt-vethとペアで作成され、仮想スイッチに接続される仮想NIC

 

ということで以下のコマンドを実施します。

# ip link add name ns-veth1 type veth peer name br-veth1
# ip link add name ns-veth2 type veth peer name br-veth2
# ip link add name rt-veth type veth peer name br-veth3

 

できたかどうか確認してみましょう。

# ip l
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN mode DEFAULT group default qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP mode DEFAULT group default qlen 1000
    link/ether 00:1c:42:d6:34:95 brd ff:ff:ff:ff:ff:ff
12: br-veth1@ns-veth1: <BROADCAST,MULTICAST,M-DOWN> mtu 1500 qdisc noop state DOWN mode DEFAULT group default qlen 1000
    link/ether fe:30:c8:94:51:21 brd ff:ff:ff:ff:ff:ff
13: ns-veth1@br-veth1: <BROADCAST,MULTICAST,M-DOWN> mtu 1500 qdisc noop state DOWN mode DEFAULT group default qlen 1000
    link/ether c6:ab:0a:b3:e4:c4 brd ff:ff:ff:ff:ff:ff
14: br-veth2@ns-veth2: <BROADCAST,MULTICAST,M-DOWN> mtu 1500 qdisc noop state DOWN mode DEFAULT group default qlen 1000
    link/ether a2:75:8a:a3:d8:7e brd ff:ff:ff:ff:ff:ff
15: ns-veth2@br-veth2: <BROADCAST,MULTICAST,M-DOWN> mtu 1500 qdisc noop state DOWN mode DEFAULT group default qlen 1000
    link/ether f6:e6:d6:a7:b5:a2 brd ff:ff:ff:ff:ff:ff
16: br-veth3@rt-veth: <BROADCAST,MULTICAST,M-DOWN> mtu 1500 qdisc noop state DOWN mode DEFAULT group default qlen 1000
    link/ether 16:7b:be:f8:dd:1b brd ff:ff:ff:ff:ff:ff
17: rt-veth@br-veth3: <BROADCAST,MULTICAST,M-DOWN> mtu 1500 qdisc noop state DOWN mode DEFAULT group default qlen 1000
    link/ether 5a:2e:38:61:6c:3a brd ff:ff:ff:ff:ff:ff
18: br0: <BROADCAST,MULTICAST> mtu 1500 qdisc noop state DOWN mode DEFAULT group default qlen 1000 link/ether fe:39:fb:4e:ea:a7 brd ff:ff:ff:ff:ff:ff

 

問題なくできてます。現在の状態は以下のとおりです。必要な仮想NICのペアは作成されました。しかし、どこにも接続されていません。次は、これらの仮想NICをNetwork Namespaceや仮想スイッチに接続する作業を行います。

Network Namespaceへの仮想ネットワークインターフェースの接続

Network Namespaceのns1、ns2に仮想NICを接続します。コマンドの書式は以下のとおりです。

ip link set [仮想NIC名] netns [Network Namespace名]

 

以下のコマンドを実行します。

# ip link set ns-veth1 netns ns1
# ip link set ns-veth2 netns ns2

 

確認してみましょう。先程までPC上で見えていた仮想NICのns-veth1とns-veth2が消えてしまいました。どこへ行ってしまったのでしょうか?

# ip l
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN mode DEFAULT group default qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP mode DEFAULT group default qlen 1000
    link/ether 00:1c:42:d6:34:95 brd ff:ff:ff:ff:ff:ff
12: br-veth1@if13: <BROADCAST,MULTICAST> mtu 1500 qdisc noop state DOWN mode DEFAULT group default qlen 1000
    link/ether fe:30:c8:94:51:21 brd ff:ff:ff:ff:ff:ff link-netnsid 0
14: br-veth2@if15: <BROADCAST,MULTICAST> mtu 1500 qdisc noop state DOWN mode DEFAULT group default qlen 1000
    link/ether a2:75:8a:a3:d8:7e brd ff:ff:ff:ff:ff:ff link-netnsid 1
16: br-veth3@rt-veth: <BROADCAST,MULTICAST,M-DOWN> mtu 1500 qdisc noop state DOWN mode DEFAULT group default qlen 1000
    link/ether 16:7b:be:f8:dd:1b brd ff:ff:ff:ff:ff:ff
17: rt-veth@br-veth3: <BROADCAST,MULTICAST,M-DOWN> mtu 1500 qdisc noop state DOWN mode DEFAULT group default qlen 1000
    link/ether 5a:2e:38:61:6c:3a brd ff:ff:ff:ff:ff:ff
18: br0: <BROADCAST,MULTICAST> mtu 1500 qdisc noop state DOWN mode DEFAULT group default qlen 1000
    link/ether fe:39:fb:4e:ea:a7 brd ff:ff:ff:ff:ff:ff

 

Network Namespaceの中を見てみますと、やはり先程のns-veth1とns-veth2はNetwork Namespaceの中に移動していることがわかります。これでバッチリです。

# ip netns exec ns1 ip l
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN mode DEFAULT group default qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
13: ns-veth1@if12: <BROADCAST,MULTICAST> mtu 1500 qdisc noop state DOWN mode DEFAULT group default qlen 1000
    link/ether c6:ab:0a:b3:e4:c4 brd ff:ff:ff:ff:ff:ff link-netnsid 0
# ip netns exec ns2 ip l
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN mode DEFAULT group default qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
15: ns-veth2@if14: <BROADCAST,MULTICAST> mtu 1500 qdisc noop state DOWN mode DEFAULT group default qlen 1000
    link/ether f6:e6:d6:a7:b5:a2 brd ff:ff:ff:ff:ff:ff link-netnsid 0

 

そいで、今の状態は以下のような感じです。

仮想スイッチへの仮想ネットワークインターフェースの接続

次は先程Network Namespaceに接続した仮想NICの対向の仮想NICを仮想スイッチに接続しましょう。コマンドの書式は以下のとおりです。

ip link set dev [仮想NIC名] master [仮想スイッチ名]

 

早速やってみましょう。

# ip link set dev br-veth1 master br0
# ip link set dev br-veth2 master br0
# ip link set dev br-veth3 master br0

 

仮想スイッチにちゃんと仮想NICが結びついているかどうか確認します。

# ip link show master br0
12: br-veth1@if13: <BROADCAST,MULTICAST> mtu 1500 qdisc noop master br0 state DOWN mode DEFAULT group default qlen 1000
    link/ether fe:30:c8:94:51:21 brd ff:ff:ff:ff:ff:ff link-netnsid 0
14: br-veth2@if15: <BROADCAST,MULTICAST> mtu 1500 qdisc noop master br0 state DOWN mode DEFAULT group default qlen 1000
    link/ether a2:75:8a:a3:d8:7e brd ff:ff:ff:ff:ff:ff link-netnsid 1
16: br-veth3@rt-veth: <BROADCAST,MULTICAST,M-DOWN> mtu 1500 qdisc noop master br0 state DOWN mode DEFAULT group default qlen 1000
    link/ether 16:7b:be:f8:dd:1b brd ff:ff:ff:ff:ff:ff

 

バッチリですーヮ(゚д゚)ォ!

今の状態は以下の図のとおりです。

仮想NICと仮想スイッチのUp

各仮想NICと仮想スイッチはデフォルトではDownという状態で使えません。これをUpという状態にして使えるようにします。

# ip netns exec ns1 ip link set ns-veth1 up
# ip netns exec ns2 ip link set ns-veth2 up
# ip link set rt-veth up
# ip link set br-veth1 up
# ip link set br-veth2 up
# ip link set br-veth3 up
# ip link set br0 up

 

IPアドレスを付ける

このままではまだIPアドレスが付与されていません。よって、つけましょう!!

# ip netns exec ns1 ip addr add 192.168.0.1/24 dev ns-veth1
# ip netns exec ns2 ip addr add 192.168.0.2/24 dev ns-veth2 
# ip addr add 192.168.0.100/24 dev rt-veth

 

今の状態はこんな感じです。

動作確認しましょう!!

ns1からns2へPingを打ってみましょう。以下のような感じですね。

 

では、早速Pingってみます。

# ip netns exec ns1 ping 192.168.0.2
# ip netns exec ns2 ip addr add 192.168.0.2/24 dev ns-veth2
64 bytes from 192.168.0.2: icmp_seq=1 ttl=64 time=0.071 ms
64 bytes from 192.168.0.2: icmp_seq=2 ttl=64 time=0.112 ms
64 bytes from 192.168.0.2: icmp_seq=3 ttl=64 time=0.110 ms
64 bytes from 192.168.0.2: icmp_seq=4 ttl=64 time=0.093 ms
・・・以下略・・・

完璧ですーヮ(゚д゚)ォ!

では次にGoogleのパブリックDNS(8.8.8.8)にPingを打ってみましょう。

# ip netns exec ns1 ping 8.8.8.8
connect: Network is unreachable

あれあれ?ダメでした。PC内ならOKですが、外部のネットワークには到達しません。実はこれには理由が3つあります。

■ IP転送が有効になっていない

デフォルトでは、IP転送(PC内の異なるNIC間でのパケットのルーティング)が有効になっていません。つまり、以下の図で言うところのrt-vethと物理NIC間でパケットが転送されません。これを実現するには特殊な設定をする必要があります。

■ Network Namespace内でデフォルトゲートウェイが設定されてない

同じサブネット内(192.168.0.0/24)あてのパケットならいいですが、それ以外の宛先のパケットの場合、どこに届けるべきかを設定する必要があります。つまりデフォルトゲートウェイの設定なのですが、それが現在されていません。

■ NATされていない

ns1というNetwork Namespaceから8.8.8.8あてのパケットの送信元IPアドレスと宛先IPアドレスは以下のようになっています。

送信元IPアドレス
192.168.0.1
宛先IPアドレス
8.8.8.8

このアドレスでは外部ネットワークへは届きません。送信元IPアドレスがプライベートアドレスだからです。インターネットに出るときには、送信元を物理NICのIPアドレスに変換する必要はあります。つまりNATする必要があります。

送信元IPアドレス
物理NICのIPアドレス
宛先IPアドレス
8.8.8.8

外部ネットワークへの接続を有効にする

ということで、外部ネットワークへの接続を有効にする設定をしましょう。

IP転送の有効

以下の設定を行います。これだけでOKです。

# echo 1 > /proc/sys/net/ipv4/ip_forward

デフォルトゲートウェイの設定

ちょっと確認してみると、たしかにデフォルトゲートウェイがありません。

# ip netns exec ns1 route
Kernel IP routing table
Destination Gateway Genmask Flags Metric Ref Use Iface
192.168.0.0 0.0.0.0 255.255.255.0 U 0 0 0 ns-veth1

 

Network Namespaceであるns1およびns2のデフォルトゲートウェイをrt-veth(192.168.0.100)にします。

# ip netns exec ns1 ip route add default via 192.168.0.100
# ip netns exec ns2 ip route add default via 192.168.0.100

 

では確認してみましょう。

# ip netns exec ns1 route
Kernel IP routing table
Destination     Gateway         Genmask         Flags Metric Ref    Use Iface
default         gateway         0.0.0.0         UG    0      0        0 ns-veth1
192.168.0.0     0.0.0.0         255.255.255.0   U     0      0        0 ns-veth1

ばっちりっす(^o^)

NATの設定

では、最後にNATの設定をしましょう。iptablesコマンドを利用します。iptablesはパケットの書き換えを行う機能です。ここではその詳細は説明しませんが、以下のコマンドは、ルーティングが終わった後に、送信元が192.168.0.0/24で、かつeth0というネットワークデバイスから出ていくパケットの送信元アドレスをeth0のアドレス、つまり物理NICのアドレスに変更するという内容です。

# iptables -t nat -A POSTROUTING -s 192.168.0.0/24 -o eth0 -j MASQUERADE

再び動作確認!!

再び動作確認してみます。

# ip netns exec ns1 ping 8.8.8.8
PING 8.8.8.8 (8.8.8.8) 56(84) bytes of data.
64 bytes from 8.8.8.8: icmp_seq=1 ttl=127 time=12.9 ms
64 bytes from 8.8.8.8: icmp_seq=2 ttl=127 time=12.3 ms
64 bytes from 8.8.8.8: icmp_seq=3 ttl=127 time=13.0 ms
・・・以下略・・・

できたぞーヮ(゚д゚)ォ!

どんなふうにパケットが届いているの?

この動きをさわにわかりやすくするため、今のパケットの流れを図にしてみました。まずNetwork Namespaceのns1から出たパケットは、宛先が192.169.0.0/24宛ではないので、先程設定したデフォルトゲートウェイの設定により、rt-veth(192.168.0.100)に向かいます。

 

IP転送の設定によるrt-vethから物理NICにルーティングされます。

 

iptablesコマンドで行ったNATの設定により、送信元IPアドレスが物理NICのIPアドレスに変換されます。

 

パケットがインターネットに出ていきます。

こんな感じでパケットは外部ネットワークに出ていきます。

補足

先程以下の図の赤枠の仮想NICのペアを作成するときには結構面倒なことをしたと思います。

 

こんなことしたと思います。

  • br-veth3とrt-vethという仮想NICのペアの作成する
  • br-veth3 を仮想スイッチに接続する
  • rt-vethにIPアドレスを付与する。

コマンドにするとこんなことしましたよね。まず、br-veth3とrt-vethという仮想NICのペアの作成して、、、

# ip link add name br-veth3 type veth peer name rt-veth

 

br-veth3 を仮想スイッチに接続して、、、

# ip link set dev br-veth3 master br0

 

rt-vethにIPアドレスを付与します。

ip addr add 192.168.0.100/24 dev rt-veth

 

こんなめんどくさいことしましたよね。。。

 

実は上記の3つのコマンドは、以下のコマンドで一発で代用できたりします。

# ip addr add dev br0 192.168.0.100/24

 

仮想スイッチにIPアドレスを付けるというコマンドです。仮想スイッチにIPアドレスっていうのがなんともわかりにくいのですが、要は以下の赤枠のものを作ることと同じことをやっているのです。

 

仮想スイッチの状態を見ると、こんな見え方をします。

# ip addr show
・・・略・・・
18: br0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default qlen 1000
    link/ether fe:30:c8:94:51:21 brd ff:ff:ff:ff:ff:ff
    inet 192.168.0.100/24 scope global br0
       valid_lft forever preferred_lft forever
    inet6 fe80::147b:beff:fef8:dd1b/64 scope link 
       valid_lft forever preferred_lft forever

 

仮想スイッチにIPアドレスが付いているのです。私、最初みたとき戸惑ったのです。なんでL2のレイヤーであるはずの仮想スイッチにIPアドレスが付くのだろうと。。。でも実は、くどいようですが、これは以下の図の赤枠の部分を作る処理を簡略化したに過ぎないらしいです(同じ図を3度も登場させてすみません)。

 

ややこしいですね。実際にDockerでネットワークを作成するときも、仮想スイッチにIPアドレスが付きます。

Dockerを使えばこんなに簡単!!

さて、先程ipコマンドを色入こねくり回して作ったネットワークですが、実はこれ、dockerコマンドを使ってコンテナを作ると特に意識することもなく出来てしまいます。

CentOSのコンテナを2つ作ってみましょう。

# docker run -it -d --name test01 centos:centos7
# docker run -it -d --name test02 centos:centos7

 

実はこれだけで既に以下のようなネットワークが出来上がります。

 

docker0というのは、Dockerをインストールしたときにデフォルトで作成される仮想スイッチです。特に何も指定せずコンテナを作成すると、このdocker0に接続されます。docker0にはIPアドレスが付与されており、コンテナのデフォルトゲートウェイはこのアドレス(172.17.0.1)になっています。新しく2つのコンテナを作成したので、「veth9f0eb93」「veth7010a0b」という新たに2つの仮想NICが出来上がっています。

# ip addr show
・・・略・・・
5: docker0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default 
    link/ether 02:42:be:19:a5:0e brd ff:ff:ff:ff:ff:ff
    inet 172.17.0.1/16 brd 172.17.255.255 scope global docker0
       valid_lft forever preferred_lft forever
    inet6 fe80::42:beff:fe19:a50e/64 scope link 
       valid_lft forever preferred_lft forever
32: veth9f0eb93@if31: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue master docker0 state UP group default 
    link/ether fa:43:35:64:5a:8b brd ff:ff:ff:ff:ff:ff link-netnsid 0
    inet6 fe80::f843:35ff:fe64:5a8b/64 scope link 
       valid_lft forever preferred_lft forever
34: veth7010a0b@if33: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue master docker0 state UP group default 
    link/ether 2e:f1:f5:7d:0f:0e brd ff:ff:ff:ff:ff:ff link-netnsid 1
    inet6 fe80::2cf1:f5ff:fe7d:f0e/64 scope link 
       valid_lft forever preferred_lft forever

 

docker0の仮想スイッチに接続されている仮想NICを見てみます。確かに先程の「veth9f0eb93」「veth7010a0b」という仮想NICがdocker0という仮想スイッチに接続されているのがわかります。

# ip link show master docker0
32: veth9f0eb93@if31: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue master docker0 state UP mode DEFAULT group default 
    link/ether fa:43:35:64:5a:8b brd ff:ff:ff:ff:ff:ff link-netnsid 0
34: veth7010a0b@if33: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue master docker0 state UP mode DEFAULT group default 
    link/ether 2e:f1:f5:7d:0f:0e brd ff:ff:ff:ff:ff:ff link-netnsid 1

 

では、各コンテナのデフォルトゲートウェイも調べてみましょう。docker inspectコマンドでコンテナの詳細情報を見ることが出来ます。–formatオプションを使うと、出力する情報を整形出来ます。デフォルトゲートウェイの情報のみを出すように整形してみます。

# docker inspect --format '{{.NetworkSettings.Gateway}}' test01
172.17.0.1
# docker inspect --format '{{.NetworkSettings.Gateway}}' test02
172.17.0.1

たしかにデフォルトゲートウェイは、docker0のIPアドレスになっていました。

Dockerであれば、ややこしいNetwork Namespaceや仮想スイッチ、仮想NICの作成をすべてやってくれるのです。スゴイですね、Dockerコマンド。

まとめ

いかがでしょうか?なかなかにややこしいDockerのネットワークについて、なるべくわかりみの深い説明をしたつもりです。本記事がみなさまのお役に立てれば幸いでございます。もしお役に立てたら、いいねをしたりTwitterとかSNSで拡散したりコメントしたりしてくれると、とってもうれしいです(/ω・\)チラッ次回は、Dockerのファイルシステムについて説明いたします。【連載】世界一わかりみが深いコンテナ & Docker入門 〜 その6:Dockerのファイルシステムってどうなってるの? 〜

アバター画像
About 武井 宜行 267 Articles
Microsoft MVP for Azure🌟「最新の技術を楽しくわかりやすく」をモットーにブログtech-lab.sios.jp)で情報を発信🎤得意分野はAzureによるクラウドネイティブな開発(Javaなど)💻「世界一わかりみの深いクラウドネイティブ on Azure」の動画を配信中📹 https://t.co/OMaJYb3pRN
ご覧いただきありがとうございます! この投稿はお役に立ちましたか?

役に立った 役に立たなかった

66人がこの投稿は役に立ったと言っています。


ご覧いただきありがとうございます。
ブログの最新情報はSNSでも発信しております。
ぜひTwitterのフォロー&Facebookページにいいねをお願い致します!



>> 雑誌等の執筆依頼を受付しております。
   ご希望の方はお気軽にお問い合わせください!

5 Comments

  1. めちゃくちゃ分かりやすいです!
    続きを楽しみにしております。

    • ありがとうございます!!そう言っていただきますと執筆意欲100倍です。続きもがんばります(๑•̀ㅂ•́)و✧

  2. サイオステクノロジー 武井さん

    とても勉強になりました!
    他社さんが公開しているDocker ハンズオンの内容に沿って
    Docker ネットワークを使って通信を行うことをやってみたものの、実態として何が起きているのかはよく分からず…。
    (ハンズオンなんで仕方ないですが)

    コマンド例と図解付きでよく理解できました!わかりみ深いです!!!(/・ω・)/

    お忙しいと思いますが続編もお待ちしています。
    よろしくお願いします!

    • ウチイダユウゴ様

      武井です。ありがとうございます!!そう言っていただきますと、ますます執筆意欲がわきてきます(●´ω`●)続編もがんばりますー。

  3. めちゃくちゃよくわかりました。
    ありがとうございます。

武井宜行 へ返信する コメントをキャンセル

Your email address will not be published.


*


質問はこちら 閉じる