Docker Swarm: フィルタ

# フィルタ
原文：https://docs.docker.com/swarm/scheduler/filter/

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

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

* Constraint（強制）
* Affinity（親密さ）
* Port（ポート）
* Dependency（依存性）
* Health（ヘルス）

何らかのフィルタを使いたいときは `swam manage` の `--filter` フラグを使います。

## Constraint フィルタ

Constraint は特定のノードに対してキー・バリューの組を結び付けます。これらは ***node*** タグで見られます。

コンテナの作成時、ノードのサブセットを選択することができます。これは、スケジューリングのために、特定のノード、あるいはキー・バリューの組で一致すべきと考えられるノードです。

この手法はいくつかの特定の使い方があります：

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

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

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

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

もう一度、ただし次は `node-2` を `storage=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` で起動します。

## Constraints の標準

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

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

## アフィニティ （親密さ）フィルタ

コンテナ間を"引き寄せて" を作成するのに、`--affinity:<フィルタ>` を使うことができます。例えば、コンテナを実行するときの場所を指定し、その隣に次の特定のイメージやラベルを持つコンテナを配置することができます。この引き寄せ機能によって、コンテナを同じネットワークノードで確実に動かします。そのとき、どちらのノードで実行しているか知る必要はありません。

### コンテナのアフィニティ（親密さ）

新しいコンテナを実行したあと、隣に他の名前や ID を持つコンテナを実行できます。例えば、`frontend` という名前で `nginx` コンテナを実行します：

```
$ docker run -d -p 80:80 --name front 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` フラグを使い、２つめのコンテナを `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      logge
```

affinity の名前によって、`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-1` と `node-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=front"
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` ラベルを指定しているからです。

### 文法表現

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

`value` は次のようなものです：

* 英数字の文字列、ドット、ハイフン、アンダースコア
* 部分一致、例えば `abc*`
* `/regexp/` 形式の正規表現

Go 言語の正規表現文法をサポートしています。

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

例えば、

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

### Soft affinities/constrains (ソフトなアフィニティ・制約)

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

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-1` や `node-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` を使ったホスト・モードの実行は、ポートのバインディングができない `host` モードのため、デフォルトの `bridge` モードとは異なります。そのため、１つ以上のポート番号を明示する必要があります（ `Dockerfile` で `EXPOSE` を使うか、コマンドラインで `--expose` を使います ）。Swarm は新しいコンテナ向けのノードを選ぶときに `host` モードのこの情報を使います。

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

```
$ 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
```

４つめのコンテナを準備しようとしても、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_goldstine
```

## dependency（依存）フィルタ

このフィルタは同じノード上でのコンテナ依存性をスケジュールします。

現時点では、次の依存性を宣言できます：

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

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

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

## ヘルス・フィルタ

障害が発生しているノードをフィルタします。