Swarm で Compose を使う

Docker Compose と Docker Swarm は完全な統合を目指しています。つまり、Compose アプリケーションを Swarm クラスタに適用したら、単一の Docker ホスト上で展開するのと同じように動作します。

使用する Compose ファイル形式のバージョン によって、統合できる範囲が異なります。

  1. バージョン1で links (リンク機能)を使う場合、アプリケーションは動作します。しかし、Swarm は全てのコンテナを1つのホスト上にスケジュールします。これは古いネットワーキング・システム上ではホストを横断したコンテナが扱えないためです。
  1. バージョン2であれば、アプリケーションを変更しなくても動作するでしょう。
  • 主な 制限 については以下をご覧ください。
  • Swarm では オーバレイ・ドライバ の設定や、マルチホスト・ネットワーク機能をサポートするカスタム・ドライバが利用可能です。

Docker Machine で Swarm クラスタやオーバレイ・ドライバをセットアップする方法は、 マルチホスト・ネットワーク機能を始める をご覧ください。セットアップ後にアプリケーションをデプロイするには、次のように非常にシンプルです。

$ eval "$(docker-machine env --swarm <name of swarm master machine>)"
$ docker-compose up

Compose の制限

イメージ構築

Dockerfile を使ったイメージ構築は、Swarm 上では単一ホスト上でしか行えません。そのため、構築したイメージは対象のホスト上のみに存在しており、他のノードに配布できません。

Compose を複数のノードにスケールさせる課題がある時は、自分自身で構築したレジストリ(例: Docker Hub)にイメージを push し、 docker-compose.yml で参照させてください。

$ docker build -t myusername/web .
$ docker push myusername/web

$ cat docker-compose.yml
web:
  image: myusername/web

$ docker-compose up -d
$ docker-compose scale web=3

複数の依存関係

サービスが強制共用スケジューリング(force co-scheduling)型で複数の依存関係がある場合(以下の 自動スケジューリング をご覧ください)、 Swarm は異なったノード上でも依存関係を解決できるかもしれません。たとえば、以下の例では foo が必要とする barbaz を一緒にスケジュールします。

version: "2"
services:
  foo:
    image: foo
    volumes_from: ["bar"]
    network_mode: "service:baz"
  bar:
    image: bar
  baz:
    image: baz

問題は、Swarm が最初に barbaz が別のノードにスケジュールしてしまう可能性です(この時点ではお互いの依存性はありません)。そうならないように、 foo を適切なノードに置く必要があります。

正常に行うためには、 手動スケジューリング で、3つのサービスを同じノード上で確実に起動します。

version: "2"
services:
  foo:
    image: foo
    volumes_from: ["bar"]
    network_mode: "service:baz"
    environment:
      - "constraint:node==node-1"
  bar:
    image: bar
    environment:
      - "constraint:node==node-1"
  baz:
    image: baz
    environment:
      - "constraint:node==node-1"

ホスト側のポートとコンテナの再作成

サービスがホスト側のポートを 80:8000 のように割り当てる(マップする)場合があります。それが docker-compose up の初回実行時であればエラーが出るかもしれません。

docker: Error response from daemon: unable to find a node that satisfies
container==6ab2dfe36615ae786ef3fc35d641a260e3ea9663d6e69c5b70ce0ca6cb373c02.

エラーが発生する一般的なケースは、明確な割り当てのない( イメージや Compose ファイルで定義されていない)ボリュームを持つコンテナを作成する場合です。その場合はデータ領域を予約するために、Compose は Swarm に対して、前に起動したコンテナと同じノード上に新しいコンテナをスケジュールします。この結果、ポートが衝突してしまう可能性があります。

この問題に対処する2つの解決策があります。

  • コンテナがボリュームをマウントできるボリューム・ドライバを使えば、ボリュームに名前を指定することで、コンテナがどのノードにスケジュールされても適切にマウントします。

Compose でサービスのボリュームに名前を付けるだけでは、Swarm に対してスケジューリングの指示を出しません。

version: "2"

services:
  web:
    build: .
    ports:
      - "80:8000"
    volumes:
      - web-logs:/var/log/web

volumes:
  web-logs:
    driver: custom-volume-driver
  • 新しいコンテナを作成する前に、古いコンテナを削除したら、ボリュームの中のデータが失われます。
$ docker-compose stop web
$ docker-compose rm -f web
$ docker-compose up web

コンテナのスケジューリング

自動スケジューリング

コンテナを同じ Swarm ノード上に確実にスケジュールするための、複数のオプションがあります。オプションは次の通りです。

  • network_mode: "service:..." と、 network_mode: "container:..." (と、バージョン1のフォーマットであれば net: "container:..."
  • volumes_from
  • links

手動スケジューリング

Swarm にはコンテナをどこに配置するかを制御できるようにするための、豊富なスケジューリング群と親和性の示唆(affinity hint;アフィニティ・ヒント)があります。これらはコンテナの環境を通して指定可能です。Compose では environment オプションを使って設定できます。

# 特定のノードにコンテナをスケジュールする
environment:
  - "constraint:node==node-1"

# 「storage」ラベルに「ssd」が設定されているノードにコンテナをスケジュールする
environment:
  - "constraint:storage==ssd"

# 「redis」イメージをダウンロード済みのコンテナにスケジュールする
environment:
  - "affinity:image==redis"

利用可能なフィルタと表現については、Swarm のドキュメント をご覧ください。

参考

Using Compose with Swarm
https://docs.docker.com/compose/swarm/