フィルタ

Docker Swarm スケジューラは複数のフィルタを持っています。

ノードの一部(サブセット)にコンテナをスケジュールする時、次のフィルタが利用できます。

利用可能なフィルタを指定するには、 swarm manage の実行時に --filter フラグを使って指定します。

Constraint (コンストレイント)フィルタ

Constraint(訳者注:制約や制限の意味)は、特定のノードをキー・バリューの組で結び付けます。これらの情報は node のタグとして確認できます。

コンテナを作成時、ノードを配置するグループ(サブセット)を選べます。グループとは、コンテナのスケジューリングにあたり、1つまたは複数のキー・バリューの組が一致すると考えられるものです。この方法には、複数の指定方法があります。

  • ホスト・プロパティを指定した選択( storage=ssd のように、特定のハードウェアにコンテナをスケジュールするため)
  • ノードの基盤に、物理的な場所をタグ付けする( region=us-ease のように、指定した場所でコンテナを強制的に実行)
  • 論理的なクラスタの分割( environment=production のように、プロパティの違いによりクラスタを複数のサブクラスタに分割)

ノードに対して特定のキー・バリューの組み合わせをタグ付けするには、docker 起動時のオプションで、少なくとも1つの --label 指定が必要です。

例えば、 node-1 を起動するとき storage=ssd ラベルを付けてみましょう。

$ docker -d --label storage=ssd
$ swarm join --advertise=192.168.0.42:2375 token://XXXXXXXXXXXXXXXXXX

ただし次は node-2storage=disk で起動します:

$ docker -d --label storage=disk
$ swarm join --advertise=192.168.0.43:2375 token://XXXXXXXXXXXXXXXXXX

ノードがクラスタに登録されると、マスタは各々のタグを取得し、新しいコンテナをスケジューリングするときにそれらを反映します。

それでは MySQL サーバを起動し、良い I/O 性能を持つフラッシュ・ドライブ上で利用できるようにしましょう:

$ docker run -d -P -e constraint:storage==ssd --name db mysql
f8b693db9cd6

$ docker ps
CONTAINER ID        IMAGE               COMMAND             CREATED                  STATUS              PORTS                           NODE        NAMES
f8b693db9cd6        mysql:latest        "mysqld"            Less than a second ago   running             192.168.0.42:49178->3306/tcp    node-1      db

この例では、マスタは選択された全てのノードから、事前に指定された storage=ssd を強制したリソース管理を適用します。 node-1 は フラッシュ・ドライブ上で動いているホストが選ばれています。

次はクラスタ上に Nginx フロントエンドを走らせてみます。ですが、ログをディスクに沢山書き込むのでフラッシュ・ドライブを使いたくありません。

$ docker run -d -P -e constraint:storage==disk --name frontend nginx
963841b138d8

$ docker ps
CONTAINER ID        IMAGE               COMMAND             CREATED                  STATUS              PORTS                           NODE        NAMES
963841b138d8        nginx:latest        "nginx"             Less than a second ago   running             192.168.0.43:49177->80/tcp      node-2      frontend
f8b693db9cd6        mysql:latest        "mysqld"            Up About a minute        running             192.168.0.42:49178->3306/tcp    node-1      db

スケジューラは storage=disk ラベルを付けて起動済みの node-2 で起動します。

標準制約(Standard Constraint)

さらに、ノードを開始するときに特に指定していなくても、コンテナのスケジュールに使う標準 constraint セットを使えます。これらのタグは docker info で確認できるものです。現在利用できるのは次の通りです。

  • ノード ID またはノード名(”node” をキーに用いる)
  • storagedriver(ストレージ・ドライバ)
  • executiondriver(実行ドライバ)
  • kernelversion(カーネルバージョン)
  • operatingsystem(オペレーティング・システム)

Affinity(アフィニティ)フィルタ

コンテナ間を”引き寄せて” を作成するのに、–affinity:<フィルタ> を使うことができます(訳者注:affinity とは親密さの意味)。例えば、あるコンテナを実行したとします。別のコンテナを実行するとき、特定のイメージやラベルを持つコンテナのある場所で実行できます。この引き寄せ機能によって、コンテナを同じネットワーク・ノード上で確実に動かせます。そのとき、どのノードで実行しているかを知る必要はありません。

コンテナの親密さ(affinity)

新しいコンテナを、既存のコンテナ名や ID を基にしてスケジューリングできます。例えば、 frontend という名前で nginx を実行します。

$ docker run -d -p 80:80 --name frontend nginx
 87c4376856a8


$ docker ps
CONTAINER ID        IMAGE               COMMAND             CREATED                  STATUS              PORTS                           NODE        NAMES
87c4376856a8        nginx:latest        "nginx"             Less than a second ago   running             192.168.0.42:80->80/tcp         node-1      frontend

それから、 -e affinity:container==frontend フラグを使い、2つめのコンテナを frontend の隣にスケジュールします。

$ docker run -d --name logger -e affinity:container==frontend logger
 87c4376856a8

$ docker ps
CONTAINER ID        IMAGE               COMMAND             CREATED                  STATUS              PORTS                           NODE        NAMES
87c4376856a8        nginx:latest        "nginx"             Less than a second ago   running             192.168.0.42:80->80/tcp         node-1      frontend
963841b138d8        logger:latest       "logger"            Less than a second ago   running                                             node-1      logger

コンテナ名のアフィニティ指定によって、 logger コンテナは frontend コンテナと同じ node-1 コンテナで実行されることになります。 frontend という名前だけでなく、次のように ID を使った指定もできます

docker run -d --name logger -e affinity:container==87c4376856a8

イメージ・アフィニティ

コンテナを起動するとき、特定のイメージをダウンロード済みのノードのみにスケジュールすることができます。

$ docker -H node-1:2375 pull redis
$ docker -H node-2:2375 pull mysql
$ docker -H node-3:2375 pull redis

node-1node-3 のみが redis イメージを持っています。 -e affinity:image==redis フィルタを使い、これらのノード上でスケジュールします。

$ docker run -d --name redis1 -e affinity:image==redis redis
$ docker run -d --name redis2 -e affinity:image==redis redis
$ docker run -d --name redis3 -e affinity:image==redis redis
$ docker run -d --name redis4 -e affinity:image==redis redis
$ docker run -d --name redis5 -e affinity:image==redis redis
$ docker run -d --name redis6 -e affinity:image==redis redis
$ docker run -d --name redis7 -e affinity:image==redis redis
$ docker run -d --name redis8 -e affinity:image==redis redis

$ docker ps
CONTAINER ID        IMAGE               COMMAND             CREATED                  STATUS              PORTS                           NODE        NAMES
87c4376856a8        redis:latest        "redis"             Less than a second ago   running                                             node-1      redis1
1212386856a8        redis:latest        "redis"             Less than a second ago   running                                             node-1      redis2
87c4376639a8        redis:latest        "redis"             Less than a second ago   running                                             node-3      redis3
1234376856a8        redis:latest        "redis"             Less than a second ago   running                                             node-1      redis4
86c2136253a8        redis:latest        "redis"             Less than a second ago   running                                             node-3      redis5
87c3236856a8        redis:latest        "redis"             Less than a second ago   running                                             node-3      redis6
87c4376856a8        redis:latest        "redis"             Less than a second ago   running                                             node-3      redis7
963841b138d8        redis:latest        "redis"             Less than a second ago   running                                             node-1      redis8

ここで見えるように、コンテナがスケジュールされるのは redis イメージを持っているノードのみです。イメージ名に加えて、特定のイメージ ID も指定できます。

$ docker images
REPOSITORY                         TAG                       IMAGE ID            CREATED             VIRTUAL SIZE
redis                              latest                    06a1f75304ba        2 days ago          111.1 MB

$ docker run -d --name redis1 -e affinity:image==06a1f75304ba redis

ラベル・アフィニティ

ラベル・アフィニティによって、コンテナのラベルで引き寄せてセットアップできます。例えば、 nginx コンテナを com.example.type=frontend ラベルをつけて起動します。

$ docker run -d -p 80:80 --label com.example.type=frontend nginx
 87c4376856a8

$ docker ps  --filter "label=com.example.type=frontend"
CONTAINER ID        IMAGE               COMMAND             CREATED                  STATUS              PORTS                           NODE        NAMES
87c4376856a8        nginx:latest        "nginx"             Less than a second ago   running             192.168.0.42:80->80/tcp         node-1      trusting_yonath

それから、 -e affinity:com.example.type==frontend を使って、 com.example.type==fronten ラベルを持つコンテナの隣にスケジュールします。

$ docker run -d -e affinity:com.example.type==frontend logger
 87c4376856a8

$ docker ps
CONTAINER ID        IMAGE               COMMAND             CREATED                  STATUS              PORTS                           NODE        NAMES
87c4376856a8        nginx:latest        "nginx"             Less than a second ago   running             192.168.0.42:80->80/tcp         node-1      trusting_yonath
963841b138d8        logger:latest       "logger"            Less than a second ago   running                                             node-1      happy_hawking

logger コンテナは、最終的に node-1 に置かれます。これはアフィニティに com.example.type==frontend ラベルを指定しているからです。

文法表現

アフィニティや制約は、 keyvalue の組み合わせで表現します。 key は英数字のパターンに従います。ただし、先頭はアルファベットかアンダースコアです。 value は次のようなものです。

  • 英数字の文字列、ドット、ハイフン、アンダースコア。
  • 部分一致、例えば abc*
  • /regexp/ 形式の正規表現。Go 言語の正規表現構文をサポート。

現時点の Swarm は、アフィニティ・constraint で演算子 ==!= をサポートしています。

  • constraint:node==node1 は、ノード node1 に一致。
  • constraint:node!=node1 は、node1 をのぞく全てのノードに一致。
  • constraint:region!=us* は、 us が付いているリージョン以外のノードに一致。
  • constraint:node==/node[12]/ は、 node1node2 に一致。
  • constraint:node==/node\d/ は、 node + 10進数の1文字に一致。
  • constraint:node!=/node-[01]/ は、 node-0node-1 以外の全てのノードに一致。
  • constraint:node!=/foo\[bar\]/ は、 foo[var] 以外の全てのノードに一致。
  • constraint:node==/(?i)node1/ は、大文字・小文字を区別しない node1 に一致。そのため、 NoDe1NODE1 も一致する。

Soft アフィニティ・制約の設定

デフォルトでは、アフィニティと制約は厳密(ハード)に強制されるものです。アフィニティや制約で指定した条件に対応するノードがなければ、コンテナはスケジュールされません。Soft affinities/constrains (ソフト設定)があれば、スケジュールが一致するルールを探そうとします。もし一致しなければ、スケジューラはフィルタを廃棄し、コンテナはスケジューラのストラテジに従ってスケジュールします

アフィニティと制約のソフト設定は ~ で指定します。例えば、次のように指定します。

$ docker run -d --name redis1 -e affinity:image==~redis redis

もし、クラスタにイメージ redis を持つノードが無ければ、スケジューラはアフィニティを破棄し、ストラテジに従ってスケジュールします。

$ docker run -d --name redis2 -e constraint:region==~us* redis

もし、 us リージョンに属すノードがクラスタに無ければ、スケジューラは制約を破棄し、ストラテジに従ってスケジュールします。

$ docker run -d --name redis5 -e affinity:container!=~redis* redis

アフィニティ・フィルタは新しい redis5 コンテナを、指定した redis* の名前を含むコンテナが無いノードにスケジュールします。もしクラスタの各々のノードが redis* コンテナを持っている場合、スケジューラはアフィニティのルールを破棄し、ストラテジに従ってスケジュールします。

ポート・フィルタ

ports フィルタは、ユニーク(未使用)なリソースを探し出します。

$ docker run -d -p 80:80 nginx
87c4376856a8

$ docker ps
CONTAINER ID    IMAGE               COMMAND         PORTS                       NODE        NAMES
87c4376856a8    nginx:latest        "nginx"         192.168.0.42:80->80/tcp     node-1      prickly_engelbart

Docker クラスタから、パブリックのポート 80 が利用可能なノードを選択し、コンテナの実行をスケジュールします。この例では node-1 が該当します。

他のコンテナでパブリックのポート 80 で起動しようとするなら、既に node-1 は使用中のため、別のノードが選ばれることになります。

$ docker run -d -p 80:80 nginx
963841b138d8

$ docker ps
CONTAINER ID        IMAGE          COMMAND        PORTS                           NODE        NAMES
963841b138d8        nginx:latest   "nginx"        192.168.0.43:80->80/tcp         node-2      dreamy_turing
87c4376856a8        nginx:latest   "nginx"        192.168.0.42:80->80/tcp         node-1      prickly_engelbart

再び同じコマンドを実行すると、ポート 80 が使えない node-1node-2 ではなく、 node-3 が選ばれます。

$ docker run -d -p 80:80 nginx
963841b138d8

$ docker ps
CONTAINER ID   IMAGE               COMMAND        PORTS                           NODE        NAMES
f8b693db9cd6   nginx:latest        "nginx"        192.168.0.44:80->80/tcp         node-3      stoic_albattani
963841b138d8   nginx:latest        "nginx"        192.168.0.43:80->80/tcp         node-2      dreamy_turing
87c4376856a8   nginx:latest        "nginx"        192.168.0.42:80->80/tcp         node-1      prickly_engelbart

最終的に、クラスタ上でポート 80 が利用可能なノードが無くなると、Docker Swam はコンテナの実行を拒否します。

$ docker run -d -p 80:80 nginx
2014/10/29 00:33:20 Error response from daemon: no resources available to schedule container

ホスト・モードでのポートフィルタ

Docker を --net=host を使ったホスト・モードで実行すると、デフォルトの bridge モードとは異なり、ポートのバインディングができない host モードになります。そのため、1つ以上のポート番号を明示する必要があります( Dockerfile で EXPOSE を使うか、コマンドラインで --expose を使います )。Swarm がこの情報を使うのは、 host モードで新しいコンテナが利用可能なノードを選ぶときです。

例えば、以下のコマンドは nginx を3つのノード・クラスタで起動します。

$ docker run -d --expose=80 --net=host nginx
640297cb29a7
$ docker run -d --expose=80 --net=host nginx
7ecf562b1b3f
$ docker run -d --expose=80 --net=host nginx
09a92f582bc2

ポートの利用情報は、 docker ps コマンドを通して利用可能です。これは全てのノードが host モードで起動されているためです。

$ docker ps
CONTAINER ID        IMAGE               COMMAND                CREATED                  STATUS              PORTS               NAMES
640297cb29a7        nginx:1             "nginx -g 'daemon of   Less than a second ago   Up 30 seconds                           box3/furious_heisenberg
7ecf562b1b3f        nginx:1             "nginx -g 'daemon of   Less than a second ago   Up 28 seconds                           box2/ecstatic_meitner
09a92f582bc2        nginx:1             "nginx -g 'daemon of   46 seconds ago           Up 27 seconds                           box1/mad_goldstine

4つめのコンテナを準備しようとしても、Swarm は拒否するでしょう。

$  docker run -d --expose=80 --net=host nginx
FATA[0000] Error response from daemon: unable to find a node with port 80/tcp available in the Host mode

そのかわり、ポート 81 のような、異なったポートをバインドすることはできます。

$  docker run -d -p 81:80 nginx:latest
832f42819adc
$  docker ps
CONTAINER ID        IMAGE               COMMAND                CREATED                  STATUS                  PORTS                                 NAMES
832f42819adc        nginx:1             "nginx -g 'daemon of   Less than a second ago   Up Less than a second   443/tcp, 192.168.136.136:81->80/tcp   box3/thirsty_hawking
640297cb29a7        nginx:1             "nginx -g 'daemon of   8 seconds ago            Up About a minute                                             box3/furious_heisenberg
7ecf562b1b3f        nginx:1             "nginx -g 'daemon of   13 seconds ago           Up About a minute                                             box2/ecstatic_meitner
09a92f582bc2        nginx:1             "nginx -g 'daemon of   About a minute ago       Up About a minute                                             box1/mad_gol

依存関係フィルタ

このフィルタは、コンテナの依存関係により、同じノード上にスケジュールするものです。

現時点では、次の依存関係を宣言できます。

  • ボリューム共有: --volumes-from=dependency
  • リンク: --link=dependency:alias
  • 共有ネットワーク層: --net=container:dependency

Swarm は依存関係のある同じノード上にコンテナを設置しようとします。もし実行できなそうであれば(依存関係のコンテナが存在しなかったり、ノードに十分なリソースが無い場合)、コンテナは作成されません。

必要であれば、複数の依存関係を組み合わせることもできます。例えば、 --volumes-from=A --net=container:B は、コンテナ AB を同じノード上に置こうとします。しかし、これらのコンテナが別々のノードで動いているなら、Swarm はコンテナのスケジューリングを行いません。

ヘルス・フィルタ

ヘルスフィルタは、障害が発生しているノードへのスケジューリングを阻止します。