#
ドキュメント

Document

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

Docker ネットワーク

前提

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

Ref

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

  • bridge
  • host
  • none

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

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

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

基礎知識

  • veth:Linux 仮想イーサネット
  • brxxx:Linux 仮想ブリッジ
  • SourceNAT(IPマスカレード)
  • iptables
  • ip
  • brctl
  • route -ne

コンテナから外部へ

コンテナから外部へはセグメントをまたぐ(例 docker0 172.16.0.0/16 から ホストのネットワーク 10.3.0.0/16)ためにパケット転送net.ipv4.ip.forward=1が使われて10.3.0.0/16から外にる際にIPマスカレード(NAT)が使われます。

コンテナ通信の理解

net.ipv4.ip.forward

// 有効
$ sudo sysctl -w net.ipv4.ip_forward=1
// 無効
$ sudo sysctl -w net.ipv4.ip_forward=0

Docker Network(例 172.16.0.0/16)がホストネットワーク(例 10.3.0.0/16)を通してが外部と通信できるのはnet.ipv4.ip.forwardが有効になっているため。

同じPCの場合net.ipv4.ip.forwardがtrueなら転送される。
https://tanke25616429.hatenablog.com/entry/2021/01/17/011553

ホストOSのnet.ipv4.ip.forwardはDockerが自動で有効化する。
コンテナのカーネルパラメータは起動後には変更できない。
変更するには起動時に--sysctlオプションで設定する。

$ docker run --sysctl net.ipv4.ip_forward=1 someimage

ref. 実行時に名前空間のカーネル・パラメータ(sysctl)を設定

IPマスカレード

IPマスカレードのわかりやすい記事。
http://blog.livedoor.jp/harukisan7/archives/17114894.html?utm_source=pocket_mylist

docker0とeth0は、DockerホストのOS機能であるiptablesによるIPマスカレードが行われています。

-- https://www.memotansu.jp/docker/858/#toc5

ホストのネットワークインターフェースdocker0eth0ホストiptablesでIPマスカレードを実現しています。

sudo iptables -t nat -nL
Chain PREROUTING (policy ACCEPT)
target     prot opt source               destination
DOCKER     all  --  0.0.0.0/0            0.0.0.0/0            ADDRTYPE match dst-type LOCAL

Chain INPUT (policy ACCEPT)
target     prot opt source               destination

Chain OUTPUT (policy ACCEPT)
target     prot opt source               destination
DOCKER     all  --  0.0.0.0/0           !127.0.0.0/8          ADDRTYPE match dst-type LOCAL

Chain POSTROUTING (policy ACCEPT)
target     prot opt source               destination
MASQUERADE  all  --  172.19.0.0/16        0.0.0.0/0
MASQUERADE  all  --  172.17.0.0/16        0.0.0.0/0

Chain DOCKER (2 references)
target     prot opt source               destination
RETURN     all  --  0.0.0.0/0            0.0.0.0/0
RETURN     all  --  0.0.0.0/0            0.0.0.0/0

外部から内部へ(ホスト上にコンテナのポートを割り当て)

https://docs.docker.jp/engine/userguide/networking/default_network/binding.html
上記ドキュメントで分かりやすく解説されています。

コンテナが外部から内部への接続を受け付けたいならdocker runpまたは--publishオプション、Docker Composeportsプロパティでポートを公開します。 DockerがiptablesDNATを追加します。

例えばNginxコンテナをrunするときにpオプションで8000を指定したときの例です(コンテナは172.17.0.2で稼働していると仮定します)。

$ docker run -d --name web -p 8000:80 nginx:latest
$ iptables -t nat -L -n
.....
Chain DOCKER-INGRESS (2 references)
target     prot opt source               destination
DNAT       tcp  --  0.0.0.0/0            0.0.0.0/0            tcp dpt:8000 to:172.17.0.2:80
.....

任意の場所(0.0.0.0)からのポート8000でのアクセスを172.17.0.2のポート80に接続します。

bridgeネットワークの概要

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

[^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

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

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

docker inspect

Docker Network情報

$ docker inspect {{NETWORK ID}}

コンテナのネットワーク情報

# コンテナのIPアドレス
$ docker inspect {{コンテナID or NAME}}

デフォルトの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
          ...

docker inspect alpine-sample1でも確認できます。

  • 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ネットワーク作成

ネットワーク構成を把握

https://cacoo.com/diagrams/0jEiiI3UonJoOn6D/C8EAD

確認事項

  • bridgeネットワークを作成したときにホストに追加されるブリッジの仮想ネットワークインターフェース
  • bridgeネットワークにコンテナを追加したときにホストに追加されるブリッジ側のネットワークインターフェース
  • コンテナを追加したときのコンテナのネットワークインターフェース
  • ブリッジのネットワークインターフェース(`brctl show ブリッジ名)
  • コンテナのrouteテーブル(ip route)
  • ホストのIPテーブル

ネットワーク作成前を確認

ホストのネットワークインターフェースとルートテーブルを確認します。

ネットワークインターフェースを確認します。

$ ip a
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    inet 127.0.0.1/8 scope host lo
       valid_lft forever preferred_lft forever
    inet6 ::1/128 scope host
       valid_lft forever preferred_lft forever
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 9001 qdisc fq_codel state UP group default qlen 1000
    link/ether 06:b5:10:1f:21:cd brd ff:ff:ff:ff:ff:ff
    inet 10.3.0.183/24 brd 10.3.0.255 scope global dynamic eth0
       valid_lft 2700sec preferred_lft 2700sec
    inet6 fe80::4b5:10ff:fe1f:21cd/64 scope link
       valid_lft forever preferred_lft forever
4: docker0: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc noqueue state DOWN group default
    link/ether 02:42:bc:b0:20:a7 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

ルートテーブルを確認します。

$ ip route
default via 10.3.0.1 dev eth0 proto dhcp src 10.3.0.183 metric 100
10.3.0.0/24 dev eth0 proto kernel scope link src 10.3.0.183
10.3.0.1 dev eth0 proto dhcp scope link src 10.3.0.183 metric 100
172.17.0.0/16 dev docker0 proto kernel scope link src 172.17.0.1 linkdown

$ route -ne
Kernel IP routing table
Destination     Gateway         Genmask         Flags   MSS Window  irtt Iface
0.0.0.0         10.3.0.1        0.0.0.0         UG        0 0          0 eth0
10.3.0.0        0.0.0.0         255.255.255.0   U         0 0          0 eth0
10.3.0.1        0.0.0.0         255.255.255.255 UH        0 0          0 eth0
172.17.0.0      0.0.0.0         255.255.0.0     U         0 0          0 docker0

ネットワークを作成

birdgeドライバのsampleネットワークを作成します。

$ sudo docker network create sample
29faca36a6b48487a16a1071c20dbe0b0b0bab3f6695d5d96324b80a9c3da773

ホストには仮想ブリッジが作成されて、仮想ブリッジのネットワークインターフェースが追加されます。
ホストのネットワークインターフェースを確認します。

ip a
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    inet 127.0.0.1/8 scope host lo
       valid_lft forever preferred_lft forever
    inet6 ::1/128 scope host
       valid_lft forever preferred_lft forever
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 9001 qdisc fq_codel state UP group default qlen 1000
    link/ether 06:b5:10:1f:21:cd brd ff:ff:ff:ff:ff:ff
    inet 10.3.0.183/24 brd 10.3.0.255 scope global dynamic eth0
       valid_lft 2360sec preferred_lft 2360sec
    inet6 fe80::4b5:10ff:fe1f:21cd/64 scope link
       valid_lft forever preferred_lft forever
4: docker0: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc noqueue state DOWN group default
    link/ether 02:42:bc:b0:20:a7 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
5: br-29faca36a6b4: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc noqueue state DOWN group default <--- sampleの仮想ネットワークインターフェース
    link/ether 02:42:c1:07:54:7f brd ff:ff:ff:ff:ff:ff
    inet 172.18.0.1/16 brd 172.18.255.255 scope global br-29faca36a6b4
       valid_lft forever preferred_lft forever

sampleを作成したことによってbr-29faca36a6b4が作成されて、ブリッジのネットワークインターフェースbr-29faca36a6b4172.168.0.1)が追加されました。 (ブリッジとブリッジのネットワークインターフェースは同一名になっています)

ルートテーブルを確認します。

$ ip route
default via 10.3.0.1 dev eth0 proto dhcp src 10.3.0.183 metric 100
10.3.0.0/24 dev eth0 proto kernel scope link src 10.3.0.183
10.3.0.1 dev eth0 proto dhcp scope link src 10.3.0.183 metric 100
172.17.0.0/16 dev docker0 proto kernel scope link src 172.17.0.1 linkdown
172.18.0.0/16 dev br-29faca36a6b4 proto kernel scope link src 172.18.0.1 linkdown

$ route -ne
Kernel IP routing table
Destination     Gateway         Genmask         Flags   MSS Window  irtt Iface
0.0.0.0         10.3.0.1        0.0.0.0         UG        0 0          0 eth0
10.3.0.0        0.0.0.0         255.255.255.0   U         0 0          0 eth0
10.3.0.1        0.0.0.0         255.255.255.255 UH        0 0          0 eth0
172.17.0.0      0.0.0.0         255.255.0.0     U         0 0          0 docker0
172.18.0.0      0.0.0.0         255.255.0.0     U         0 0          0 br-29faca36a6b4

コンテナを追加(container1)

sampleネットワークにコンテナを追加します。 コンテナ名はcontainer1にします。

$ sudo docker run -itd --name container1 --net sample  ubuntu:latest
daf864877355b6444832408add4afe7819c2e75446259aec9ca66ed399f692af

ホストにコンテナ(container1)とブリッジを接続するvethのブリッジ側ネットワークインターフェース(veth7a0c3b1@if6)が追加されています。

$ ip a
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    inet 127.0.0.1/8 scope host lo
       valid_lft forever preferred_lft forever
    inet6 ::1/128 scope host
       valid_lft forever preferred_lft forever
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 9001 qdisc fq_codel state UP group default qlen 1000
    link/ether 06:b5:10:1f:21:cd brd ff:ff:ff:ff:ff:ff
    inet 10.3.0.183/24 brd 10.3.0.255 scope global dynamic eth0
       valid_lft 1949sec preferred_lft 1949sec
    inet6 fe80::4b5:10ff:fe1f:21cd/64 scope link
       valid_lft forever preferred_lft forever
4: docker0: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc noqueue state DOWN group default
    link/ether 02:42:bc:b0:20:a7 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
5: br-29faca36a6b4: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default
    link/ether 02:42:c1:07:54:7f brd ff:ff:ff:ff:ff:ff
    inet 172.18.0.1/16 brd 172.18.255.255 scope global br-29faca36a6b4
       valid_lft forever preferred_lft forever
    inet6 fe80::42:c1ff:fe07:547f/64 scope link
       valid_lft forever preferred_lft forever
7: veth7a0c3b1@if6: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue master br-29faca36a6b4 state UP group default <----- container1のネットワークインターフェース
    link/ether 12:b9:a1:ec:09:76 brd ff:ff:ff:ff:ff:ff link-netnsid 0
    inet6 fe80::10b9:a1ff:feec:976/64 scope link
       valid_lft forever preferred_lft forever

sampleネットワークを確認します。

$ sudo docker network inspect sample
[
    {
        "Name": "sample",
        "Id": "29faca36a6b48487a16a1071c20dbe0b0b0bab3f6695d5d96324b80a9c3da773",
        "Created": "2022-06-08T11:33:26.165225737Z",
        "Scope": "local",
        "Driver": "bridge",
        "EnableIPv6": false,
        "IPAM": {
            "Driver": "default",
            "Options": {},
            "Config": [
                {
                    "Subnet": "172.18.0.0/16",
                    "Gateway": "172.18.0.1" <--- ブリッジのIP
                }
            ]
        },
        "Internal": false,
        "Attachable": false,
        "Ingress": false,
        "ConfigFrom": {
            "Network": ""
        },
        "ConfigOnly": false,
        "Containers": {
            "daf864877355b6444832408add4afe7819c2e75446259aec9ca66ed399f692af": {
                "Name": "container1",
                "EndpointID": "8233bb298d5fe7ef0d06af692a87c013677bdff42be7c22fbaa382105f3255c6",
                "MacAddress": "02:42:ac:12:00:02",
                "IPv4Address": "172.18.0.2/16",
                "IPv6Address": ""
            }
        },
        "Options": {},
        "Labels": {}
    }
]

container1が追加されています。

コンテナを追加(container2)

コンテナを追加します。

$ sudo docker run -itd --name container2 --net sample  ubuntu:latest
31abe1533cfed576910011ca990445da30dc641e2447ad63663b91175e52ea15

ホストにコンテナ(container2)とブリッジを接続するvethのブリッジ側ネットワークインターフェース(veth2a5f404@if8)が追加されています。

 ip a
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    inet 127.0.0.1/8 scope host lo
       valid_lft forever preferred_lft forever
    inet6 ::1/128 scope host
       valid_lft forever preferred_lft forever
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 9001 qdisc fq_codel state UP group default qlen 1000
    link/ether 06:b5:10:1f:21:cd brd ff:ff:ff:ff:ff:ff
    inet 10.3.0.183/24 brd 10.3.0.255 scope global dynamic eth0
       valid_lft 3439sec preferred_lft 3439sec
    inet6 fe80::4b5:10ff:fe1f:21cd/64 scope link
       valid_lft forever preferred_lft forever
4: docker0: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc noqueue state DOWN group default
    link/ether 02:42:bc:b0:20:a7 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
5: br-29faca36a6b4: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default
    link/ether 02:42:c1:07:54:7f brd ff:ff:ff:ff:ff:ff
    inet 172.18.0.1/16 brd 172.18.255.255 scope global br-29faca36a6b4
       valid_lft forever preferred_lft forever
    inet6 fe80::42:c1ff:fe07:547f/64 scope link
       valid_lft forever preferred_lft forever
7: veth7a0c3b1@if6: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue master br-29faca36a6b4 state UP group default
    link/ether 12:b9:a1:ec:09:76 brd ff:ff:ff:ff:ff:ff link-netnsid 0
    inet6 fe80::10b9:a1ff:feec:976/64 scope link
       valid_lft forever preferred_lft forever
9: veth2a5f404@if8: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue master br-29faca36a6b4 state UP group default <----- container2のネットワークインターフェース
    link/ether 3a:aa:3b:e3:ed:9b brd ff:ff:ff:ff:ff:ff link-netnsid 1
    inet6 fe80::38aa:3bff:fee3:ed9b/64 scope link
       valid_lft forever preferred_lft forever

sampleネットワークにコンテナが追加されています。

$ sudo docker network inspect sample
[
    {
        "Name": "sample",
        "Id": "29faca36a6b48487a16a1071c20dbe0b0b0bab3f6695d5d96324b80a9c3da773",
        "Created": "2022-06-08T11:33:26.165225737Z",
        "Scope": "local",
        "Driver": "bridge",
        "EnableIPv6": false,
        "IPAM": {
            "Driver": "default",
            "Options": {},
            "Config": [
                {
                    "Subnet": "172.18.0.0/16",
                    "Gateway": "172.18.0.1"
                }
            ]
        },
        "Internal": false,
        "Attachable": false,
        "Ingress": false,
        "ConfigFrom": {
            "Network": ""
        },
        "ConfigOnly": false,
        "Containers": {
            "31abe1533cfed576910011ca990445da30dc641e2447ad63663b91175e52ea15": {
                "Name": "container2",
                "EndpointID": "63366b86e00fae9705030e42606dd311807cd03330678d73372cb715d6b74d22",
                "MacAddress": "02:42:ac:12:00:03",
                "IPv4Address": "172.18.0.3/16",
                "IPv6Address": ""
            },
            "daf864877355b6444832408add4afe7819c2e75446259aec9ca66ed399f692af": {
                "Name": "container1",
                "EndpointID": "8233bb298d5fe7ef0d06af692a87c013677bdff42be7c22fbaa382105f3255c6",
                "MacAddress": "02:42:ac:12:00:02",
                "IPv4Address": "172.18.0.2/16",
                "IPv6Address": ""
            }
        },
        "Options": {},
        "Labels": {}
    }

ブリッジを確認

ブリッジbr-29faca36a6b4

$ brctl show br-29faca36a6b4
bridge name	bridge id		STP enabled	interfaces
br-29faca36a6b4		8000.0242c107547f	no		veth2a5f404
                                    veth7a0c3b1

コンテナの内部を確認

  • apt install iproute2 ipコマンド
  • apt install net-tools -y routeコマンド
container1
sudo docker exec -it container1 /bin/bash
root@daf864877355:/#
# ip a
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    inet 127.0.0.1/8 scope host lo
       valid_lft forever preferred_lft forever
6: eth0@if7: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default
    link/ether 02:42:ac:12:00:02 brd ff:ff:ff:ff:ff:ff link-netnsid 0
    inet 172.18.0.2/16 brd 172.18.255.255 scope global eth0
       valid_lft forever preferred_lft forever

コンテナのネットワークインターフェースeth0@if7はブリッジのネットワークインターフェースveth7a0c3b1@if6とvethで接続。

ルートテーブルを確認。

$ route -ne
Kernel IP routing table
Destination     Gateway         Genmask         Flags   MSS Window  irtt Iface
0.0.0.0         172.18.0.1      0.0.0.0         UG        0 0          0 eth0
172.18.0.0      0.0.0.0         255.255.0.0     U         0 0          0 eth0
  • 172.18.0.0/16は自身(0.0.0.0)、つまりルーティングしない
  • その他は172.18.0.1(br-29faca36a6b4)にルーティング
container2
sudo docker exec -it container2 /bin/bash
root@31abe1533cfe:/#
ip a
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    inet 127.0.0.1/8 scope host lo
       valid_lft forever preferred_lft forever
8: eth0@if9: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default
    link/ether 02:42:ac:12:00:03 brd ff:ff:ff:ff:ff:ff link-netnsid 0
    inet 172.18.0.3/16 brd 172.18.255.255 scope global eth0
       valid_lft forever preferred_lft forever

コンテナのネットワークインターフェースeth0@if9とブリッジのネットワークインターフェースveth2a5f404@if8が仮想イーサネットで接続。

root@31abe1533cfe:/# route -ne
Kernel IP routing table
Destination     Gateway         Genmask         Flags   MSS Window  irtt Iface
0.0.0.0         172.18.0.1      0.0.0.0         UG        0 0          0 eth0
172.18.0.0      0.0.0.0         255.255.0.0     U         0 0          0 eth0
  • 172.18.0.0/16のIPは0.0.0.0(自身に)、つまりルーティングしない。
  • 上記以外は172.18.0.1(br-29faca36a6b4)にルーティング
iptables

ホストのIPテーブルを見る。 ブリッジの仮想NICからホストの物理NICにIPマスカレードさえることを確認。

$ sudo iptables -t nat -nL -v
Chain PREROUTING (policy ACCEPT 21 packets, 1034 bytes)
 pkts bytes target     prot opt in     out     source               destination
   22   938 DOCKER     all  --  *      *       0.0.0.0/0            0.0.0.0/0            ADDRTYPE match dst-type LOCAL

Chain INPUT (policy ACCEPT 13 packets, 554 bytes)
 pkts bytes target     prot opt in     out     source               destination

Chain OUTPUT (policy ACCEPT 50 packets, 4834 bytes)
 pkts bytes target     prot opt in     out     source               destination
    0     0 DOCKER     all  --  *      *       0.0.0.0/0           !127.0.0.0/8          ADDRTYPE match dst-type LOCAL

Chain POSTROUTING (policy ACCEPT 50 packets, 4834 bytes)
 pkts bytes target     prot opt in     out     source               destination
    8   480 MASQUERADE  all  --  *      !br-29faca36a6b4  172.18.0.0/16        0.0.0.0/0
    0     0 MASQUERADE  all  --  *      !docker0  172.17.0.0/16        0.0.0.0/0

Chain DOCKER (2 references)
 pkts bytes target     prot opt in     out     source               destination
    0     0 RETURN     all  --  br-29faca36a6b4 *       0.0.0.0/0            0.0.0.0/0
    0     0 RETURN     all  --  docker0 *       0.0.0.0/0            0.0.0.0/0

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

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

$ 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サーバ利用できます。   内蔵DNSはDocker デーモンが提供します。

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

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

ホスト名

  • /etc/hostname/に記載された名前。
  • hostnameコマンドで確認できる。

同一のユーザー定義bridgeネットワークでは、IPアドレス、サービス名、ホスト名で通信できる。

sudo docker ps --all
CONTAINER ID   IMAGE     COMMAND                  CREATED          STATUS          PORTS                                   NAMES
bd1d6037c81e   httpd     "httpd-foreground"       29 minutes ago   Up 29 minutes   0.0.0.0:9000->80/tcp, :::9000->80/tcp   web-httpd2
b44bfea6fb4d   httpd     "httpd-foreground"       48 minutes ago   Up 48 minutes   0.0.0.0:8080->80/tcp, :::8080->80/tcp   web-httpd
2b4840ca369d   nginx     "/docker-entrypoint.…"   53 minutes ago   Up 53 minutes   0.0.0.0:8000->80/tcp, :::8000->80/tcp   web-nginx

Dockerの場合ホスト名はコンテナIDになる。 bd1d6037c81eにexecしてホスト名b44bfea6fb4dの80ポートからHTMLを取得する例。

root@bd1d6037c81e:/usr/local/apache2# curl --head b44bfea6fb4d:80
HTTP/1.1 200 OK
Date: Fri, 22 Jul 2022 23:53:54 GMT
Server: Apache/2.4.53 (Unix)
Last-Modified: Mon, 11 Jun 2007 18:53:14 GMT
ETag: "2d-432a5e4a73a80"
Accept-Ranges: bytes
Content-Length: 45
Content-Type: text/html