Dockerのネットワークの基礎

今までいろいろ触ってきて,Dockerネットワーク周りに関しては何となくは理解していたが,人に説明できるほど理解してなかったのでまとめておく.基本は,Advanced networking - Docker Documentationがベースになっている.

仮想ブリッジの仕組み

Dockerのネットワークは,仮想ブリッジdocker0を通じて管理され,他のネットワークとは隔離された環境で動作する.

Dockerデーモンを起動すると,

  • 仮想ブリッジdocker0の作成
  • ホストの既存ルートからの空きのIPアドレス空間を検索
  • 空きから特定の範囲のIPアドレス空間を取得
  • 取得したIPアドレス空間をdocker0に割り当て

が行われる.

コンテナを起動すると,コンテナには以下が割り当てられる.

  • docker0に紐づいたveth(Virtual Ethernet)インターフェース
  • docker0に割り当てられたIPアドレス空間から専用のIPアドレス

そしてdocker0はコンテナのデフォルトのgatewayとして利用されるようになる.コンテナに付与されるvethは仮想NICで,コンテナ側からはeth0として見える.2つはチューブのように接続され,あらゆるやりとりはここを経由して行われるようになる.

実際にコンテナを起動して確認する.まず,インターフェースから.

$ brctl show
bridge name     bridge id               STP enabled     interfaces
docker0         8000.000000000000       no
$ docker run -d ubuntu /bin/sh -c "while true; do echo hello world; sleep 1; done"
b9ffb0800ca5
$ docker run -d ubuntu /bin/sh -c "while true; do echo hello world; sleep 1; done"
4c0d9b786e8f
$ brctl show
bridge name     bridge id               STP enabled     interfaces
docker0         8000.7ab1e2001566       no              veth29c1, veth9eb7

b9ffb0800ca5コンテナにはveth29c1が,4c0d9b786e8fコンテナにはveth9eb7がそれぞれ割り当てられているのがわかる.

次にIPを見てみる.

$ ifconfig docker0
docker0   Link encap:Ethernet  HWaddr 7a:b1:e2:00:15:66
          inet addr:172.17.42.1  Bcast:0.0.0.0  Mask:255.255.0.0
$ docker inspect --format '{{ .NetworkSettings.IPAddress }}' b9ffb0800ca5
172.17.0.2
$ docker inspect --format '{{ .NetworkSettings.IPAddress }}' 4c0d9b786e8f
172.17.0.3

docker0に割り当てられた172.17.42.1/16のうち,b9ffb0800ca5コンテナには172.17.02が,4c0d9b786e8fコンテナには172.17.0.3がそれぞれ割り当てられているのがわかる.

コンテナ同士のやりとり

コンテナ間のやりとりの制御は,Dockerデーモンの-iccパラメータにより行う.Dockerはこれにiptablesを使っている.

  • -icc=trueとすると,コンテナ間のやりとりが可能になる(default)
  • -icc=falseとすると,コンテナ同士は隔離される

さらに,コンテナ同士でやりとりするにはportをdocker0に晒す必要がある.これには,以下の2つの方法がある.

  • DockerfileにEXPOSE <port>を記述する
  • コンテナ起動時に--expose <port>を指定する

具体的に,docker0を通じてコンテナ同士を接続する場合は,link機能を使う.コンテナを起動する際に,--link コンテナ名:エイリアス名とすると,環境変数を通じて接続したいコンテナのIPやPortを取得できるようになる(詳しくは,“Dockerコンテナ間のlink,database.ymlの書き方”に書いた).

外部ネットワークからコンテナへのアクセス

外部ネットワークからコンテナにアクセスするには,コンテナを起動するときに外部ポートをdocker0に晒した内部portにマップする必要がある.

例えば,ホストの8080ポートをコンテナの80ポートにマップしてApacheコンテナを起動してみる.

$ ID=$(docker run -d -p 8080:80 tcnksm/apache)
caad0cfc2a0

マッピングは以下で確認できる.

$ docker ps
CONTAINER ID        IMAGE                  COMMAND                CREATED              STATUS              PORTS                  NAMES
caad0cfc2a03        tcnksm/apache:latest   /usr/sbin/apache2 -D   About a minute ago   Up About a minute   0.0.0.0:8080->80/tcp   elegant_thompson

IDと晒したportを基に確認することもできる.

$ docker port $ID 80
0.0.0.0:8080

実際に接続してみる.

$ curl http://localhost:8080
Hello, docker

ホスト側のIPの指定を省略することもできる.この場合,自動でポートが選ばれる.

$ ID=$(docker run -d -p 80 tcnksm/apache)
$ docker port $ID 80
0.0.0.0:49156
$ curl `docker port $ID 80`
Hello, docker

参考