オーバーレイネットワークのチュートリアル
読む時間の目安: 10 分
ここに示すチュートリアルは、Swarm サービスに対するネットワークを扱います。 スタンドアロンコンテナーに対するネットワークについては、スタンドアロンコンテナーのネットワークチュートリアル を参照してください。 Docker ネットワークの全般的なことを確認したい場合は ネットワーク概要 を参照してください。
このトピックには 4 つのチュートリアルがあります。 それぞれは Linux、Windows、Mac 上において実行することができます。 ただし Windows と Mac の場合は、2 つめの Docker ホストを、どこか別に用意することが必要になります。
-
デフォルトのオーバーレイネットワーク利用 では、Swarm の初期化時、または Swarm への参加時に Docker が自動的に設定するデフォルトのオーバーレイネットワークの利用方法を示します。 このネットワークは、本番環境向けには適していません。
-
ユーザー定義のオーバーレイネットワーク利用 では、独自にオーバーレイネットワークを生成し、サービスに接続して利用する方法を示します。 本番環境においてサービスを稼動させる場合には、この方法が推奨されます。
-
スタンドアロンコンテナーに対するオーバーレイネットワーク利用 では、異なる Docker デーモン上にあるスタンドアロンコンテナーに対して、オーバーレイネットワークを使って互いに通信する方法について説明します。
-
コンテナー、Swarm サービス間の通信 では、スタンドアロンコンテナーと Swarm サービスの間で通信を行うための設定を行います。 そこではアタッチ可能なオーバーレイネットワークを用います。
前提条件
最低でも単一ノードからなる Swarm が必要です。
つまり Docker ホスト上にデーモンが起動している状態でdocker swarm init
を実行します。
もちろん複数ノードの Swarm 上でも、利用例を試すことができます。
デフォルトのオーバーレイネットワーク利用
以下の例ではalpine
サービスを起動し、コンテナーの個々から見たネットワークの特徴を確認していきます。
このチュートリアルでは、オーバーレイネットワークがどのように実装されているかといった、システム詳細については立ち入った説明はしません。 ただしサービスの観点から、オーバーレイ機能がどのようなものかは、詳しく見ていきます。
前提条件
このチュートリアルでは、物理ホスト、仮想ホストは問わず Docker ホストを 3 つ利用して、互いに通信を行うようにします。 この 3 つは、同一のネットワーク上にファイアウォールなしに稼動しているものとします。
各ホストはmanager
、worker-1
、worker-2
とします。
manager
ホストは、マネージャーとワーカーの両方の役割を持つものです。
つまりこれは、サービスタスクが稼動すると同時に、Swarm の管理も行います。
worker-1
とworker-2
は、ワーカーとしてのみ動作します。
3 つのホストを手元で自由に使えないといったときには、簡単な策として Amazon EC2 などのクラウドプロバイダー上に 3 つの Ubuntu ホストを設定するという方法があります。 そうすれば同一のネットワーク上において、各ホストが確実に通信できるようになります(EC2 のセキュリティグループなどの機能を利用して実現されます)。 これを実行するなら、Ubuntu 向け Docker Engine - Community のインストール の手順に従ってください。
ウォークスルー
Swarm の生成
この手順を進めることで、最終的には 3 つの Docker ホストが Swarm に参加し、ingress
というオーバーレイネットワークを使って互いに通信できる状態になります。
-
manager
において Swarm を初期化します。 このホストがただ 1 つのネットワークインターフェースしかない場合は--advertise-addr
フラグの指定は任意です。$ docker swarm init --advertise-addr=<managerのIPアドレス>
出力される文字列は書きとめておいてください。 そこにはトークンが出力されます。 この後に
worker-1
とworker-2
を Swarm に参加させる際に必要となります。 パスワード管理ツールがあれば、そこにトークンを保存しておくのでもよいでしょう。 -
worker-1
上から、これを Swarm に参加させます。 このホストがただ 1 つのネットワークインターフェースしかない場合は--advertise-addr
フラグの指定は任意です。$ docker swarm join --token <トークン> \ --advertise-addr <worker-1のIPアドレス> \ <managerのIPアドレス>:2377
-
worker-2
上から、これを Swarm に参加させます。 このホストがただ 1 つのネットワークインターフェースしかない場合は--advertise-addr
フラグの指定は任意です。$ docker swarm join --token <トークン> \ --advertise-addr <worker-2のIPアドレス> \ <managerのIPアドレス>:2377
-
manager
においてノード一覧を確認します。 このコマンドはマネージャーからしか実行することができません。$ docker node ls ID HOSTNAME STATUS AVAILABILITY MANAGER STATUS d68ace5iraw6whp7llvgjpu48 * ip-172-31-34-146 Ready Active Leader nvp5rwavvb8lhdggo8fcf7plg ip-172-31-35-151 Ready Active ouvx2l7qfcxisoyms8mtkgahw ip-172-31-36-89 Ready Active
--filter
フラグを使って役割(role)を絞ることができます。$ docker node ls --filter role=manager ID HOSTNAME STATUS AVAILABILITY MANAGER STATUS d68ace5iraw6whp7llvgjpu48 * ip-172-31-34-146 Ready Active Leader $ docker node ls --filter role=worker ID HOSTNAME STATUS AVAILABILITY MANAGER STATUS nvp5rwavvb8lhdggo8fcf7plg ip-172-31-35-151 Ready Active ouvx2l7qfcxisoyms8mtkgahw ip-172-31-36-89 Ready Active
-
manager
、worker-1
、worker-2
のそれぞれにおいて、Docker ネットワークの一覧を表示します。 そこにはいずれもingress
というオーバーレイネットワークがあり、docker_gwbridge
というブリッジネットワークがあることを確認してください。 なお以下ではmanager
におけるネットワーク一覧のみを示します。$ docker network ls NETWORK ID NAME DRIVER SCOPE 495c570066be bridge bridge local 961c6cae9945 docker_gwbridge bridge local ff35ceda3643 host host local trtnl4tqnc3n ingress overlay swarm c8357deec9cb none null local
docker_gwbridge
はingress
ネットワークを Docker ホストのネットワークインターフェースに接続するものであり、これがあることで、Swarm マネージャーとワーカーとの間でのトラフィックが通信できるようになります。
Swarm サービスを生成する際にネットワーク指定を行わない場合は、そのサービスはingress
ネットワークに接続されます。
いくつかのアプリケーションがまとまって動作するような場合には、各アプリケーションごとにオーバーレイネットワークを用いることが推奨されます。
次の手順では、オーバーレイネットワークを 2 つ生成して、サービスをそこに接続します。
サービスの生成
-
manager
において、nginx-net
という名前の新たなオーバーレイネットワークを生成します。$ docker network create -d overlay nginx-net
このオーバーレイネットワークは、他のノード上で生成する必要はありません。 それぞれのノードにおいて、このネットワークを必要とするサービスタスクが起動した際に、ネットワークは自動生成されます。
-
manager
において、nginx-net
に接続する Nginx サービスをレプリカ数 5 で生成します。 このサービスは外部に対してはポート 80 を公開します。 サービスタスクを実行するコンテナーは、ポートを公開しなくても互いに通信できるようになっています。メモ: サービスはマネージャー上からしか生成できません。
$ docker service create \ --name my-nginx \ --publish target=80,published=80 \ --replicas=5 \ --network nginx-net \ nginx
ingress
のデフォルトの公開モードは、--publish
フラグに対してmode
を指定しなかった場合に用いられます。 そしてmanager
、worker-1
、worker-2
のポート 80 にアクセスしたときに、5 つのサービスタスクのどれか 1 つのポート 80 に接続できるものであり、たとえ接続したノードそのものにおいて、その時点でサービスタスクが稼動していなくても接続が可能なものです。host
モードを利用してポートを公開したい場合は、--publish
においてmode=host
を追加します。 ただしその場合は、--replicas=5
を指定するのではなく--mode global
としなければなりません。 そのときには、指定されたノード上の指定されたポートに割り当てることができるサービスタスクは、ただ 1 つになるからです。 -
docker service ls
を実行して、サービスの稼働状況を確認してみます。 これには数秒かかります。 -
manager
、worker-1
、worker-2
上のnginx-net
ネットワークを確認します。worker-1
とworker-2
においては、ネットワークを手動で生成する必要はなく、Docker が生成してくれるものであることは、前に説明しました。 出力結果は長いものになりますが、Containers
とPeers
という項をよく確認してください。Containers
には、ホストからオーバーレイネットワークに接続されたサービスタスク(あるいはスタンドアロンコンテナー)の一覧が出力されています。 -
manager
において、docker service inspect my-nginx
を実行してサービスを確認します。 サービスによって利用されているポートとエンドポイントの情報を確認してください。 -
新たに
nginx-net-2
というネットワークを生成します。 そしてそれまでのnginx-net
の代わりとして、このネットワークをサービスが利用するようにアップデートします。$ docker network create -d overlay nginx-net-2
$ docker service update \ --network-add nginx-net-2 \ --network-rm nginx-net \ my-nginx
-
docker service ls
を実行して、サービスがアップデートされたことと、タスクがすべて再デプロイされたことを確認します。 そしてdocker network inspect nginx-net
を実行して、このネットワークに接続しているコンテナーは 1 つもないことを確認します。 同様のコマンドをnginx-net-2
に対しても実行し、サービスタスクコンテナーがすべて、そのネットワークに接続されていることを確認します。メモ: Swarm ワーカーノードにおいては、処理状況に応じてオーバーレイネットワークが自動生成されますが、これは自動的には削除されません。
-
サービスとネットワークを削除します。
manager
上から、以下のコマンドを実行します。 マネージャーからは、ワーカーに対してネットワークを削除するよう、自動的に指示が送られます。$ docker service rm my-nginx $ docker network rm nginx-net nginx-net-2
ユーザー定義のオーバーレイネットワーク利用
前提条件
このチュートリアルでは、Swarm がすでに設定済みであり、マネージャーにアクセスできているものとします。
ウォークスルー
-
ユーザー定義のオーバーレイネットワークを生成します。
$ docker network create -d overlay my-overlay
-
そのオーバーレイネットワークを使ってサービスを起動します。 ポート 80 を Docker ホストのポート 8080 に公開します。
$ docker service create \ --name my-nginx \ --network my-overlay \ --replicas 1 \ --publish published=8080,target=80 \ nginx:latest
-
docker network inspect my-overlay
を実行して、my-nginx
サービスタスクがmy-overlay
に接続されていることを確認します。 確認はContainers
の項を見ます。 -
サービスとネットワークを削除します。
$ docker service rm my-nginx $ docker network rm my-overlay
スタンドアロンコンテナーに対するオーバーレイネットワーク利用
この例では、DNS によるコンテナー検出を試してみます。 特に、オーバーレイネットワークを利用する複数の Docker デーモン上に、スタンドアロンコンテナーが稼動していて、その間での通信情報を確認していきます。
host1
では、Swarm としてノードを初期化します。(マネージャー)host2
では、Swarm に対してノード参加します。(ワーカー)host1
では、アタッチ可能なオーバーレイネットワークを生成します。(test-net
)host1
では、対話的な alpine コンテナー(alpine1
)をtest-net
上で実行します。host2
では、対話的な alpine コンテナー(alpine2
)を、デタッチモードによりtest-net
上で実行します。host1
では、alpine1
のセッション内部からalpine2
に対して ping を行います。
前提条件
このテストでは、Docker ホストが 2 つ、互いに通信できるものとして用意する必要があります。 両ホスト間では、以下のポートが公開されていることが必要です。
- TCP ポート 2377
- TCP と UDP のポート 7946
- UDP ポート 4789
このような環境を準備する簡単な方法として、2 つの VM(ローカルなもの、あるいは AWS のようなクラウドプロバイダー上のもの)を利用することが考えられます。 そこで Docker をインストールし起動させます。 AWS や同様のクラウドコンピューティングプラットフォームを利用している場合は、セキュリティグループを利用すれば、ごく簡単に設定ができます。 これを使えば、2 つのホスト間での入力ポートはすべて開放され、手元のクライアントからは IP アドレスを使って SSH 接続することができます。
この例では、Swarm 上に配置する 2 つのノードをhost1
、host2
とします。
またこの例では Linux ホストを用いますが、同じコマンドは Windows 上でも動作します。
ウォークスルー
-
Swarm を設定します。
a.
host1
において Swarm を初期化します。 (プロンプトが出る場合は--advertise-addr
を指定します。 これを使って、Swarm 内の別ホストの間で通信を行うインターフェースの IP アドレスを指定します。 たとえば AWS 上であれば、プライベート IP アドレスを指定します。)$ docker swarm init Swarm initialized: current node (vz1mm9am11qcmo979tlrlox42) is now a manager. To add a worker to this swarm, run the following command: docker swarm join --token SWMTKN-1-5g90q48weqrtqryq4kj6ow0e8xm9wmv9o6vgqc5j320ymybd5c-8ex8j0bc40s6hgvy5ui5gl4gy 172.31.47.252:2377 To add a manager to this swarm, run 'docker swarm join-token manager' and follow the instructions.
b.
host2
において、上のコマンド実行により出力された Swarm への参加を行います。$ docker swarm join --token <トークン> <IPアドレス>:2377 This node joined a swarm as a worker.
Swarm へのノード参加に失敗すると、
docker swarm join
コマンドはタイムアウトします。 これを解消するにはhost2
上でdocker swarm leave --force
を実行して、ネットワークとファイアウォールの設定を正しくした上で、再度コマンド実行を試してみてください。 -
host1
においてtest-net
という名前の、アタッチ可能なオーバーレイネットワークを生成します。$ docker network create --driver=overlay --attachable test-net uqsof8phj3ak0rq9k86zta6ht
ここで出力される ネットワーク ID を覚えておいてください。 同じものが
host2
から接続した際にも表示されます。 -
host1
において、test-net
に接続する対話形式(-it
)のコンテナー(alpine1
)を起動します。$ docker run -it --name alpine1 --network test-net alpine / #
-
host2
において利用可能なネットワーク一覧を表示します。 なおtest-net
はこの時点ではまだ存在しません。$ docker network ls NETWORK ID NAME DRIVER SCOPE ec299350b504 bridge bridge local 66e77d0d0e9a docker_gwbridge bridge local 9f6ae26ccb82 host host local omvdxqrda80z ingress overlay swarm b65c952a4b2b none null local
-
host2
において、test-net
に接続するデタッチモード(-d
)かつ対話形式(-it
)のコンテナー(alpine2
)を起動します。$ docker run -dit --name alpine2 --network test-net alpine fb635f5ece59563e7b8b99556f816d24e6949a5f6a5b1fbd92ca244db17a4342
DNS のコンテナー自動検出の機能は、一意のコンテナー名に対してのみ動作します。
-
host2
においてtest-net
が生成されていることを確認します。 (そしてhost1
上から見たtest-net
と同じように、同一のネットワーク ID となっていることを確認します。)$ docker network ls NETWORK ID NAME DRIVER SCOPE ... uqsof8phj3ak test-net overlay swarm
-
host1
において、alpine1
の対話型ターミナル内からalpine2
に向けて ping を行います。/ # ping -c 2 alpine2 PING alpine2 (10.0.0.5): 56 data bytes 64 bytes from 10.0.0.5: seq=0 ttl=64 time=0.600 ms 64 bytes from 10.0.0.5: seq=1 ttl=64 time=0.555 ms --- alpine2 ping statistics --- 2 packets transmitted, 2 packets received, 0% packet loss round-trip min/avg/max = 0.555/0.577/0.600 ms
2 つのコンテナーは、2 つのホストに接続しているオーバーレイネットワークを使って、互いに通信します。
host2
からもう一つ別の alpine コンテナーを、デタッチモードではなく 起動すると、hosts
からalpine1
へ ping を行うことができます。 (そしてここでは rm オプション を指定に加えて、コンテナーの自動削除を行うことにします。)$ docker run -it --rm --name alpine3 --network test-net alpine / # ping -c 2 alpine1 / # exit
-
host1
においてalpine1
のセッションを閉じます。 (さらにコンテナーも停止します。)/ # exit
-
コンテナーとネットワークを削除します。
コンテナーの停止と削除は、個々のホスト上においてそれぞれ行うことが必要です。 Docker デーモンはそれぞれに動作しているからであり、コンテナーはすべてスタンドアロンだからです。 ネットワークを削除するのは
host1
上だけで十分です。host2
上にてalpine2
を停止したらtest-net
はなくなります。a.
host2
においてalpine2
を停止させます。 そしてtest-net
が削除されていることを確認した上でalpine2
を削除します。$ docker container stop alpine2 $ docker network ls $ docker container rm alpine2
a.
host1
においてalpine1
とtest-net
を削除します。$ docker container rm alpine1 $ docker network rm test-net
コンテナー、Swarm サービス間の通信
この例では、2 つのalpine
コンテナーを同一の Docker ホスト上に稼動させます。
そしてテストを行って、コンテナー間の通信がどのように行われるかを確認します。
Docker がインストール済みであり起動していることを確認いてください。
-
ターミナル画面を開きます。 まず初めに、現在のネットワーク一覧を確認しておきます。 ネットワークをまったく追加せず、Docker デーモン上において Swarm の初期化も行っていなければ、以下のような表示になるはずです。 複数のネットワークが表示されるはずであり、最低で以下のものがあるはずです。 (ネットワーク ID は異なります。)
$ docker network ls NETWORK ID NAME DRIVER SCOPE 17e324f45964 bridge bridge local 6ed54d316334 host host local 7092879f2cc8 none null local
デフォルトの
bridge
ネットワークが一覧に表示されます。 これとともにhost
とnone
があります。 この 2 つは完全なネットワークではありませんが、コンテナーを起動して Docker デーモンホストのネットワークに直接接続するために、あるいはネットワークデバイスのないコンテナーを起動するために必要となります。 このチュートリアルでは、2 つのコンテナーをbridge
ネットワークに接続します。 -
alpine
コンテナーを 2 つ起動してash
を実行します。 Alpine のデフォルトシェルがbash
ではなくash
です。-dit
フラグは、コンテナーをデタッチモードで(バックグラウンドで)実行し、対話を行い(入力を可能とし)、TTY を利用する(入出力が確認できる)ことを意味します。 デタッチモードで起動するため、コンテナーに即座に接続されるわけではありません。 その前にコンテナー ID が出力されます。--network
フラグを何も指定しなかったので、コンテナーはデフォルトのbridge
ネットワークに接続されます。$ docker run -dit --name alpine1 alpine ash $ docker run -dit --name alpine2 alpine ash
2 つのコンテナーが実際に開始されたことを確認します。
$ docker container ls CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 602dbf1edc81 alpine "ash" 4 seconds ago Up 3 seconds alpine2 da33b7aa74b0 alpine "ash" 17 seconds ago Up 16 seconds alpine1
-
bridge
ネットワークを参照して、どのコンテナーがこれに接続しているかを確認します。$ docker network inspect bridge [ { "Name": "bridge", "Id": "17e324f459648a9baaea32b248d3884da102dde19396c25b30ec800068ce6b10", "Created": "2017-06-22T20:27:43.826654485Z", "Scope": "local", "Driver": "bridge", "EnableIPv6": false, "IPAM": { "Driver": "default", "Options": null, "Config": [ { "Subnet": "172.17.0.0/16", "Gateway": "172.17.0.1" } ] }, "Internal": false, "Attachable": false, "Containers": { "602dbf1edc81813304b6cf0a647e65333dc6fe6ee6ed572dc0f686a3307c6a2c": { "Name": "alpine2", "EndpointID": "03b6aafb7ca4d7e531e292901b43719c0e34cc7eef565b38a6bf84acf50f38cd", "MacAddress": "02:42:ac:11:00:03", "IPv4Address": "172.17.0.3/16", "IPv6Address": "" }, "da33b7aa74b0bf3bda3ebd502d404320ca112a268aafe05b4851d1e3312ed168": { "Name": "alpine1", "EndpointID": "46c044a645d6afc42ddd7857d19e9dcfb89ad790afb5c239a35ac0af5e8a5bc5", "MacAddress": "02:42:ac:11:00:02", "IPv4Address": "172.17.0.2/16", "IPv6Address": "" } }, "Options": { "com.docker.network.bridge.default_bridge": "true", "com.docker.network.bridge.enable_icc": "true", "com.docker.network.bridge.enable_ip_masquerade": "true", "com.docker.network.bridge.host_binding_ipv4": "0.0.0.0", "com.docker.network.bridge.name": "docker0", "com.docker.network.driver.mtu": "1500" }, "Labels": {} } ]
上の方に
bridge
ネットワークに関する情報が一覧表示されます。 Docker ホストとbridge
ネットワーク間のゲートウェイに対する IP アドレス(172.17.0.1
)も表示されています。Containers
キーの配下に、接続されているコンテナーがそれぞれ表示されています。 そこには IP アドレスの情報もあります(alpine1
が172.17.0.2
、alpine2
が172.17.0.3
となっています)。 -
コンテナーはバックグラウンドで実行しています。
docker attach
コマンドを使ってalpine1
に接続してみます。$ docker attach alpine1 / #
プロンプトが
#
に変わりました。 これはコンテナー内のroot
ユーザーであることを意味します。ip addr show
コマンドを使って、コンテナー内部からalpine1
のネットワークインターフェースを見てみます。# ip addr show 1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN 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 27: eth0@if28: <BROADCAST,MULTICAST,UP,LOWER_UP,M-DOWN> mtu 1500 qdisc noqueue state UP 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
1 つめのインターフェースはループバックデバイスです。 今はこれを無視します。 2 つめのインターフェースの IP アドレスは
172.17.0.2
となっています。 前の手順で確認したalpine1
のアドレスと同じです。 -
alpine1
の内部からgoogle.com
への ping を行って、インターネットに接続してみます。-c 2
フラグにより 2 回だけping
を行います。# ping -c 2 google.com PING google.com (172.217.3.174): 56 data bytes 64 bytes from 172.217.3.174: seq=0 ttl=41 time=9.841 ms 64 bytes from 172.217.3.174: seq=1 ttl=41 time=9.897 ms --- google.com ping statistics --- 2 packets transmitted, 2 packets received, 0% packet loss round-trip min/avg/max = 9.841/9.869/9.897 ms
-
そこで 2 つめのコンテナーに対して ping してみます。 最初は IP アドレス
172.17.0.3
を使って ping します。# ping -c 2 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.086 ms 64 bytes from 172.17.0.3: seq=1 ttl=64 time=0.094 ms --- 172.17.0.3 ping statistics --- 2 packets transmitted, 2 packets received, 0% packet loss round-trip min/avg/max = 0.086/0.090/0.094 ms
成功しました。 次に
alpine2
コンテナーに向けて、コンテナー名により ping をしてみます。 これは失敗します。# ping -c 2 alpine2 ping: bad address 'alpine2'
-
alpine1
を停止させることなくデタッチします。 これはデタッチを行うキー操作、つまりCTRL
+p
、CTRL
+q
により行います(CTRL
を押したまま、p
とq
を順に押します)。 この後alpine2
に対して同じことをするなら、手順の 4、5、6 をもう一度行います。alpine1
のところはalpine2
に変えて実施します。 -
2 つのコンテナーを停止させ削除します。
$ docker container stop alpine1 alpine2 $ docker container rm alpine1 alpine2
デフォルトのbridge
ネットワークは、本番環境向けとしては推奨されない点を覚えておいてください。
ユーザー定義のブリッジネットワークについては、次のチュートリアル に進んでください。
その他のネットワークチュートリアル
オーバーレイネットワークのチュートリアルを終えたので、以下に示すような別のネットワークチュートリアルも見てください。
networking, bridge, routing, ports, swarm, overlay