理系学生日記

おまえはいつまで学生気分なのか

ぜんぜん分からない、俺たちは雰囲気で Docker のネットワークを使っている

ぜんぜんわからない、俺たちは雰囲気で Docker を使っている。そんな状況を打破するために、きちんと基本を押さえましょうという状況です。

今日の docker (for mac) はこちらです。

$ docker -v
Docker version 18.03.1-ce, build 9ee9f40

というわけで、まずはネットワークからですが、面白かったのを最初にまとめておきます。

NW コンテナ間疎通 コンテナの名前解決
bridge o x
ユーザ定義ネットワーク o o

Docker におけるネットワーク

Docker が持つネットワークは、docker network ls で参照することができます。 マルチホストネットワークを使わない場合、何も考えずにコンテナを run するときに使うのは bridge というネットワークになります。

$ docker network ls | head -2
NETWORK ID          NAME                DRIVER              SCOPE
01da84451bd1        bridge              bridge              local

この bridge と呼ばれるデフォルトネットワークは、同名の bridge という Docker のネットワークドライバを使用するネットワークになります。

Docker におけるネットワークドライバは、デフォルトで以下の 5 つが用意されています。 スタンドアローンで Docker を使う場合、ほとんどは bridge で済むはずです。ちなみに host は Docker Swarm 限定で、overlay は複数の Docker Host 上のコンテナを繋ぐ場合に利用するヤツです。

  • bridge
  • host
  • overlay
  • macvlan
  • none

単純に bridge を使う

何も考えずに docker run すると、そこで起動されたコンテナは bridge ネットワークに接続されます。 例えば、以下のように 2 つコンテナを立ち上げた後、bridge を inspect すると、立ち上げた 2 つのコンテナが bridge ネットワークに接続されていることが分かります。

$ docker run -dit --name alpine1 alpine ash
$ docker run -dit --name alpine2 alpine ash
$ docker network inspect bridge | jq -r '.[0].Containers[] | [.Name, .IPv4Address]'
[
  "alpine1",
  "172.17.0.2/16"
]
[
  "alpine2",
  "172.17.0.3/16"
]

疎通確認と名前解決

それじゃ、alpine1 に attach して、alpine2 と疎通が取れるかを確認してみます。 結果は以下のとおりで、ping は通りますが、alpine2 の名前解決はできません。

/ # ping -c1 172.17.0.3
PING 172.17.0.3 (172.17.0.3): 56 data bytes
64 bytes from 172.17.0.3: seq=0 ttl=64 time=0.126 ms

--- 172.17.0.3 ping statistics ---
1 packets transmitted, 1 packets received, 0% packet loss
round-trip min/avg/max = 0.126/0.126/0.126 ms
/ # ping alpine2
ping: bad address 'alpine2'

自分でネットワークをつくる

IP アドレス帯を勝手に決められるのもアレですので、自分でネットワークを作ってしまいましょう。 ここでは、10.0.0.0/8 のサブネット内に、10.1.0.0/16 にコンテナを配置するようにしてみます。

$ docker network create --driver=bridge \
                        --subnet=10.0.0.0/8\
                        --ip-range=10.1.0.0/16\
                        --gateway=10.1.255.254\
                        kiririmode-network
244715a00a1c6eb5d0e1094b1117921dbd493f63cb2504102fb04498c3d8a9d4

そして、ここにコンテナを紐付けてみると、コンテナ 2 つが 10.1.0.0/16 の IP アドレスを持って kiririmode-network に接続できていることが分かります。

$ docker run -dit --name alpineA --network kiririmode-network alpine ash
$ docker run -dit --name alpineB --network kiririmode-network alpine ash
$ docker network inspect kiririmode-network | jq -r '.[0].Containers[] | [.Name, .IPv4Address]'
[
  "alpineA",
  "10.1.0.0/8"
]
[
  "alpineB",
  "10.1.0.1/8"
]

疎通確認と名前解決

こちらでも、疎通確認と名前解決をしてみましょう。 以下のように、bridge ネットワークとは違い、名前解決も含めて成功していることが分かります。

/ # ping -c1 10.1.0.1
PING 10.1.0.1 (10.1.0.1): 56 data bytes
64 bytes from 10.1.0.1: seq=0 ttl=64 time=0.222 ms

--- 10.1.0.1 ping statistics ---
1 packets transmitted, 1 packets received, 0% packet loss
round-trip min/avg/max = 0.222/0.222/0.222 ms
/ # ping -c1 alpineB
PING alpineB (10.1.0.1): 56 data bytes
64 bytes from 10.1.0.1: seq=0 ttl=64 time=0.439 ms

--- alpineB ping statistics ---
1 packets transmitted, 1 packets received, 0% packet loss
round-trip min/avg/max = 0.439/0.439/0.439 ms

名前解決の差異はどこから来るのか

簡単な違いは以下のとおりです。

# bridge に接続されたコンテナ
% docker exec alpine1 grep nameserver /etc/resolv.conf
nameserver 192.168.65.1
# ユーザ定義ネットワークに接続されたコンテナ
$ docker exec alpineA grep nameserver /etc/resolv.conf
nameserver 127.0.0.11

このように、向いている DNS サーバが異なります。

ユーザ定義ネットワークである kiririmode-network に接続されたコンテナは、127.0.0.11 という DNS サーバを向いていますが、 これはユーザ定義ネットワーク上にのみ構築される埋め込み DNS サーバになります。 ガイド を見る限り、bridge ネットワークのみ、後方互換性を考慮して挙動に違いがあるようで。

埋め込み DNS サーバって、停止させたコンテナの名前を解決できるの?

では、一方のコンテナを停止させた場合、名前解決は引き続き行われるのでしょうか、それとも解決できなくなるのでしょうか。試した結果がこちら。

# 前提として最初は繋がる
$ docker exec alpineA ping -c 1 alpineB
PING alpineB (10.1.0.1): 56 data bytes
64 bytes from 10.1.0.1: seq=0 ttl=64 time=0.142 ms

--- alpineB ping statistics ---
1 packets transmitted, 1 packets received, 0% packet loss
round-trip min/avg/max = 0.142/0.142/0.142 ms

# alpineB を停止
$ docker stop alpineB
alpineB

# 名前解決できなくなる
$ docker exec alpineA ping -c 1 alpineB
ping: bad address 'alpineB'