オーバーレイネットワークのチュートリアル
読む時間の目安: 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 \ nginxingressのデフォルトの公開モードは、--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 fb635f5ece59563e7b8b99556f816d24e6949a5f6a5b1fbd92ca244db17a4342DNS のコンテナー自動検出の機能は、一意のコンテナー名に対してのみ動作します。
-
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 ms2 つのコンテナーは、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 alpine2a.
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 ash2 つのコンテナーが実際に開始されたことを確認します。
$ 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 forever1 つめのインターフェースはループバックデバイスです。 今はこれを無視します。 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