Swarm フィルタ¶
フィルタとは Docker Swarm スケジューラに対して、ノードを使ってコンテナの作成・実行をするか伝えます。
設定可能なフィルタ¶
フィルタはノード・フィルタ(node filters)とコンテナ設定フィルタ(container configuration filters)の2種類に分けられます。ノード・フィルタは Docker ホストの特徴か Docker デーモンの設定によって処理します。コンテナ設定フィルタはコンテナの特徴か、あるいは、ホスト上で利用可能なイメージによって処理します。
各フィルタには名前があります。ノード・フィルタは、以下の2つです。
constraint
(ノードの制限)health
(ノードが正常かどうか)
コンテナ設定フィルタは以下の通りです。
affinity
(親密さ)dependency
(依存関係)port
(ポート)
swarm manage
コマンドで Swarm マネージャの起動する時、全てのフィルタが指定可能です。もしも Swarm に対して利用可能なフィルタを制限したい場合は、 --filter
フラグと名前のサブセットを指定します。
$ swarm manage --filter=health --filter=dependency
注釈
コンテナ設定フィルタに一致するのは全てのコンテナが対象でです。フィルタが適用されるのは停止しているコンテナも含みます。コンテナによって使用されているノードを解放するには、ノード上からコンテナを削除する必要があります。
ノード・フィルタ¶
コンテナ作成時とイメージ構築時に、 `constraint
か health
フィルタを使い、コンテナをスケジューリングするノード群を選択できます。
constraint (制限)フィルタを使う¶
ノード制限(constraint;コンストレイント=制限・制約の意味)は Docker のデフォルトのタグやカスタム・ラベルを参照します。デフォルトのタグとは docker info
の情報を元にします。しばし Docker ホストの設定状態に関連づけられます。現在以下の項目をデフォルト・タグとして利用できます。
node
ノードを参照するための ID もしくは名前storagedriver
executiondriver
kernelversion
operatingsystem
カスタム・ノード・ラベルは docker daemon
起動時に追加できます。実行例:
$ docker daemon --label com.example.environment="production" --label
com.example.storage="ssd"
そして、クラスタ上でコンテナを起動するときに、これらのデフォルト・タグかカスタム・ラベルを使って制限(constraint)を指定可能です。Swarm スケジューラはクラスタ上に条件が一致するノードを探し、そこでコンテナを起動します。この手法は、いくつもの実践的な機能になります。
- ホスト・プロパティを指定した選択(
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
ノードがクラスタに登録されると、Swarm マスタは個々のタグを取得します。マスタは新しいコンテナをスケジューリングする時に、ここで取得下タグの情報を使って処理します。
ノードがクラスタに登録されると、マスタは各々のタグを取得し、新しいコンテナをスケジューリングするときにそれらを反映します。
先ほどのサンプルを例に進めましょう。クラスタには node-1
と node-2
があります。このクラスタ上に MySQL サーバ・コンテナを実行できます。コンテナの実行時、 constraint
(制限) を使い、データベースが良い I/O 性能を得られるようにできます。そのためには、フラッシュ・ドライブを持つノードをフィルタします。
$ docker tcp://<manager_ip:manager_port> run -d -P -e constraint:storage==ssd --name db mysql
f8b693db9cd6
$ docker tcp://<manager_ip:manager_port> 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 tcp://<manager_ip:manager_port> run -d -P -e constraint:storage==disk --name frontend nginx
963841b138d8
$ docker tcp://<manager_ip:manager_port> 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
で起動します。
最後に、 docker build
の構築時の引数としてもノード制限を利用できます。今度もフラッシュ・ドライブを避けてみましょう。
$ mkdir sinatra
$ cd sinatra
$ echo "FROM ubuntu:14.04" > Dockerfile
$ echo "MAINTAINER Kate Smith <ksmith@example.com>" >> Dockerfile
$ echo "RUN apt-get update && apt-get install -y ruby ruby-dev" >> Dockerfile
$ echo "RUN gem install sinatra" >> Dockerfile
$ docker build --build-arg=constraint:storage==disk -t ouruser/sinatra:v2 .
Sending build context to Docker daemon 2.048 kB
Step 1 : FROM ubuntu:14.04
---> a5a467fddcb8
Step 2 : MAINTAINER Kate Smith <ksmith@example.com>
---> Running in 49e97019dcb8
---> de8670dcf80e
Removing intermediate container 49e97019dcb8
Step 3 : RUN apt-get update && apt-get install -y ruby ruby-dev
---> Running in 26c9fbc55aeb
---> 30681ef95fff
Removing intermediate container 26c9fbc55aeb
Step 4 : RUN gem install sinatra
---> Running in 68671d4a17b0
---> cd70495a1514
Removing intermediate container 68671d4a17b0
Successfully built cd70495a1514
$ docker images
REPOSITORY TAG IMAGE ID CREATED VIRTUAL SIZE
dockerswarm/swarm master 8c2c56438951 2 days ago 795.7 MB
ouruser/sinatra v2 cd70495a1514 35 seconds ago 318.7 MB
ubuntu 14.04 a5a467fddcb8 11 days ago 187.9 MB
health フィルタを使う¶
ノード health
フィルタは障害の発生したノードにコンテナをスケジュールするの防ぎます。対象のノードはダウンしているか、クラスタ・ストアとの通信ができないことが考えられます。
コンテナ・フィルタ¶
コンテナの作成時、3種類のコンテナ・フィルタを使えます。
affinity
dependency
port
アフィニティ(親密さ)フィルタを使う¶
アフィニティ(親密さ)フィルタを使えば、コンテナ間を「集めて」作成できます。例えばコンテナを実行する時に、次の3つの親密さを元にして Swarm に対してスケジュールできます。
- コンテナ名か ID
- イメージのあるホスト
- コンテナに適用したカスタム・ラベル
これらのアフィニティ(親密さ)とは、コンテナを同じネットワーク・ノード上で実行することです。それぞれどのノード上で実行しているかどうか、知る必要がありません。
名前アフィニティの例¶
新しいコンテナを、既存のコンテナ名や ID を基にしてスケジューリングできます。例えば、 frontend
という名前で nginx
を実行します。
$ docker tcp://<manager_ip:manager_port> run -d -p 80:80 --name frontend nginx
87c4376856a8
$ docker tcp://<manager_ip:manager_port> 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 tcp://<manager_ip:manager_port> run -d --name logger -e affinity:container==frontend logger
87c4376856a8
$ docker tcp://<manager_ip:manager_port> 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
イメージ・アフィニティの例¶
コンテナを起動するとき、特定のイメージをダウンロード済みのノードのみにスケジュールすることができます。たとえば、2つのホストに redis
イメージをダウンロードし、3つめのホストに mysql
イメージをダウンロードしたい場合があるでしょう。
$ 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 tcp://<manager_ip:manager_port> run -d --name redis1 -e affinity:image==redis redis
$ docker tcp://<manager_ip:manager_port> run -d --name redis2 -e affinity:image==redis redis
$ docker tcp://<manager_ip:manager_port> run -d --name redis3 -e affinity:image==redis redis
$ docker tcp://<manager_ip:manager_port> run -d --name redis4 -e affinity:image==redis redis
$ docker tcp://<manager_ip:manager_port> run -d --name redis5 -e affinity:image==redis redis
$ docker tcp://<manager_ip:manager_port> run -d --name redis6 -e affinity:image==redis redis
$ docker tcp://<manager_ip:manager_port> run -d --name redis7 -e affinity:image==redis redis
$ docker tcp://<manager_ip:manager_port> run -d --name redis8 -e affinity:image==redis redis
$ docker tcp://<manager_ip:manager_port> 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 tcp://<manager_ip:manager_port> run -d --name redis1 -e affinity:image==06a1f75304ba redis
ラベル・アフィニティの例¶
ラベル・アフィニティによって、コンテナのラベルで引き寄せてセットアップできます。例えば、 nginx
コンテナを com.example.type=frontend
ラベルをつけて起動します。
$ docker tcp://<manager_ip:manager_port> run -d -p 80:80 --label com.example.type=frontend nginx
87c4376856a8
$ docker tcp://<manager_ip:manager_port> 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 tcp://<manager_ip:manager_port> run -d -e affinity:com.example.type==frontend logger
87c4376856a8
$ docker tcp://<manager_ip:manager_port> 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
ラベルを指定しているからです。
dependency フィルタを使う¶
コンテナの依存関係(dependency)フィルタは、既にスケジューリング済みのコンテナと同じ場所でスケジューリングするという依存関係をもたらします。現時点では、以下の依存関係を宣言できます。
--volumes-from=dependency
(共有ボリューム)--link=dependency:alias
(リンク機能)--net=container:dependency
(共有ネットワーク)
Swarm は依存関係のあるコンテナを同じノード上に置こうとします。もしそれができない場合(依存関係のあるコンテナが存在しない場合や、ノードが十分なリソースを持っていない場合)、コンテナの作成を拒否します。
必要であれば、複数の依存関係を組み合わせることもできます。例えば、 --volumes-from=A --net=container:B
は、コンテナ A
と B
を同じノード上に置こうとします。しかし、これらのコンテナが別々のノードで動いているなら、Swarm はコンテナのスケジューリングを行いません。
port フィルタを使う¶
port
フィルタが有効であれば、コンテナのポート利用がユニークになるよう設定します。Docker Swarm は対象のポートが利用可能であり、他のコンテナのプロセスにポートが専有されていないノードを選びます。ホスト側にポート番号を割り当てたい場合や、ホスト・ネットワーキング機能を使っている場合は、対象ポートの明示が必要になるかもしれません。
ブリッジ・モードでの例 ^^^^^^^^E^^^^^^^^^^^^^^^^^^^^^
デフォルトでは、コンテナは Docker のブリッジ・ネットワーク上で動作します。ブリッジ・ネットワーク上で port
フィルタを使うには、コンテナを次のように実行します。
$ docker tcp://<manager_ip:manager_port> run -d -p 80:80 nginx
87c4376856a8
$ docker tcp://<manager_ip:manager_port> ps
CONTAINER ID IMAGE COMMAND PORTS NODE NAMES
87c4376856a8 nginx:latest "nginx" 192.168.0.42:80->80/tcp node-1 prickly_engelbart
Docker Swarm はポート 80
が利用可能であり他のコンテナ・プロセスに専有されていないノードを探します。この例では node-1
にあたります。ポート 80
を使用する他のコンテナを起動しようとしても、Swarm は他のノードを選択します。理由は node-1
では既にポート 80
が使われているからです。
$ docker tcp://<manager_ip:manager_port> run -d -p 80:80 nginx
963841b138d8
$ docker tcp://<manager_ip:manager_port> 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
同じコマンドを繰り返すと node-3
が選ばれます。これは node-1
と node-2
の両方でポート 80
が使用済みのためです。
$ docker tcp://<manager_ip:manager_port> run -d -p 80:80 nginx
963841b138d8
$ docker tcp://<manager_ip:manager_port> 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
最終的に、Docker Swarm は他のコンテナがポート 80
を要求しても拒否するでしょう。クラスタ上のすべてのノードでポートが使えないためです。
$ docker tcp://<manager_ip:manager_port> run -d -p 80:80 nginx
2014/10/29 00:33:20 Error response from daemon: no resources available to schedule container
各ノード中のポート 80
は、各コンテナによって専有されています。これはコンテナ作成時からのものであり、コンテナを削除するとポートは解放されます。コンテナが exited
(終了)の状態であれば、まだポートを持っている状態です。もし node-1
の prickly_engelbart
が停止したとしても、ポートの情報は削除されないため、 node-1
上でポート 80
を必要とする他のコンテナの起動を試みても失敗します。nginx インスタンスを起動するには、 prickly_engelbart
コンテを再起動するか、あるいは prickly_engelbart
コンテナを削除後に別のコンテナを起動します。
ホスト・ネットワーキング機能とノード・ポート・フィルタを使う¶
コンテナ実行時に --net=host
を指定すると、デフォルトの bridge
モードとは違い、 host
モードはどのポートもバインドしません。そのため、 host モードでは公開したいポート番号を明示する必要があります。このポート公開には Dockerfile
で EXPOSE
命令を使うか、コマンドラインで --expose
を指定します。Swarm は host
モードで新しいコンテナを作成しようとする時にも、これらの情報を利用します。
例えば、以下のコマンドは3つのノードのクラスタで nginx
を起動します。
$ docker tcp://<manager_ip:manager_port> run -d --expose=80 --net=host nginx
640297cb29a7
$ docker tcp://<manager_ip:manager_port> run -d --expose=80 --net=host nginx
7ecf562b1b3f
$ docker tcp://<manager_ip:manager_port> run -d --expose=80 --net=host nginx
09a92f582bc2
docker ps
コマンドを実行してもポートをバインド(拘束)している情報が表示されないのは、全てのノードで host
ネットワークを利用しているためです。
$ docker tcp://<manager_ip:manager_port> 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 tcp://<manager_ip:manager_port> 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 tcp://<manager_ip:manager_port> run -d -p 81:80 nginx:latest
832f42819adc
$ docker tcp://<manager_ip:manager_port> 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
フィルタ表現の書き方¶
ノード constraint
やコンテナ affinity
フィルタをノードに適用するには、コンテナがフィルタ表現を使うため環境変数の指定が必要です。例:
$ docker tcp://<manager_ip:manager_port> run -d --name redis1 -e affinity:image==~redis redis
表現は次のような記述方式です。
<filter-type>:<key><operator><value>
<filter-type>
は affinity
か constraint
のキーワードのどちらかです。使いたいフィルタのタイプによって異なります。
<key>
は英数字のパターンであり、先頭はアルファベットかアンダースコアです。 <key>
に相当するのは以下の条件です。
container
キーワードnode
キーワード- デフォルト・タグ(node 制限)
- カスタム・メタデータ・ラベル(node あるいは containers)
<オペレータ>
(演算子)は ==
か !=
のどちらかです。デフォルトではフィルタ処理はハード・エンフォース(hard enforced:強制)です。指定した表現に一致しなければ、マネージャはコンテナをスケジュールしません。 ~
(チルダ)を使い 「ソフト」表現を作成できます。こちらはフィルタ条件に一致しなくても、スケジューラ自身のストラテジに従ってコンテナを実行します。
<value>
は英数時、ドット、ハイフン、アンダースコアと、以下を組みあわせた文字列です。
- 部分一致、例えば
abc*
。 /regexp/
形式の正規表現。Go 言語の正規表現構文をサポート。
現時点の Swarm は、以下のような命令をサポートしています。
constraint:node==node1
は、ノードnode1
に一致。constraint:node!=node1
は、node1
をのぞく全てのノードに一致。constraint:region!=us*
は、us
が付いているリージョン以外のノードに一致。constraint:node==/node[12]/
は、node1
とnode2
に一致。constraint:node==/node\d/
は、node
+ 10進数の1文字に一致。constraint:node!=/node-[01]/
は、node-0
とnode-1
以外の全てのノードに一致。constraint:node!=/foo\[bar\]/
は、foo[var]
以外の全てのノードに一致。constraint:node==/(?i)node1/
は、大文字・小文字を区別しないnode1
に一致。そのため、NoDe1
やNODE1
も一致する。affinity:image==~redis
は、redis
に一致する名前のイメージがあるノード上でコンテナを実行。constraint:region==~us*
は、*us
に一致するリージョンのノードを探す。affinity:container!=~redis*
は、redis*
という名前を持つコンテナが動いていないノードでnode5
コンテナをスケジュール。
警告
以下 v1.9 用のドキュメント、削除予定
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*
コンテナを持っている場合、スケジューラはアフィニティのルールを破棄し、ストラテジに従ってスケジュールします。