swarm モード・ルーティング・メッシュを使う¶
Docker Engine swarm モードは、swarm の外にあるリソースからサービスが利用できるように、ポートを簡単に公開できるようにします。全ての参加ノードは ingress ルーティング・メッシュ (routing mesh)内です。ルーティング・メッシュは swarm 内の各ノードが、 swarm 上で実行しているあらゆるサービスの公開ポートを受け付けます。受付は、たとえ自分のノード上でタスクを実行していなくてもです。ルーティング・メッシュは利用可能なノード上で、公開ポートに対する全てのリクエストを受信し、アクティブなコンテナにルーティングします。
swarm 内で ingress ネットワークを使うには、swarm ノードを有効化する前に、 swarm ノード間で以下のポートを利用可能にする必要があります。
コンテナのネットワーク・ディスカバリ用に TCP/UDP ポート
7946
コンテナ ingress ネットワーク用に UDP ポート
4689
また、swarm ノードとあらゆる外部リソース間で公開ポートをオープンにする必要もあります。例えば、外部のロードバランサなどが、ポートに対してアクセスできる必要があります。
また、サービスは ルーティング・メッシュの迂回(バイパス) しても提供できます。
サービス用にポートを公開¶
サービスの作成時にポートを公開するには、 --publish
フラグを使います。コンテナ内のポート指定は target
を使い、かつ、ルーティング・メッシュ上にバインドするポート指定に published
を使います。もしも published
ポートの指定がなければ、各サービス・タスクのためにランダムな高い番号のポートが割り当てられます。その場合、ポートを割り出すにはタスクの調査(inspect)が必要になります。
$ docker service create \
--name <サービス名> \
--publish published=<公開ポート>,target=<コンテナ側ポート> \
<イメージ>
注釈
旧形式の構文は、 -p 8080:80
のように、まず公開用のポートがあり、次にターゲット用ポートがあり、間をコロン文字列で区切るものでした。新しい構文では読みやすさと柔軟性を持たせるのに適しています。
ノード上のポート 8080 にアクセスすると、Docker はアクティブなコンテナに対してリクエストを転送(ルーティング)します。対象の swarm node 自身がポート 8080 の実際の到達先でなくても、ルーティング・メッシュがトラフィックをどこに転送するかを知っており、あらゆるポートで衝突は発生しません。
ルーティング・メッシュがリッスンするのは、ノードに対して割り当てられた、あらゆる IP アドレスに対する公開ポートです。外部にルーティングできる IP アドレスであれば、ホスト外からポートを利用できます。その他すべての IP アドレスでアクセスできるのは、そのホストで利用可能な IP アドレスのみです。
$ docker service update \
--publish-add published=<公開ポート>,target=<コンテナ・ポート> \
<サービス>
サービスの公開ポートは docker service inspect
で調べられます。例:
$ docker service inspect --format="{{json .Endpoint.Spec.Ports}}" my-web
[{"Protocol":"tcp","TargetPort":80,"PublishedPort":8080}]
出力では <コンテナ・ポート>
( TargetPort
のラベル)はコンテナからのポートで、 <公開ポート>
( PublishedPort
のラベル)はサービスに対するリクエストをリッスンしているノード上のポートです。
TCP のみか UDP のみのポート公開¶
デフォルトでは、ポートを公開すると、それは TCP ポートです。TCP ポートの代わりに、あるいは TCP ポートに加えて UDP ポートを公開を指定できます。TCP と UDP ポートの両方を公開するつもりでも、プロトコルの指定を省略すると、ポートは TCP として公開されてしまいます。長い構文( 推奨)を使う場合、 protocol
キーで tcp
か udp
を指定します。
TCP のみ¶
長い構文:
$ docker service create --name dns-cache \
--publish published=53,target=53 \
dns-cache
短い構文:
$ docker service create --name dns-cache \
-p 53:53 \
dns-cache
ルーティング・メッシュの迂回(バイパス)¶
ルーティング・メッシュは迂回できますので、特定のノード上でバインド(固定)しているポートにアクセスするときは、対象のノード上でサービスを実行しているインスタンスに対し常にアクセスします。これは host
モードとも呼ばれます。いくつかの注意点がありますので、ご注意ください。
サービス・タスクが動作していないノードに対してアクセスしても、サービスは対象となるポートをリッスンしていません。これは、何もリッスンしていないか、全く異なるアプリケーションがリッスンしている可能性があります。
各ノード上で複数のサービス・タスクを動かすつもりの場合(5ノードがあり、10 レプリカを実行するような場合)は、対象となるポート固定を指定できません。(
publiched
を省略した場合は )Docker がランダムな高い番号のポートを割り当てるか、あるいは、対象ノード上で実行しているサービスの1つのインスタンスに対してのみアクセスするか、こののどちらかです。その場合、レプリカに対するアクセスできる場所を制限(constraint)するよりは、グローバル・サービスを使う方が良いでしょう。
ルーティング・メッシュを迂回するには、長い --publish
サービスを使い、 mode
を host
に指定する必要があります。もしも mode
キーを省略するか、キーを ingress
に指定する場合は、ルーティング・メッシュを使います。以下のコマンドは host
モードを使ってグローバル・サービスを作成し、ルーティング・メッシュを迂回します。
$ docker service create --name dns-cache \
--publish published=53,target=53,protocol=udp,mode=host \
--mode global \
dns-cache
外部ロードバランサの設定¶
swarm サービスに対し、外部のロードバランサを設定できます。このとき、ルーティング・メッシュを組み合わせることも、全くルーティング・メッシュを使わないことも可能です。
ルーティング・メッシュを使う¶
外部のロードバランサから、swarm サービスに対してリクエストを転送する設定が可能です。たとえば、 HAProxy を使い、リクエストを nginx サービスが公開しているポート 8080 に振り分けるよう調整できます。
この場合、ポート 8080 はロードバランサと swarm 上のノード間でオープンになっている必要があります。swarm ノードはプライベート・ネットワーク上にあり、プロキシ・サーバにはアクセス可能ですが、パブリックにはアクセスできません。
swarm 上の各ノードが、たとえノード上でタスクを全くスケジュールしていなくても、ロードバランサでリクエストを分散するように設定できます。たとえば、 /etc/haproxy/haproxy.cfg
で以下のように HAProxy を設定できます。
global
log /dev/log local0
log /dev/log local1 notice
...snip...
# HAProxy がポート 80 をリッスンする設定
frontend http_front
bind *:80
stats uri /haproxy?stats
default_backend http_back
# HAProxy が swarm ノード上のポート 8080 に転送する設定
backend http_back
balance roundrobin
server node1 192.168.99.100:8080 check
server node2 192.168.99.101:8080 check
server node3 192.168.99.102:8080 check
HAProxy ロードバランサのポート 80 にアクセスすると、リクエストは swarm 上のノードに転送されます。swarm ルーティング・メッシュはリクエストをアクティブなタスクに転送します。もし何らかの理由により swarm スケジューラが別のノードにタスクを移動したとしても、ロードバランサ側で設定を変える必要はありません。
swarm ノードに対するリクエストを転送するため、あらゆる種類のロードバランサを設定できます。 HAProxy について詳しく学ぶには、 HAProxy ドキュメント をご覧ください。
ルーティング・メッシュを使わない設定¶
ルーティング・メッシュを使わずに外部のロードバランサを使うように設定するには、 --endpoint-mode
を使い、 vip
のデフォルト値の代わりに dnsrr
を指定します。この場合、単一のバーチャル IP を扱いません。そのかわりに、 Docker はサービスに対する DNS エントリを準備し、サービス名の DNS クエリに対して IP アドレスのリストを返し、かつ、クライアントが直接その IP アドレスにアクセスできるようにします。あなたが責任をもってロードバランサに指定する必要があるのは、IP アドレスとポートのリストです。 configure-service-discovery をご覧ください。