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 アドレスのみです。

サービス ingress の図
$ 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 キーで tcpudp を指定します。

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

TCP と UDP

長い構文:

$ docker service create --name dns-cache \
  --publish published=53,target=53 \
  --publish published=53,target=53,protocol=udp \
  dns-cache

短い構文:

$ docker service create --name dns-cache \
  -p 53:53 \
  -p 53:53/udp \
  dns-cache

UDP のみ

長い構文:

$ docker service create --name dns-cache \
  --publish published=53,target=53,protocol=udp \
  dns-cache

短い構文:

$ docker service create --name dns-cache \
  -p 53:53/udp \
  dns-cache

ルーティング・メッシュの迂回(バイパス)

ルーティング・メッシュは迂回できますので、特定のノード上でバインド(固定)しているポートにアクセスするときは、対象のノード上でサービスを実行しているインスタンスに対し常にアクセスします。これは host モードとも呼ばれます。いくつかの注意点がありますので、ご注意ください。

  • サービス・タスクが動作していないノードに対してアクセスしても、サービスは対象となるポートをリッスンしていません。これは、何もリッスンしていないか、全く異なるアプリケーションがリッスンしている可能性があります。

  • 各ノード上で複数のサービス・タスクを動かすつもりの場合(5ノードがあり、10 レプリカを実行するような場合)は、対象となるポート固定を指定できません。( publiched を省略した場合は )Docker がランダムな高い番号のポートを割り当てるか、あるいは、対象ノード上で実行しているサービスの1つのインスタンスに対してのみアクセスするか、こののどちらかです。その場合、レプリカに対するアクセスできる場所を制限(constraint)するよりは、グローバル・サービスを使う方が良いでしょう。

ルーティング・メッシュを迂回するには、長い --publish サービスを使い、 modehost に指定する必要があります。もしも 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 に振り分けるよう調整できます。

外部のロードバランサを使う ingress の図

この場合、ポート 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 をご覧ください。

詳しく学ぶ

swarm にサービスをデプロイ

参考

Use swarm mode routing mesh

https://docs.docker.com/engine/swarm/ingress/