Docker の bridge と host ネットワークについて勉強する
勉強した時のメモ。
もし違っている部分などあればご指摘頂けますと幸いです。
なお、bridge で docker0 ではなく、ユーザーが定義した bridge も使えるようですが、こちらについては調査していません
参考
- Docker Reference Architecture: Designing Scalable, Portable Docker Container Networks
- Network overview
- Use bridge networks
- Dockerのネットワークの基礎
まとめ
- bridge・host いずれもインターネット経由でコンテナへのアクセスが可能
- bridge はホストの任意のポートをコンテナのポートにマップすることが出来る
- host はコンテナで expose されたポートをホストでも利用する。その為、一つのホストで同じポートを使うコンテナは利用できない
環境
ECS をよく使うので Amazon ECS-Optimized AMI(ami-f3f8098c) を使って確認。
$Containers: 1
Running: 1
Paused: 0
Stopped: 0
Images: 2
Server Version: 17.12.1-ce
Storage Driver: devicemapper
Pool Name: docker-docker--pool
Pool Blocksize: 524.3kB
Base Device Size: 10.74GB
Backing Filesystem: ext4
Udev Sync Supported: true
Data Space Used: 366.5MB
Data Space Total: 23.33GB
Data Space Available: 22.96GB
Metadata Space Used: 176.1kB
Metadata Space Total: 25.17MB
Metadata Space Available: 24.99MB
Thin Pool Minimum Free Space: 2.333GB
Deferred Removal Enabled: true
Deferred Deletion Enabled: true
Deferred Deleted Device Count: 0
Library Version: 1.02.135-RHEL7 (2016-11-16)
Logging Driver: json-file
Cgroup Driver: cgroupfs
Plugins:
Volume: local
Network: bridge host macvlan null overlay
Log: awslogs fluentd gcplogs gelf journald json-file logentries splunk syslog
Swarm: inactive
Runtimes: runc
Default Runtime: runc
Init Binary: docker-init
containerd version: 9b55aab90508bd389d7654c4baf173a981477d55
runc version: 9f9c96235cc97674e935002fc3d78361b696a69e
init version: 949e6fa
Security Options:
seccomp
Profile: default
Kernel Version: 4.14.33-51.37.amzn1.x86_64
Operating System: Amazon Linux AMI 2018.03
OSType: linux
Architecture: x86_64
CPUs: 1
Total Memory: 987.5MiB
Name: ip-172-31-20-151
ID: YBHO:HF57:Q7OR:UMAL:EZX7:GU7X:TMXR:C2A5:FZU7:B5MX:W6SR:OQ26
Docker Root Dir: /var/lib/docker
Debug Mode (client): false
Debug Mode (server): false
Registry: https://index.docker.io/v1/
Labels:
Experimental: false
Insecure Registries:
127.0.0.0/8
Live Restore Enabled: false
bridge ネットワーク
概要
以下より図を拝借。
Docker Reference Architecture: Designing Scalable, Portable Docker Container Networks
-
bridge
はデフォルトのネットワークドライバ。docker run
で--net
オプションを指定しない場合、bridge が利用される - bridge はデフォルトでは docker0という仮想ブリッジを通じて通信を行う
- コンテナ内部と仮想ブリッジ(docker0)は Virtual Ethernet Tunnel(veth)によって接続されている
- コンテナ内ではeth0を通じてネットワーク通信が出来る
以下より実際に操作しながら動きを見てみる。
bridge ネットワークのコンテナを起動してネットワークを確認する
まずはホスト側の状況を確認。
# host 側のネットワークインターフェースとして docker 0 が確認できる。
$ifconfig docker0
docker0 Link encap:Ethernet HWaddr 02:42:C3:DD:AF:A1
inet addr:172.17.0.1 Bcast:172.17.255.255 Mask:255.255.0.0
UP BROADCAST MULTICAST MTU:1500 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:0
RX bytes:0 (0.0 b) TX bytes:0 (0.0 b)
# brctl コマンドをインストール
$sudo yum install bridge-utils
# docker0 という仮想ブリッジが確認できる
$ brctl show
bridge name bridge id STP enabled interfaces
docker0 8000.0242c3ddafa1 no
OK.
docker0 は 172.0.1/16 となっている。
この状態で bridge のコンテナを起動してコンテナの IPアドレスを確認してみる。
docker runのドキュメントにもあるように何も指定しない場合、デフォルトの bridge を利用する。
# busybox イメージのコンテナを起動。echo をずっと出力させてコンテナが終了しないようにする
$docker run -d --name c1 busybox /bin/sh -c "while true; do echo hello world; sleep 1; done"
910e1fd20f69d0b01169af02e47f54c1b751cd065db5c1a8ffa76cb9b8c25a0e
# コンテナ内で /bin/sh を実行
$docker exec -it c1 /bin/sh
#コンテナ内で ip アドレスを見る
$ip address
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue 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
12: eth0@if13: <BROADCAST,MULTICAST,UP,LOWER_UP,M-DOWN> mtu 1500 qdisc noqueue
link/ether 02:42:ac:11:00:02 brd ff:ff:ff:ff:ff:ff
inet 172.17.0.2/16 brd 172.17.255.255 scope global eth0
valid_lft forever preferred_lft forever
# ルートテーブルのデフォルトゲートウェイはdocker0 の 172.17.0.1
$ip route
default via 172.17.0.1 dev eth0
172.17.0.0/16 dev eth0 scope link src 172.17.0.2
#ping で外に届く
$ping 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=46 time=4.349 ms
64 bytes from 8.8.8.8: seq=1 ttl=46 time=4.298 ms
^C
--- 8.8.8.8 ping statistics ---
2 packets transmitted, 2 packets received, 0% packet loss
round-trip min/avg/max = 4.298/4.323/4.349 ms
# traceroute を確認。172.17.0.1 にまずは接続している。実際に接続も出来る
$traceroute www.google.co.jp
traceroute to www.google.co.jp (172.217.161.67), 30 hops max, 46 byte packets
1 ip-172-17-0-1.ap-northeast-1.compute.internal (172.17.0.1) 0.005 ms 0.004 ms 0.002 ms
2 ec2-175-41-192-146.ap-northeast-1.compute.amazonaws.com (175.41.192.146) 14.286 ms 21.584 ms ec2-175-41-192-152.ap-northeast-1.compute.amazonaws.com (175.41.192.152) 20.200 ms
(省略)
17 108.170.242.193 (108.170.242.193) 5.247 ms nrt20s09-in-f3.1e100.net (172.217.161.67) 5.338 ms 108.170.242.193 (108.170.242.193) 5.179 ms
# sh を終了してホストの操作をする
$exit
# docker0 ブリッジに veth012d945 というインターフェースが追加されている。
$brctl show
bridge name bridge id STP enabled interfaces
docker0 8000.0242c3ddafa1 no veth012d945
# ifconfig で veth を確認
$ifconfig veth012d945
veth012d945 Link encap:Ethernet HWaddr EA:66:DE:92:63:6C
inet6 addr: fe80::e866:deff:fe92:636c/64 Scope:Link
UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1
RX packets:0 errors:0 dropped:0 overruns:0 frame:0
TX packets:15 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:0
RX bytes:0 (0.0 b) TX bytes:1186 (1.1 KiB)
$ NetworkMode は default
$docker inspect c1 --format '{{ .HostConfig.NetworkMode }}'
default
# docker inspect でネットワークの設定が諸々見える
$docker inspect --format '{{json .NetworkSettings }}' c1 | jq .
{
"Bridge": "",
"SandboxID": "1f2e8d7244945d72f268c63f88172899807d0dce91aaa9cbbbb60f330079250d",
"HairpinMode": false,
"LinkLocalIPv6Address": "",
"LinkLocalIPv6PrefixLen": 0,
"Ports": {},
"SandboxKey": "/var/run/docker/netns/1f2e8d724494",
"SecondaryIPAddresses": null,
"SecondaryIPv6Addresses": null,
"EndpointID": "d49d1d764911932df51249ca9af7a6e51f6863ab4fb30bc05dd1536c470b6d7e",
"Gateway": "172.17.0.1",
"GlobalIPv6Address": "",
"GlobalIPv6PrefixLen": 0,
"IPAddress": "172.17.0.2",
"IPPrefixLen": 16,
"IPv6Gateway": "",
"MacAddress": "02:42:ac:11:00:02",
"Networks": {
"bridge": {
"IPAMConfig": null,
"Links": null,
"Aliases": null,
"NetworkID": "e35b6ce31179971ba74b870093a61596a8b031cffe02452c1b5bde6f45bf49d7",
"EndpointID": "d49d1d764911932df51249ca9af7a6e51f6863ab4fb30bc05dd1536c470b6d7e",
"Gateway": "172.17.0.1",
"IPAddress": "172.17.0.2",
"IPPrefixLen": 16,
"IPv6Gateway": "",
"GlobalIPv6Address": "",
"GlobalIPv6PrefixLen": 0,
"MacAddress": "02:42:ac:11:00:02",
"DriverOpts": null
}
}
}
分かったこと
- bridge でコンテナを作成すると veth が作成される
- コンテナで内では docker0 の IP(172.0.1/16)で IPアドレスが割り振られる
- コンテナでは docker0 の IPアドレスがデフォルトの Gateway として設定され、こちらを通じてホスト経由でインターネットへの接続ができる
bridge コンテナに外部からアクセスする
外部からコンテナにアクセスする場合、-p
もしくは--publish
オプションを利用してコンテナのポートをホストに publish する。
Publish or expose port (-p, --expose)
# nginx イメージを pull する
$docker pull nginx
# nginx イメージでは 80ポートを expose している
$docker image inspect nginx --format '{{ .Config.ExposedPorts }}'
map[80/tcp:{}]
# ホストの5000ポートをコンテナの80番ポートにマップする
$docker run -d --name C2 -p 5000:80 nginx
# PORTS 列でもポートのマッピングが分かる
$docker ps -f "name=C2"
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
28af5b8a506f nginx "nginx -g 'daemon of…" 2 minutes ago Up 2 minutes 0.0.0.0:5000->80/tcp C2
# ホストの5000ポートへのアクセスでコンテナの80番ポートにアクセスする
$curl http://localhost:5000
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
<style>
body {
width: 35em;
margin: 0 auto;
font-family: Tahoma, Verdana, Arial, sans-serif;
}
</style>
</head>
<body>
<h1>Welcome to nginx!</h1>
<p>If you see this page, the nginx web server is successfully installed and
working. Further configuration is required.</p>
<p>For online documentation and support please refer to
<a href="http://nginx.org/">nginx.org</a>.<br/>
Commercial support is available at
<a href="http://nginx.com/">nginx.com</a>.</p>
<p><em>Thank you for using nginx.</em></p>
</body>
</html>
上記についても図があると分かりやすいのでこちらも拝借。
Docker Reference Architecture: Designing Scalable, Portable Docker Container Networks
次に同じホストからではなく、インターネット経由でコンテナにアクセスしみる。
# 停止
$docker stop C2
# 今度はホストの80番をコンテナの80番にマップする
$docker run -d --name C3 -p 80:80 nginx
# この状態で外部から対象の PublicIP にアクセスするとコンテナにアクセスできる。
$curl http://xxxx/
<!DOCTYPE html>
<html>
<head>
# 停止しておく
$docker stop C3
OK.
Host ネットワーク
概要
こちらも概要図があると分かりやすい。
Host ではホスト側のネットワーク・インターフェースを利用する。
試す
先程動作させた nginx コンテナを bridge ではなく、host で動かす。
docker run
で --net=host
とすることでコンテナは bridge ドライバではなく、ホストのネットワークを利用するように起動できる。
# host ネットワークでコンテナを起動
$docker run -d --name host --net host nginx
# PORTS 列が host となっている
$docker ps -f "name=host"
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
0d22b4718e80 nginx "nginx -g 'daemon of…" 48 seconds ago Up 47 seconds host
# アクセス可能
$curl http://localhost/
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
<style>
# NetworkMode が host になっている。
$docker inspect host --format '{{ .HostConfig.NetworkMode }}'
host
# 同じようにインターネット経由でもアクセス可能
$curl http://xxxxx/
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
OK.
ただし、こちらは docker run からも分かるようにポートのマッピングはしていない。
上記より、例えば 80番ポートのコンテナを一つのホストで動かしたい という事はできない。