Help us understand the problem. What is going on with this article?

Docker の bridge と host ネットワークについて勉強する

More than 1 year has passed since last update.

Docker の bridge と host ネットワークについて勉強する

勉強した時のメモ。
もし違っている部分などあればご指摘頂けますと幸いです。
なお、bridge で docker0 ではなく、ユーザーが定義した bridge も使えるようですが、こちらについては調査していません

参考

まとめ

  • 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-driver.png

  • 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

nat.png

次に同じホストからではなく、インターネット経由でコンテナにアクセスしみる。

# 停止
$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 ではホスト側のネットワーク・インターフェースを利用する。

host-driver.png

試す

先程動作させた 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番ポートのコンテナを一つのホストで動かしたい という事はできない。

toshihirock
こちらは個人の意見で会社とは関係ありません。お約束です。
http://toshihirock.blogspot.jp/
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした