Dockerのマルチホストネットワークで複数ホスト間を繋ぐ仮想ネットワークを作る(Dockerの最新機能を使ってみよう:第1回)

進化しているDockerのネットワーク機能

DockerはLinuxカーネルが持つ名前空間(namespace)機構を使い、プロセスやリソースを隔離するツールとして開発が始められた。そのような背景もあってか、開発当初はネットワーク関連機能としては仮想ブリッジとiptablesを使ったパケットルーティング程度の機能しか備えられていなかった。そのため、Dockerを使ってコンテナを動かすマシン(Dockerホスト)を複数台用意して動かすような環境の場合、別途仮想ネットワーク構築ツールを用意する必要があった。

この状況が変わり始めたのが、2015年6月16日にリリースされたバージョン1.7からである。バージョン1.7ではネットワークスタックがリライトされて「libnetwork」という別モジュールに分離されると同時に、異なるDockerホストをまたぐ仮想ネットワークの構築機能の実験的実装がDockerに追加された。2015年11月3日にリリースされたバージョン1.9ではこの仮想ネットワーク機能が正式な機能となり、ついにDocker単独での仮想ネットワーク構築が可能となった。2016年2月4日にリリースされたDocker 1.10では従来はbridgeネットワークでのみ利用できたリンク機能がほかのネットワークでも利用可能になり、また外部ネットワークと通信できない「内部ネットワーク」を作成できるようになるなど、ネットワーク関連機能が強化されている。

Dockerのネットワーク機能の基本

このように機能強化が進められているDockerのネットワーク機構であるが、その基本となるのはブリッジを使った仮想ネットワークだ。まずはこのブリッジによる仮想ネットワークについて、そのデフォルトの動作を簡単に説明しておこう。

DockerではDockerホスト上に仮想ブリッジを利用してネットワークの端点を作り、それをコンテナの仮想ネットワークインターフェイスに仮想的に接続することで、コンテナとのネットワーク通信を可能にしている(図1)。

図1 仮想ブリッジを使用したコンテナネットワーク図1 仮想ブリッジを使用したコンテナネットワーク
図1 仮想ブリッジを使用したコンテナネットワーク

 Dockerホスト上で作成されているネットワークブリッジは、brctlコマンドで確認できる。

# brctl show
bridge name     bridge id               STP enabled     interfaces
docker0         8000.02429125675e       no
docker_gwbridge         8000.0242ef401d36       no

これらネットワークブリッジにはそれぞれIPアドレスが割り当てられており、これを経由してコンテナとの通信が行われる。先の例では、以下のように「docker0」ブリッジには172.17.0.1/16、「docker_gwbridge」には172.18.0.1/16というIPアドレスが割り当てられていることが確認できる。

# ip a
  
  
3: docker0: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc noqueue state DOWN group default
    link/ether 02:42:91:25:67:5e brd ff:ff:ff:ff:ff:ff
    inet 172.17.0.1/16 scope global docker0
       valid_lft forever preferred_lft forever
    inet6 fe80::42:91ff:fe25:675e/64 scope link
       valid_lft forever preferred_lft forever
70: docker_gwbridge: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc noqueue state DOWN group default
    link/ether 02:42:ef:40:1d:36 brd ff:ff:ff:ff:ff:ff
    inet 172.18.0.1/16 scope global docker_gwbridge
       valid_lft forever preferred_lft forever
    inet6 fe80::42:efff:fe40:1d36/64 scope link
       valid_lft forever preferred_lft forever

この環境で新たに仮想マシンを作成すると、新たにdocker0ブリッジに接続された仮想ネットワークインターフェイスが作成される。この仮想ネットワークインターフェイスはコンテナの仮想ネットワークインターフェイスに対応付けられており、コンテナへのパケットの出入りはこの仮想ネットワークインターフェイスを経由して行われる。

たとえば、次のようにして新たなコンテナを作成してみよう。なおここで作成している「busybox」コンテナは、最小限のシェル環境を備えるコンテナだ。

# docker run -ti --rm busybox

すると、Dockerホスト上では次のような仮想ネットワークインターフェイスが新たに作成される。

# ip a
  
  
180: veth7044735@if179: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue master docker0 state UP group default
    link/ether ae:9a:34:77:96:e9 brd ff:ff:ff:ff:ff:ff link-netnsid 13
    inet6 fe80::ac9a:34ff:fe77:96e9/64 scope link
       valid_lft forever preferred_lft forever

brctlコマンドで確認すると、この仮想ネットワークインターフェイスがdocker0ブリッジに接続されていることが分かる。

# brctl show
bridge name     bridge id               STP enabled     interfaces
docker0         8000.02429125675e       no              veth7044735
docker_gwbridge         8000.0242ef401d36       no

コンテナ内でネットワークインターフェイスに割り当てられたIPアドレスを確認すると、docker0ブリッジと同じネットワークに属する172.17.0.2/16というIPアドレスが割り当てられていることが分かる。

/ # ip a
  
  
179: eth0@if180: <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 scope global eth0
       valid_lft forever preferred_lft forever
    inet6 fe80::42:acff:fe11:2/64 scope link
       valid_lft forever preferred_lft forever

Dockerホスト上で複数のコンテナを起動した場合、これと同様にそのコンテナに対応する仮想ネットワークインターフェイスが作成され、それがdocker0ブリッジに接続される。この場合、すべてのコンテナは同一の仮想ネットワーク内に存在するため、コンテナ同士はこの仮想ネットワークを通じて通信でき、さらにコンテナ-ホスト間の通信も可能となる。しかし、Dockerホストが複数存在する場合、デフォルトでは各Dockerホスト上に構築された仮想ネットワーク同士は接続されていないため、異なるDockerホスト上にあるコンテナはそのままではお互いに通信は行えない(図2)。

図2 ブリッジネットワークの場合、異なるDockerホスト上にあるコンテナ同士はそのままでは通信できない図2 ブリッジネットワークの場合、異なるDockerホスト上にあるコンテナ同士はそのままでは通信できない
図2 ブリッジネットワークの場合、異なるDockerホスト上にあるコンテナ同士はそのままでは通信できない

 そのため、異なるDockerホスト上にあるコンテナ間で通信を行いたい場合はコンテナの起動時に「-p」オプションを使用してDockerホストからのポートフォワーディングを利用するか、flannelなどの仮想ネットワーク構築ソフトウェアを使って別途複数のDockerホストにまたがる仮想ネットワークを構築する必要があった。

Docker 1.9で導入されたマルチホストネットワーク機能

Docker 1.9ではネットワーク機能が刷新され、新たに複数のDockerホストにまたがる仮想ネットワークを構築・管理する機構であるマルチホストネットワーク機能が導入された。マルチホストネットワーク機能は「ドライバ」と呼ばれるプラグイン形式で実装されており、さらに仮想ネットワーク技術であるVXLANベースでマルチホストネットワークを実現するドライバも標準で提供されるようになった。これによって、従来は別途用意する必要があったDockerホスト間を繋ぐ仮想ネットワークをDocker単体で実現できるようになったのである(図3)。

図3 VXLANベースの仮想ネットワークを構築することで、異なるDockerホスト上にある仮想ネットワークを接続されているように扱える図3 VXLANベースの仮想ネットワークを構築することで、異なるDockerホスト上にある仮想ネットワークを接続されているように扱える
図3 VXLANベースの仮想ネットワークを構築することで、異なるDockerホスト上にある仮想ネットワークを接続されているように扱える

 なお、Docker標準のマルチホストネットワークドライバ(「overlay」ドライバ)以外のドライバを利用することも可能だ。これらのうちいくつかはDocker公式ドキュメントのプラグインページに掲載されているが、Dockerクラスタ構築支援ツールを提供するWeaveによるものや、ポリシーベースのコンテナ向けネットワーク/ストレージを提供するContivが提供するもの、IaaS型クラウド構築ソフトウェア「OpenStack」の仮想ネットワーク管理機構を利用する「Kuryr」などがすでにリリースされている。

「docker network」コマンドによるネットワーク管理

前述のとおり、Docker 1.9以降ではネットワーク関連が刷新され、新たに「docker network」コマンドを利用して仮想ネットワークの作成や管理を行うようになった。以下では、この「docker network」コマンドによるネットワーク管理について紹介していこう。

なお、記事執筆時点では最新のDocker 1.10を安定版パッケージとして提供しているLinuxディストリビューションは少なかったため、検証環境としてはFedora 24向けの開発版パッケージとして提供されているDocker 1.10.3(docker-1.10.3-4.gitf8a9a2a.fc24.x86_64)をFedora 23にインストールして利用している。

単一ホスト向けのネットワーク作成

ネットワークの作成は、「docker network create <ネットワーク名>」コマンドで行える。たとえば「testnet01」というネットワークを作成する場合、以下のようになる。

# docker network create testnet01
bd48adb5b4a787cb499640fabff3b2a4bd5c13077b954926443298bf5ad297bc

使用するドライバは「-d」オプションで指定できるが、この例のように省略するとブリッジ接続を利用する「bridge」ドライバが使用される。

作成済みのネットワークは、「docker network ls」コマンドで確認できる。

# docker network ls
NETWORK ID          NAME                DRIVER
ad6ade0d2b2e        none                null
7eee9e7be7e3        host                host
e492e130b418        bridge              bridge
bd48adb5b4a7        testnet01           bridge

ちなみに、作成したネットワークを削除するには「docker network rm」コマンドを使用する。

# docker network rm testnet01

コンテナの作成時に「--net=<ネットワーク名>」オプションを指定することで、作成したコンテナを指定したネットワークに接続できる。次の例はMySQLコンテナを作成し、testnet01ネットワークに接続するものだ。

# docker run --name mysql01 --net=testnet01 -e MYSQL_ROOT_PASSWORD=<MySQLのrootパスワード> -d mysql

ネットワークのIPアドレスや接続されているコンテナなどの情報は、「docker network inspect」コマンドで確認できる。

# docker network inspect testnet01
[
    {
        "Name": "testnet01",
        "Id": "bd48adb5b4a787cb499640fabff3b2a4bd5c13077b954926443298bf5ad297bc",
        "Scope": "local",
        "Driver": "bridge",
        "IPAM": {
            "Driver": "default",
            "Options": {},
            "Config": [
                {
                    "Subnet": "172.18.0.0/16",
                    "Gateway": "172.18.0.1/16"
                }
            ]
        },
        "Containers": {
            "6bae0a01a426bb41acc95decba22e62d8d61d15ce6509f315566c67341c9677b": {
                "Name": "mysql01",
                "EndpointID": "78401ea23166d967bff762d2cce92362e7743af9c332b176d58082fc3b5d42b7",
                "MacAddress": "02:42:ac:12:00:02",
                "IPv4Address": "172.18.0.2/16",
                "IPv6Address": ""
            }
        },
        "Options": {}
    }
]

この例では、testnet01ネットワークには172.18.0.0/16というIPアドレスが割り当てられ、ゲートウェイは172.18.0.1、mysq01コンテナには172.18.0.2というIPアドレスが割り当てられていることが分かる。

DockerホストからはこのIPアドレスを使ってコンテナへアクセスを行える。たとえば、以下のようにしてmysql01コンテナで稼動しているMySQLサーバーにアクセスできる。

# mysql -u root -h 172.18.0.2 -p
Enter password:
Welcome to the MariaDB monitor.  Commands end with ; or \g.
Your MySQL connection id is 2
Server version: 5.7.11 MySQL Community Server (GPL)

Copyright (c) 2000, 2015, Oracle, MariaDB Corporation Ab and others.

Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.

MySQL [(none)]>

ホストからコンテナへの通信の場合、先のように「docker network inspect」コマンドなどを利用して割り当てられたIPアドレスを取得できるが、コンテナ内からはどのコンテナにどのIPアドレスが割り当てられているかは分からない。そのため、同じネットワーク内に接続されているコンテナ同士の通信は、コンテナ名、もしくは「<コンテナ名>.<ネットワーク名>」というホスト名を使って行えるようになっている。たとえば先に作成しているmysql01コンテナは、同じネットワークにあるほかのコンテナからは「mysql01」もしくは「mysql01.testnet01」というホスト名でアクセスが可能だ。

以下の例は、これを利用して先に作成したmysql01コンテナ上で稼動しているデータベースに接続するWordPressコンテナを作成するものだ。ここでは「-e」オプションで環境変数として使用するデータベースの情報を指定している。

$ docker run --name wp01 --net=testnet01 -p 80:80 -e WORDPRESS_DB_HOST=mysql01.testnet01 -e WORDPRESS_DB_USER=wordpress -e WORDPRESS_DB_PASSWORD=<MySQLのrootパスワード> -d wordpress

このホスト名解決は、Docker 1.9では/etc/hostsファイル経由で行われていたが、Docker 1.10以降ではDockerが内部的にDNSサーバーを立ち上げるようになっており、そのDNSサーバー経由で名前解決が行われるようになっている。そのため、/etc/hostsファイルにはそれらの情報が記述されないので注意したい。

複数のDockerホストにまたがるネットワーク(マルチホストネットワーク)を利用するための設定

続いて、今度は複数のDockerホストにまたがるネットワーク(マルチホストネットワーク)を作成してみよう。前述のとおりDockerでは複数のネットワークドライバが利用できるが、今回はDockerに標準で付属するoverlayドライバを使用して環境を構築する。

overlayドライバでは、各種設定を各Dockerホストで共有するためにKey-valueストアを使用する。Key-valueストアへのアクセスには「libkv」というライブラリを使用するようになっており、現時点ではConsulおよびEtcdApache ZooKeeperがサポートされている。このうち、今回はインストールや設定が容易なEtcdを利用して設定共有を行うこととする。

EtcdはCoreOSが開発している分散型Key-valueストアで、すでに多くのディストリビューションでパッケージが提供されている。たとえばDebianやUbuntuでは「apt-get」、Red Hat Enterprise Linux/CentOSでは「yum」、Fedoraでは「dnf」といったパッケージマネージャ経由でのインストールが可能だ。今回検証に使用したFedora 23では、以下のようにしてインストールが行える。

# dnf install etcd

Etcdは分散型Key-valueストアであり、実際の運用環境では複数台で分散運用するのが一般的だが、今回はテストということで1台のマシンだけで稼動させ、そこに各Dockerホストがアクセスする形とした。

なお、Fedora 23のEtcdのデフォルト設定ではlocalhostからのみアクセスを受け付ける設定になっているので、/etc/etcd/etcd.confファイルを修正して外部から接続できるよう設定を変更しておく必要がある。具体的には、下記の「localhost」をそのホストのIPアドレスに変更している。

(修正前)
ETCD_LISTEN_CLIENT_URLS="http://localhost:2379"
  
  
ETCD_ADVERTISE_CLIENT_URLS="http://localhost:2379"

変更後の設定ファイルは以下のようになる。

(修正後)
ETCD_LISTEN_CLIENT_URLS="http://<ホストのIPアドレス>:2379"
  
  
ETCD_ADVERTISE_CLIENT_URLS="http://<ホストのIPアドレス>:2379"

また、ほかのホストからEtcdが使用するTCPの2379および2380番ポートにアクセスできるよう、ファイアウォール等の設定を行っておく。Fedora 23であれば、以下のように「firewall-cmd」コマンドで設定が可能だ。

# firewall-cmd --add-port=2379/tcp
# firewall-cmd --add-port=2380/tcp

以上の設定が完了したら、次のようにEtcdを起動させておく。

# systemctl start etcd

続いてDockerホスト側の設定を行っていく。この作業は、使用するDockerホストそれぞれで必要となる。まず、Dockerのネットワーク設定が記述されている/etc/sysconfig/docker-networkファイル内の「DOCKER_NETWORK_OPTIONS」部分を次のように変更する。

DOCKER_NETWORK_OPTIONS='--cluster-store=etcd://*<Etcdを稼動させているホストのIPアドレス>:2379 --cluster-advertise=<そのホストのIPアドレス>:2376'

注意したいのが、「--cluster-store」ではEtcdを稼動させているホストのIPアドレスを、「--cluster-advertise」にはそのDockerホスト自身のIPアドレスを指定する点だ。

また、DockerホストからEtcdに対し通信が行えるかを確認しておこう。これは、次のようにcurlコマンドでテストできる。

# curl -L http://<Etcd稼動ホストのIPアドレス>:2379/version
{"etcdserver":"2.2.5","etcdcluster":"2.2.0"}

また、overlayドライバではUDPの4789番ポートとTCPおよびUDPの7946番ポートを使用する。これに加え、Docker同士の通信に使用するTCPの2376番ポートにほかのDockerホストからアクセスできるよう、ファイアウォールの設定を行っておく。

# firewall-cmd --add-port=4789/udp
# firewall-cmd --add-port=7946/udp
# firewall-cmd --add-port=7946/tcp
# firewall-cmd --add-port=2376/tcp

以上で設定は完了だ。最後にDockerサービスを再起動させておく。

# systemctl restart docker

マルチホストネットワークの作成と利用

それでは、マルチホストネットワークを実際に作成して使用してみよう。まず、次のように「-d」オプションでoverlayドライバを指定して「docker network create」コマンドを実行してネットワークを作成する。ここではネットワーク名として「testnet02」を指定した。

# docker network create -d overlay testnet02
a35a95625a09b2b7a680ec59932f323f4d5f7596b970ac3808449244eb03f7c7

「docker network inspect」コマンドでこのネットワークの情報を確認すると、bridgeドライバで作成したネットワークとは異なるネットワークアドレスが割り当てられていることが分かる。

# docker network inspect testnet02
[
    {
        "Name": "testnet02",
        "Id": "a35a95625a09b2b7a680ec59932f323f4d5f7596b970ac3808449244eb03f7c7",
        "Scope": "global",
        "Driver": "overlay",
        "IPAM": {
            "Driver": "default",
            "Options": null,
            "Config": [
                {
                    "Subnet": "10.0.0.0/24",
                    "Gateway": "10.0.0.1/24"
                }
            ]
        },
        "Containers": {},
        "Options": {}
    }
]

作成されたネットワークは、すべてのDockerホスト内で共有される。たとえば「docker create network」コマンドを実行したのとは異なるDockerホストで「docker network ls」コマンドを実行してみると、このホスト上でも先ほど作成した「testnet02」ネットワークが認識されていることが分かる。

# docker network ls
NETWORK ID          NAME                DRIVER
a35a95625a09        testnet02           overlay
1a1ecc8ea3f7        bridge              bridge
ae6eadc28386        none                null
7c66cf43a8cc        host                host

続いて、このネットワークをコンテナに接続してみよう。「docker network connect」コマンドを利用すれば、すでに稼動しているコンテナに別のネットワークを接続することが可能だ。まずは本記事の初めに作成したmysql01コンテナに、testnet02ネットワークを接続してみよう。

# docker network connect testnet02 mysql01

接続後、「docker inspect」コマンドでmysql01コンテナの情報を見てみると、testnet01およびtestnet02の2つのネットワークに対応したIPアドレスが割り当てられていることが分かる。

# docker inspect mysql01
  
  
            "Networks": {
                "testnet01": {
                    "IPAMConfig": null,
                    "Links": null,
                    "Aliases": null,
                    "NetworkID": "bd48adb5b4a787cb499640fabff3b2a4bd5c13077b954926443298bf5ad297bc",
                    "EndpointID": "0d0c13fa6d3f266411e07980e8077fcc1bfce303deb9560a96ab13cf1e877283",
                    "Gateway": "172.18.0.1",
                    "IPAddress": "172.18.0.2",
                    "IPPrefixLen": 16,
                    "IPv6Gateway": "",
                    "GlobalIPv6Address": "",
                    "GlobalIPv6PrefixLen": 0,
                    "MacAddress": "02:42:ac:12:00:02"
                },
                "testnet02": {
                    "IPAMConfig": {},
                    "Links": null,
                    "Aliases": null,
                    "NetworkID": "a35a95625a09b2b7a680ec59932f323f4d5f7596b970ac3808449244eb03f7c7",
                    "EndpointID": "0cc20ccf5c9f6a1128f1c3948c5e3065fbdc8d75b47ba74083c139f4bc278a8c",
                    "Gateway": "",
                    "IPAddress": "10.0.0.2",
                    "IPPrefixLen": 24,
                    "IPv6Gateway": "",
                    "GlobalIPv6Address": "",
                    "GlobalIPv6PrefixLen": 0,
                    "MacAddress": "02:42:0a:00:00:02"
                }
  
  

これで、testnet02ネットワーク内のコンテナからmysql01コンテナ内で稼動しているMySQLに「mysql01.testnet02」というホスト名を使ってアクセスできるようになる。もちろん、mysql01コンテナを稼動させているDockerホスト以外のDockerホスト上で稼動しているコンテナからもアクセスが可能だ。たとえば、別のDockerホスト上で次のようにWordPressコンテナを立ち上げて、WordPressを稼動させることができる。

# docker run --name wp01 --net=testnet02 -p 80:80 -e WORDPRESS_DB_HOST=mysql01.testnet02 -e WORDPRESS_DB_USER=wordpress -e WORDPRESS_DB_PASSWORD=<MySQLのrootパスワード> -d wordpress

なお、overlayドライバが割り当てたIPアドレス(この例ではtestnet02に接続された「10.0.0.2」というIPアドレス)には、そのコンテナを稼動させているDockerホストからもアクセスできない点に注意したい。

↓bridgeドライバで割り当てられたIPアドレスへのping
# ping 172.18.0.1
PING 172.18.0.1 (172.18.0.1) 56(84) bytes of data.
64 bytes from 172.18.0.1: icmp_seq=1 ttl=64 time=0.062 ms
64 bytes from 172.18.0.1: icmp_seq=2 ttl=64 time=0.075 ms
^C
--- 172.18.0.1 ping statistics ---
2 packets transmitted, 2 received, 0% packet loss, time 999ms
rtt min/avg/max/mdev = 0.062/0.068/0.075/0.010 ms

↓overlayドライバで割り当てられたIPアドレスへのping
# ping 10.0.0.2
PING 10.0.0.2 (10.0.0.2) 56(84) bytes of data.
^C
--- 10.0.0.2 ping statistics ---
4 packets transmitted, 0 received, 100% packet loss, time 2999ms
↑パケットが到達せず、通信できない

マルチホストネットワークでのリンク機能

「docker run」コマンドでは、コンテナから別のコンテナにアクセスするための別名(エイリアス)を指定する「--link」オプションが用意されていた。Docker 1.9まではこのオプションはbridgeネットワークでしか利用できなかったが、Docker 1.10ではそれ以外のネットワークでも利用が可能になっている。

「--link」オプションでは、「--link=<コンテナ名>:<エイリアス名>」といった形でコンテナ名およびエイリアス名を指定できる。例えば「--link=mysql01:db」のように指定すると、そのコンテナ内からは「db」というホスト名でmysql01コンテナにアクセスが可能になる。

# docker run -it --rm --net=testnet02 --link=mysql01:db busybox
/ # ping db
PING db (10.0.0.2): 56 data bytes
64 bytes from 10.0.0.2: seq=0 ttl=64 time=0.173 ms
64 bytes from 10.0.0.2: seq=1 ttl=64 time=0.114 ms
^C
--- db ping statistics ---
2 packets transmitted, 2 packets received, 0% packet loss
round-trip min/avg/max = 0.114/0.143/0.173 ms

なお、リンク機能は同じネットワーク上にあるホスト間でのみ利用可能だ。また、「docker network connect」コマンドの「--link」オプションでも同様のリンク設定が可能になっている。

ネットワーク外へのアクセスが禁止された内部ネットワークを作る

Docker 1.10では新たに「内部ネットワーク(internal network)」という機能も導入されている。これは、ネットワーク外との通信が行えないネットワークを作成する機能だ。内部ネットワークは、次のようにネットワークの作成時に「--internal」オプションを指定することで作成できる。

# docker network create --internal -d overlay testnet03
58936e24df57dcb670a64a5ccbb7225f7738c3d68f4ba8cc747782ab35b35c91

このように作成されたネットワークでは、ネットワーク外への通信が行えず、またネットワーク外からネットワーク内への通信も行えない。次の例は、内部ネットワークとして作成された「testnet03」ネットワークからネットワーク外にアクセスしようとした例だ。ここでは「Network is unreachable」と表示され、アクセスが行えなくなっていることが分かる。

# docker run -it --rm --net=testnet03 busybox
/ # ping osdn.jp
PING osdn.jp (202.221.179.11): 56 data bytes
ping: sendto: Network is unreachable

もちろん、この場合でも同じ内部ネットワークに接続されたホスト間での通信は可能だ。

コンテナに任意のIPアドレスを割り当てる

従来、Dockerではコンテナに割り当てられるIPアドレスは自動的に決定されていた。しかしDocker 1.10では、コンテナに割り当てるIPアドレスを指定できるよう「docker run」コマンドや「docker network」コマンドに「--ip」および「--ip6」オプションが追加されている。ただし、この機能はネットワークの作成時に明示的にネットワークが使用するIPアドレスを指定しておいたネットワークでのみ利用が可能となっている。

ネットワークの作成時に使用するIPアドレスを指定するには、「--subnet」オプションを使用する。次の例は、10.0.1.0/24というネットワークを指定するものだ。

# docker network create --subnet=10.0.1.0/24 testnet04
4d833c534a46fec58d72d8de5e8536574d6653a0600dce653c474e7d8bb4825d

このようにして作成したネットワークにコンテナを接続する場合、「--ip」オプションでコンテナに割り当てるIPアドレスを指定できる。次の例では、10.0.1.100というIPアドレスを割り当てている。

# docker run -ti --net=testnet04 --rm --ip=10.0.1.100 busybox
/ # ip a
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue qlen 1
    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
194: eth0@if195: <BROADCAST,MULTICAST,UP,LOWER_UP,M-DOWN> mtu 1500 qdisc noqueue
    link/ether 02:42:0a:00:01:64 brd ff:ff:ff:ff:ff:ff
    inet 10.0.1.100/24 scope global eth0
       valid_lft forever preferred_lft forever
    inet6 fe80::42:aff:fe00:164/64 scope link
       valid_lft forever preferred_lft forever

なお、ネットワークに割り当てられたサブネット外のIPアドレスを指定した場合、次のようにエラーが発生しコンテナを起動できない。

# docker run -ti --net=testnet04 --rm --ip=10.0.2.100 busybox
docker: Error response from daemon: Invalid preferred address 10.0.2.100: It does not belong to any of this network's subnets.

なお、ここではIPv4アドレスを指定しているが、「--ip6」オプションを利用することでIPv6アドレスを割り当てることも可能だ。また、「docker run」コマンドだけでなく、「docker network connect」コマンドでも同様に「--ip」および「--ip6」オプションでIPアドレスの割り当てが可能だ。

マルチホストネットワークでDockerクラスタの構築がより容易かつ柔軟に

今回紹介したDockerのマルチホストネットワーク機能を利用することで、従来は別の技術を導入する必要があった複数のDockerホスト間の連携が簡単に行えるようになった。また、異なるネットワーク技術を使う場合でも、同じ「docker network」コマンドで仮想ネットワークの管理が行えるというメリットもある。Docker向けのネットワークドライバは仕様が公開されているため、今後サポートされる仮想ネットワークも増えていくと思われる。

なお、Docker 1.9以降が提供されているLinuxディストリビューションはまだ多くないが、2016年4月21日リリースのUbuntu 16.04 LTSや、2016年6月にリリースが予定されているFedora 24では公式にDocker 1.10ベースのパッケージが提供される予定になっている、また、CentOS向けのExtraパッケージではすでにDocker 1.9が利用可能だ。このように、もうすぐ多くの安定版環境でマルチホストネットワーク機能は利用できることになるだろう。