#
ドキュメント

Document

自分のための備忘録です。

Docker ネットワーク

前提

  • DockerをUbuntuにインストール
    • Ubuntu: 20.04.4 LTS
    • Docker: 20.10.16

Ref

Dockerのネットワーク ドライバー

  • bridge
  • host
  • none

ブリッジの一般的な意味。

ブリッジ(Bridge/L2スイッチ) ネットワークをデータリンク層で延長する装置

-- マスタリングTCP/IP 入門編 p38

bridgeネットワークの概要

  • ネットワークを指定しないで起動したコンテナはデフォルトのbridgeネットワークに接続
    • デフォルトのbridgeネットワークのネットワークインターフェースはdocker0^docker0
    • デフォルトのbridgeネットワークに接続されたコンテナ間はIPアドレスで通信可能
    • デフォルトのbridgeネットワークに接続されたコンテナ間はコンテナ名で通信不可(デフォルトのbridgeネットワークは内蔵DNSサーバを持たないため)
  • 新規作成したbridgeネットワークに接続されたコンテナ間はコンテナ名で通信可能
  • Docker Composeは、自動でbridgeネットワークを新規作成するのでservice名`[^serivice名]で通信可能

[^service名]:Docker Composeは各コンテナをserviceと呼びます。

デフォルトネットワーク

Docker時にデフォルトでネットワーク名がbridgehostnullのネットワークが作成されます。

$ sudo docker network ls
NETWORK ID     NAME      DRIVER    SCOPE
abf0a3a6242a   bridge    bridge    local     <--- このネットワークのネットワークインターフェースがUbuntuはdocker0、Macはbridge0になります
5647050e2ccb   host      host      local
f17799accf48   none      null      local

上述したとおりデフォルトで作成されるbridgeネットワークのネットワークインターフェースがdoker0になります(Ubuntuの場合^docker0_2)。

Docker のインストールは、自動的に3つのネットワークを作成します。

https://docs.docker.jp/engine/userguide/networking/dockernetworks.html

Docker をインストールした全ての環境には、 docker0 と表示されるブリッジ( bridge )ネットワークが現れます。オプションで docker run --net=<ネットワーク名> を指定しない限り、Docker デーモンはデフォルトでこのネットワークにコンテナを接続します。ホスト上で ifconfig コマンドを使えば、ホストネットワーク上のスタックの一部として、このブリッジを見ることができます。

https://docs.docker.jp/engine/userguide/networking/dockernetworks.html

デフォルトのbridgeネットワーク

  • デフォルトのbridgeネットワークのネットワークインターフェースはdocker0になります
  • ネットワークを指定せずに起動したコンテナはデフォルトのbridgeネットワークに接続されます

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

ホストは以下のネットワークインターフェースを持つと仮定します。

  • eth0:ホストのデフォルトのネットワークインターフェース
  • docker0:デフォルトのbridgeネットワークのネットワークインターフェース

コンテナ同士の通信

IPアドレス[^docker-ip]を使ったコンテナ間通信においてdocker0に発生するデータをキャプチャしてみます。

[^docker-ip]:コンテナ名で通信する場合はdocker0ではなく、docker network create {{network_name}}で作成したネットワークを設定してコンテナを起動します。

$ docker run -itd --name alpine-sample1 alpine:latest
$ docker run -itd --name sample-sample2 alpine:latest

コンテナのIPアドレスを確認します。

$ sudo docker exec -it alpine-sample1 /bin/sh
/ # ifconfig
eth0      Link encap:Ethernet  HWaddr 02:42:AC:11:00:02
          inet addr:172.17.0.2  Bcast:172.17.255.255  Mask:255.255.0.0
          ...
  • alpine-sample1のIPアドレスは172.17.0.2です
  • 以下同様にalpine-sample2のIPアドレスは172.17.0.3とします

コンテナ間はdocker0で通信していることを確認します^docker0-2

$ sudo tcpdump -tnl -i docker0 icmp
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on docker0, link-type EN10MB (Ethernet), capture size 262144 bytes

alpine-sample1からalpine-sample2pingを発行します。

$ docker exec -it alpine /bin/sh
/ # ping -c 1 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.110 ms

予想どおりdocker0に通信が発生しています。

$ sudo tcpdump -tnl -i docker0 icmp                                                                                                                        [15/84]
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on docker0, link-type EN10MB (Ethernet), capture size 262144 bytes

.....
IP 172.17.0.2 > 172.17.0.3: ICMP echo request, id 12, seq 0, length 64
IP 172.17.0.3 > 172.17.0.2: ICMP echo reply, id 12, seq 0, length 64

コンテナとインターネットの通信

外部との通信を確認します。
例としてGoogleが提供するフルサービスリゾルバ8.8.8.8pingを発行します。

$ sudo docker exec -it alpine-sample1 /bin/sh
/ # ping -c 1 8.8.8.8
PING 8.8.8.8 (8.8.8.8): 56 data bytes
64 bytes from 8.8.8.8: seq=0 ttl=101 time=3.000 ms

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

予想どおりdocker0に通信が発生しています。

$ sudo tcpdump -tnl -i docker0 icmp
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on docker0, link-type EN10MB (Ethernet), capture size 262144 bytes

.....
IP 172.17.0.2 > 8.8.8.8: ICMP echo request, id 27, seq 0, length 64
IP 8.8.8.8 > 172.17.0.2: ICMP echo reply, id 27, seq 0, length 64

加えてホストのデフォルトのネットワークインターフェースeth0にも通信が発生していることが分かります。

$ sudo tcpdump -tnl -i eth0 icmp
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on eth0, link-type EN10MB (Ethernet), capture size 262144 bytes

...
IP 10.3.0.183 > 8.8.8.8: ICMP echo request, id 27, seq 0, length 64
IP 8.8.8.8 > 10.3.0.183: ICMP echo reply, id 27, seq 0, length 64

外部からのアクセス

外部からコンテナにアクセスする際の通信をみます。 例としてhttpd:latestを稼働しているホストに外部からアクセスした際の通信をみます。

前提

  • ホスト
    • グローバルIPアドレス:203.0.113.1
    • プレイベートIPアドレス:192.0.2.1/24
    • ネットワークインターフェース
      • eth0
      • docker0
  • コンテナ
    • プライベートIPアドレス:172.17.0.4
    • ネットワークインターフェース
      • eth0
  • クライアント
    • グローバルIPアクセス:203.0.113.150

httpd:latestifconfigがインストールされていないのでapt install net-toolsでインストールします。

コンテナ起動

$ sudo docker run -d -p 8000:80 --name httpd-sample httpd

通信を確認

クライアントのブラウザで http://203.0.113.1:8000 にアクセスします。

ホストのeth0の通信をみます。
クライアントとホストはポート8000番で通信しています。

$ sudo tcpdump -tnl -i eth0 tcp port 8000                                                                                                           [19/1972]
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on eth0, link-type EN10MB (Ethernet), capture size 262144 bytes

IP 203.0.113.150.64640 > 192.0.2.1.8000: Flags [S], seq 218777360, win 65535, options [mss 1394,nop,wscale 6,nop,nop,TS val 3364579648 ecr 0,sackOK,eol], length 0
IP 192.0.2.1.8000 > 203.0.113.150.64640: Flags [S.], seq 1645943187, ack 218777361, win 65160, options [mss 1460,sackOK,TS val 2400436806 ecr 3364579648,nop,wscale 6], length 0
IP 203.0.113.150.64639 > 192.0.2.1.8000: Flags [.], ack 1, win 2051, options [nop,nop,TS val 1426504603 ecr 2400436806], length 0

ホストのdocker0の通信をみます。
コンテナとポート80番で通信しています。

$ sudo tcpdump -tnl -i docker0 tcp port 80                                                                                                          [19/1804]
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on docker0, link-type EN10MB (Ethernet), capture size 262144 bytes

IP 203.0.113.150.64640 > 172.17.0.4.80: Flags [S], seq 218777360, win 65535, options [mss 1394,nop,wscale 6,nop,nop,TS val 3364579648 ecr 0,sackOK,eol], length 0
IP 172.17.0.4.80 > 203.0.113.150.64640: Flags [S.], seq 1645943187, ack 218777361, win 65160, options [mss 1460,sackOK,TS val 2400436806 ecr 3364579648,nop,wscale 6], length 0
IP 203.0.113.150.64639 > 172.17.0.4.80: Flags [.], ack 1, win 2051, options [nop,nop,TS val 1426504603 ecr 2400436806], length 0

コンテナのeth0の通信をみます。 予想どおりポート80番で通信しています。

# tcpdump -tnl -i eth0 tcp port 80
tcpdump: verbose output suppressed, use -v[v]... for full protocol decode
listening on eth0, link-type EN10MB (Ethernet), snapshot length 262144 bytes

IP 203.0.113.150.64640 > 172.17.0.4.80: Flags [S], seq 218777360, win 65535, options [mss 1394,nop,wscale 6,nop,nop,TS val 3364579648 ecr 0,sackOK,eol], length 0
IP 172.17.0.4.80 > 124.18.121.200.64640: Flags [S.], seq 1645943187, ack 218777361, win 65160, options [mss 1460,sackOK,TS val 2400436806 ecr 3364579648,nop,wscale 6], length 0
IP 124.18.121.200.64639 > 172.17.0.4.80: Flags [.], ack 1, win 2051, options [nop,nop,TS val 1426504603 ecr 2400436806], length 0

簡単ですが外部からのコンテナに対するアクセスついて確認しました。

bridgeネットワーク作成

デフォルトネットワークを確認

デフォルトのネットワークを再度確認してみます。

$ sudo docker network ls
NETWORK ID     NAME      DRIVER    SCOPE
abf0a3a6242a   bridge    bridge    local     <--- このネットワークのネットワークインターフェースがUbuntuはdocker0、Macはbridge0
5647050e2ccb   host      host      local
f17799accf48   none      null      local

bridgeネットワークを新規作成

$ docker network create {{network_name}}
  • ネットワークを追加するとbr-で始まるネットワーク・インターフェースがホストに作成されます
  • br-xxxxxのxxxxxは、NEWORK IDです
  • NTWORK IDはdocker network lsで確認します

例)

$ docker network create sample
$sudo docker network ls

NETWORK ID     NAME      DRIVER    SCOPE
abf0a3a6242a   bridge    bridge    local
5647050e2ccb   host      host      local
f17799accf48   none      null      local
8fe0215332a8   sample    bridge    local <--- 新規に作成

ホストでifconfigを発行してネットワークインターフェースを確認します。

  • br-8fe0215332a8が追加されています
  • br-8fe0215332a8のIPアドレスは172.18.0.1です
$ ifconfig
br-8fe0215332a8: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1500     // <--- 追加
        inet 172.18.0.1  netmask 255.255.0.0  broadcast 172.18.255.255
        inet6 fe80::42:87ff:fe57:24b2  prefixlen 64  scopeid 0x20<link>
        ether 02:42:87:57:24:b2  txqueuelen 0  (Ethernet)
        RX packets 0  bytes 0 (0.0 B)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 5  bytes 526 (526.0 B)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

docker0: flags=4099<UP,BROADCAST,MULTICAST>  mtu 1500
        inet 172.17.0.1  netmask 255.255.0.0  broadcast 172.17.255.255
        inet6 fe80::42:37ff:fe8d:b952  prefixlen 64  scopeid 0x20<link>
        ether 02:42:37:8d:b9:52  txqueuelen 0  (Ethernet)
        RX packets 1392  bytes 85837 (85.8 KB)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 2875  bytes 13382135 (13.3 MB)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

eth0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 9001
        inet 10.3.0.183  netmask 255.255.255.0  broadcast 10.3.0.255
        inet6 fe80::4b5:10ff:fe1f:21cd  prefixlen 64  scopeid 0x20<link>
        ether 06:b5:10:1f:21:cd  txqueuelen 1000  (Ethernet)
        RX packets 66248  bytes 76218429 (76.2 MB)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 96440  bytes 23585335 (23.5 MB)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

新規bridgeネットワークを使った通信

netオプションで作成したbridgeネットワークを指定してコンテナを起動します。

$ sudo docker run -itd --name alpine-sample1 --net sample alpine:latest
$ sudo docker run -itd --name alpine-sample2 --net sample alpine:latest

alpine-sample1に入ってネットワークインターフェースのIPアドレスを確認してみます。

docker exec -it alpine-sample1 /bin/sh
/ # ifconfig
eth0      Link encap:Ethernet  HWaddr 02:42:AC:12:00:02  
          inet addr:172.18.0.2  Bcast:172.18.255.255  Mask:255.255.0.0
          inet6 addr: fe80::42:acff:fe12:2/64 Scope:Link
          UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1
          RX packets:16 errors:0 dropped:0 overruns:0 frame:0
          TX packets:8 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:0 
          RX bytes:1392 (1.3 KiB)  TX bytes:656 (656.0 B)

lo        Link encap:Local Loopback  
          inet addr:127.0.0.1  Mask:255.0.0.0
          inet6 addr: ::1/128 Scope:Host
          UP LOOPBACK RUNNING  MTU:65536  Metric:1
          RX packets:0 errors:0 dropped:0 overruns:0 frame:0
          TX packets:0 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:1000 
          RX bytes:0 (0.0 B)  TX bytes:0 (0.0 B)

IPアドレスは172.18.0.2です。
同様にalpine-sample2のネットワークインターフェースのIPアドレスは172.18.0.3です。

ネットワークをまとめると以下になります。

  • ブリッジのネットワークインターフェースbr-8fe0215332a8のIPアドレスは172.18.0.1
  • alpine-sample1のネットワークインターフェースのIPアドレスは172.18.0.2
  • alpine-sample2のネットワークインターフェースのIPアドレスは172.18.0.3

ではネットワークインターフェースbr-8fe0215332a8の通信をキャプチャして使用を確認します。

$ sudo tcpdump -tnl -i br-8fe0215332a8 icmp
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on br-8fe0215332a8, link-type EN10MB (Ethernet), capture size 262144 bytes

コンテナ名alpine-sample2にアクセスします(デフォルトのbridgeネットワークインターフェースdocker0を使う場合はIPアドレスでしかアクセスできません)。 コンテナ名でアクセスできるのは、bridgeネットワーク(デフォルトのbridgeネットワークを除く)が内蔵DNSサーバを持っているからです^bridge-net

デフォルトの「bridge0」という名称のブリッジ・ネットワークでは、この名前解決機能がありません。コンテナ間の通信でサービス・ディスカバリを有効化するには、自分でブリッジ・ネットワークを作成する必用があります。これをDockerではUser Defined Bridge Network(ユーザ定義ブリッジ・ネットワーク)と呼びます。

-- https://knowledge.sakura.ad.jp/23899/

内蔵DNSサーバを使用してコンテナ名(Docker Composeではservice名)でアクセスする機能をサービス・ディスカバリと呼びます。

$ sudo docker exec -it alpine1 /bin/sh
/ # ping -c 1 alpine-sample2 // <------------------------ コンテナ名でPingを発行
PING alpine2 (172.18.0.2): 56 data bytes
64 bytes from 172.18.0.2: seq=0 ttl=64 time=0.103 ms

--- alpine-sample2 ping statistics ---
1 packets transmitted, 1 packets received, 0% packet loss
round-trip min/avg/max = 0.103/0.103/0.103 ms
sudo tcpdump -tnl -i br-8fe0215332a8 icmp
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on br-8fe0215332a8, link-type EN10MB (Ethernet), capture size 262144 bytes

IP 172.18.0.3 > 172.18.0.2: ICMP echo request, id 14, seq 0, length 64
IP 172.18.0.2 > 172.18.0.3: ICMP echo reply, id 14, seq 0, length 64

bridgeネットワークの内蔵DNSサーバ

新規に作成したbridgeネットワークは内蔵DNSサーバを持ちます。

Docker デーモンは内蔵 DNS サーバを動かし、ユーザ定義ネットワーク上でコンテナがサービス・ディスカバリを自動的に行えるようにします。コンテナから名前解決のリクエストがあれば、内部 DNS サーバを第一に使います。リクエストがあっても内部 DNS サーバが名前解決できなければ、外部の DNS サーバにコンテナからのリクエストを転送します。割り当てできるのはコンテナの作成時だけです。内部 DNS サーバが到達可能なのは 127.0.0.11 のみであり、コンテナの resolv.conf に書かれます。ユーザ定義ネットワーク上の内部 DNS サーバに関しては ユーザ定義ネットワーク用の内部 DNS サーバ をご覧ください。

-- https://docs.docker.jp/engine/userguide/networking/dockernetworks.html

上述のalpine-sample1コンテナにdigを入れて内蔵DNSサーバを確認します。

$ sudo docker exec -it alpine-sample1 /bin/sh
/ # apt update
/ # apk add --no-cache bind-tools   // <---------------------- digをインストール
fetch https://dl-cdn.alpinelinux.org/alpine/v3.15/main/x86_64/APKINDEX.tar.gz
.....
OK: 13 MiB in 29 packages
/ # dig alpine-sample2    // <------------------------ サービス・ディスカバリを見る

; <<>> DiG 9.16.27 <<>> alpine2
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 35236
;; flags: qr rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 0

;; QUESTION SECTION:
;alpine-sample2.                       IN      A

;; ANSWER SECTION:
alpine-sample2.                600     IN      A       172.18.0.2

;; Query time: 0 msec
;; SERVER: 127.0.0.11#53(127.0.0.11)   <---- 内蔵DNSサーバ
;; WHEN: Sat May 14 07:26:26 UTC 2022
;; MSG SIZE  rcvd: 48

hostネットワーク

hostネットワークは文字通りホストとネットワークを共有します。
そのためネットワークインターフェースもホストのeth0を使用します(ホストのネットワークインターフェースがeth0の場合)。

hostネットワークの特徴は以下のとおりです。

  • hostネットワークは1つしか存在できない
  • hostネットワークはDockerインストール時に作成されるのでユーザーは作成できない。
  • ユーザーがhostネットワークを作成しようとするとエラーが発生する
$ sudo docker network create -d host sample
Error response from daemon: only one instance of "host" network is allowed

hostネットワークを指定して起動

$ sudo docker run -d --name httpd --net host httpd:latest

hostネットワークはホストのネットワークを共有するのでifconfigもホストと同じです。
ネットワークインターフェースもホストのeth0を使用します。

$ sudo docker exec -it httpd /bin/bash
root@ip-10-3-0-183:/usr/local/apache2# apt install net-tools // ifconfigインストール
....
root@ip-10-3-0-183:/usr/local/apache2# ifconfig
docker0: flags=4099<UP,BROADCAST,MULTICAST>  mtu 1500
        inet 172.17.0.1  netmask 255.255.0.0  broadcast 172.17.255.255
        inet6 fe80::42:37ff:fe8d:b952  prefixlen 64  scopeid 0x20<link>
        ether 02:42:37:8d:b9:52  txqueuelen 0  (Ethernet)
        RX packets 1392  bytes 85837 (85.8 KB)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 2875  bytes 13382135 (13.3 MB)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

eth0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 9001
        inet 10.3.0.183  netmask 255.255.255.0  broadcast 10.3.0.255
        inet6 fe80::4b5:10ff:fe1f:21cd  prefixlen 64  scopeid 0x20<link>
        ether 06:b5:10:1f:21:cd  txqueuelen 1000  (Ethernet)
        RX packets 87575  bytes 94433872 (94.4 MB)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 105846  bytes 24945855 (24.9 MB)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

lo: flags=73<UP,LOOPBACK,RUNNING>  mtu 65536
        inet 127.0.0.1  netmask 255.0.0.0
        inet6 ::1  prefixlen 128  scopeid 0x10<host>
        loop  txqueuelen 1000  (Local Loopback)
        RX packets 574  bytes 61238 (61.2 KB)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 574  bytes 61238 (61.2 KB)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

ブラウザでアクセスするとeth0に通信が発生していることが確認できます(docker0には発生しない)。

$ sudo tcpdump -tnl -i eth0 tcp  port 80
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on eth0, link-type EN10MB (Ethernet), capture size 262144 bytes

IP 203.0.113.150.51221 > 10.3.0.183.80: Flags [S], seq 251437622, win 65535, options [mss 1394,nop,wscale 6,nop,nop,TS val 1967450493 ecr 0,sackOK,eol], length 0
IP 10.3.0.183.80 > 203.0.113.150.51221: Flags [S.], seq 3651188619, ack 251437623, win 62643, options [mss 8961,sackOK,TS val 14863613 ecr 1967450493,nop,wscale 6], length 0
IP 203.0.113.150.51222 > 10.3.0.183.80: Flags [.], ack 1, win 2051, options [nop,nop,TS val 1589689261 ecr 14863612], length 0

docker0には通信は発生していません。

sudo tcpdump -tnl -i docker0 tcp port 80
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on docker0, link-type EN10MB (Ethernet), capture size 262144 bytes

// ブラウザでアクセスしても通信は発生しない