インフラのデプロイ

このステップでは、複数の Docker ホストを作成し、そこでアプリケーション・スタックを実行します。進める前に、 アプリケーション・アーキテクチャを学ぶ 必要があります。

構築手順について

以降のサンプルを実行する想定システムは、Mac あるいは Windows です。システム上で Docker Machine を経由して VirtualBox 仮想マシンをローカルにプロビジョニングし、Docker Engine の docker コマンドを使えるようにします。作業は6つの VirtualBox 仮想マシンをインストールします。

今回のサンプルでは Docker Machine を使いますが、任意のインフラ上で実行できます。希望するインフラ上であれば、どこでも環境を構築できる設計です。例えば、Azure や Digital Ocean などのようなパブリックのクラウド・プラットフォーム上で実行できるだけでなく、データセンタ上のオンプレミスや、ノート PC 上のテスト環境ですら動かせます。

なお、これら手順では複数の値を設定するにあたり、一般的な bash コマンド代入技術を使います。次のコマンドを例に考えましょう。

$ eval $(docker-machine env keystore)

Windows 環境では、このような代入指定に失敗します。Widows 上で実行する場合は、この $(docker-machine env keystore) を実際の値に置き換えてください。

タスク1:keystore (キーストア)サーバの作成

Docker コンテナ・ネットワークと Swarm ディスカバリを有効化するために、キーバリュー・ストアのデプロイが必要です。キーストアはディスカバリ・バックエンドとして、Swarm マネージャが使うクラスタのメンバ一覧を常に更新し続けます。Swarm マネージャはこの一覧を使い、ノードにタスクを割り当てます。

オーバレイ・ネットワークはキーバリュー・ストアが必要です。キーバリュー・ストアはネットワーク状態を保持するために使います。ネットワーク状態には、ディスカバリ、ネットワーク、エンドポイント、IP アドレス等を含みます。

様々なバックエンドをサポートしています。今回のサンプルでは Consul コンテナを使います。

  1. keystore という名称の「マシン」を作成します。
$ docker-machine create -d virtualbox --virtualbox-memory "2000" \
--engine-opt="label=com.function=consul"  keystore

Engine デーモンにオプションを指定するには --engine-opt フラグを使います。Engine インスタンスをラベル付けするのに使います。

  1. ローカルのシェルを keystore Docker ホストに接続します。
$ eval $(docker-machine env keystore)
  1. consul コンテナ を起動します。
$ docker run --restart=unless-stopped -d -p 8500:8500 -h consul progrium/consul -server -bootstrap

-p フラグはコンテナ上のポート 8500 を公開します。これは Consul サーバがリッスンするためです。また、サーバ上では他のポートも公開します。確認するには docker ps コマンドを使います。

$ docker ps
CONTAINER ID        IMAGE               ...       PORTS                                                                            NAMES
372ffcbc96ed        progrium/consul     ...       53/tcp, 53/udp, 8300-8302/tcp, 8400/tcp, 8301-8302/udp, 0.0.0.0:8500->8500/tcp   dreamy_ptolemy
  1. curl コマンドを使い、ノードが応答するかテストします。
$ curl $(docker-machine ip keystore):8500/v1/catalog/nodes
[{"Node":"consul","Address":"172.17.0.2"}]

タスク2:Swarm マネージャの作成

このステップでは、Swarm マネージャを作成し、 keystore インスタンスに接続します。Swarm マネージャ・コンテナは Swarm クラスタの心臓部です。Docker コマンドを受け取り、クラスタに送り、クラスタ間のスケジューリングをする役割を持ちます。実際のプロダクションへのデプロイでは、高可用性(HA)のためにセカンダリの Swarm レプリカ・マネージャを設定すべきでしょう。

--eng-opt フラグを使い cluster-storecluster-advertise オプションが keystore サーバを参照するようにします。これらのオプションは後にコンテナ・ネットワークの作成時に使います。

  1. manager ホストを作成します。
$ docker-machine create -d virtualbox --virtualbox-memory "2000" \
--engine-opt="label=com.function=manager" \
--engine-opt="cluster-store=consul://$(docker-machine ip keystore):8500" \
--engine-opt="cluster-advertise=eth1:2376" manager

デーモンに対して manager ラベルも指定します。

  1. ローカルのシェルを manager Docker ホストに向けます。
$ eval $(docker-machine env manager)
  1. Swarm マネージャのプロセスを開始します。
$ docker run --restart=unless-stopped -d -p 3376:2375 \
-v /var/lib/boot2docker:/certs:ro \
swarm manage --tlsverify \
--tlscacert=/certs/ca.pem \
--tlscert=/certs/server.pem \
--tlskey=/certs/server-key.pem \
consul://$(docker-machine ip keystore):8500

このコマンドは boot2docker.iso あるいはマネージャ用の TLS 証明書を作成します。これはクラスタ上の他マシンにマネージャが接続する時に使います。

  1. ホスト上で Docker デーモンのログを参照し、正常に動いているか確認します。
$ docker-machine ssh manager
<-- 出力を省略 -->
docker@manager:~$ tail /var/lib/boot2docker/docker.log
time="2016-04-06T23:11:56.481947896Z" level=debug msg="Calling GET /v1.15/version"
time="2016-04-06T23:11:56.481984742Z" level=debug msg="GET /v1.15/version"
time="2016-04-06T23:12:13.070231761Z" level=debug msg="Watch triggered with 1 nodes" discovery=consul
time="2016-04-06T23:12:33.069387215Z" level=debug msg="Watch triggered with 1 nodes" discovery=consul
time="2016-04-06T23:12:53.069471308Z" level=debug msg="Watch triggered with 1 nodes" discovery=consul
time="2016-04-06T23:13:13.069512320Z" level=debug msg="Watch triggered with 1 nodes" discovery=consul
time="2016-04-06T23:13:33.070021418Z" level=debug msg="Watch triggered with 1 nodes" discovery=consul
time="2016-04-06T23:13:53.069395005Z" level=debug msg="Watch triggered with 1 nodes" discovery=consul
time="2016-04-06T23:14:13.071417551Z" level=debug msg="Watch triggered with 1 nodes" discovery=consul
time="2016-04-06T23:14:33.069843647Z" level=debug msg="Watch triggered with 1 nodes" discovery=consul

出力内容から consulmanager が正常に通信できているのが分かります。

  1. Docker ホストから抜けます。
docker@manager:~$ exit

タスク3:ロードバランサの追加

Interlock アプリケーションと Nginx をロードバランサとして使います。ロードバランサ用のホストを作る前に、Nginx で使う設定を作成します。

  1. ローカルホスト上に config ディレクトリを作成します。
  1. config ディレクトリに変更します。
$ cd config
  1. Swarm マネージャ・ホストの IP アドレスを取得します。

例:

$ docker-machine ip manager
192.168.99.101
  1. 任意のエディタで config.toml ファイルを作成し、次の内容をファイルに書き込みます。
ListenAddr = ":8080"
DockerURL = "tcp://SWARM_MANAGER_IP:3376"
TLSCACert = "/var/lib/boot2docker/ca.pem"
TLSCert = "/var/lib/boot2docker/server.pem"
TLSKey = "/var/lib/boot2docker/server-key.pem"

[[Extensions]]
Name = "nginx"
ConfigPath = "/etc/conf/nginx.conf"
PidPath = "/etc/conf/nginx.pid"
MaxConn = 1024
Port = 80
  1. 設定ファイルにおいて、 SWARM_MANAGE_IP は手順3で取得した manager の IP アドレスに書き換えてください。

この値はロードバランサがマネージャのイベント・ストリームを受信するために使います。

  1. config.toml ファイルを保存して閉じます。
  1. ロードバランサ用にマシンを作成します。
$ docker-machine create -d virtualbox --virtualbox-memory "2000" \
--engine-opt="label=com.function=interlock" loadbalancer
  1. 環境を loadbalancer に切り替えます。
$ eval $(docker-machine env loadbalancer)
  1. interlock コンテナを起動します。
$ docker run \
    -P \
    -d \
    -ti \
    -v nginx:/etc/conf \
    -v /var/lib/boot2docker:/var/lib/boot2docker:ro \
    -v /var/run/docker.sock:/var/run/docker.sock \
    -v $(pwd)/config.toml:/etc/config.toml \
    --name interlock \
    ehazlett/interlock:1.0.1 \
    -D run -c /etc/config.toml

このコマンドは現在のディレクトリにある config.toml ファイルを読み込みます。コマンド実行後、イメージを実行しているのを確認します。

$ docker ps
CONTAINER ID        IMAGE                      COMMAND                  CREATED             STATUS              PORTS                     NAMES
d846b801a978        ehazlett/interlock:1.0.1   "/bin/interlock -D ru"   2 minutes ago       Up 2 minutes        0.0.0.0:32770->8080/tcp   interlock

イメージが実行中でなければ、 docker ps -a を実行してシステム上で起動した全てのイメージを表示します。そして、コンテナが起動に失敗していれば、ログを取得できます。

$ docker logs interlock
INFO[0000] interlock 1.0.1 (000291d)
DEBU[0000] loading config from: /etc/config.toml
FATA[0000] read /etc/config.toml: is a directory

このエラーであれば、通常は config.toml ファイルがある同じ config ディレクトリ内で docker run を実行したのが原因でしょう。コマンドを実行し、次のような衝突が表示する場合があります。

docker: Error response from daemon: Conflict. The name "/interlock" is already in use by container d846b801a978c76979d46a839bb05c26d2ab949ff9f4f740b06b5e2564bae958. You have to remove (or rename) that container to be able to reuse that name.

このような時は、 docker rm interlock で interlock コンテナを削除し、再度試みてください。

  1. ロードバランサ上で nginx コンテナを起動します。
$ docker run -ti -d \
  -p 80:80 \
  --label interlock.ext.name=nginx \
  --link=interlock:interlock \
  -v nginx:/etc/conf \
  --name nginx \
  nginx nginx -g "daemon off;" -c /etc/conf/nginx.conf

タスク4:他の Swarm ノードを作成

Swarm クラスタのホストを「ノード」と呼びます。既にマネージャ・ノードを作成しました。ここでの作業は、各ノード用の仮想ホストを作成します。3つのコマンドが必要です。

  • Docker Machine でホストを作成
  • ローカル環境から新しい環境に切り替え
  • ホストを Swarm クラスタに追加

Mac あるいは Windows 以外で構築している場合、swarm ノードに追加するには join コマンドを実行するだけです。それだけで Consul ディスカバリ・サービスに登録します。また、ノードの作成時には次の例のようにラベルを付けます。

--engine-opt="label=com.function=frontend01"

これらのラベルはアプリケーション・コンテナを開始した後に使います。以降のコマンドで、各ノードに対してラベルを適用します。

  1. frontend01 ホストを作成し、Swarm クラスタに追加します。
$ docker-machine create -d virtualbox --virtualbox-memory "2000" \
--engine-opt="label=com.function=frontend01" \
--engine-opt="cluster-store=consul://$(docker-machine ip keystore):8500" \
--engine-opt="cluster-advertise=eth1:2376" frontend01
$ eval $(docker-machine env frontend01)
$ docker run -d swarm join --addr=$(docker-machine ip frontend01):2376 consul://$(docker-machine ip keystore):8500
  1. frontend02 仮想マシンを作成します。
$ docker-machine create -d virtualbox --virtualbox-memory "2000" \
--engine-opt="label=com.function=frontend02" \
--engine-opt="cluster-store=consul://$(docker-machine ip keystore):8500" \
--engine-opt="cluster-advertise=eth1:2376" frontend02
$ eval $(docker-machine env frontend02)
$ docker run -d swarm join --addr=$(docker-machine ip frontend02):2376 consul://$(docker-machine ip keystore):8500
  1. worker01 仮想マシンを作成します。
$ docker-machine create -d virtualbox --virtualbox-memory "2000" \
--engine-opt="label=com.function=worker01" \
--engine-opt="cluster-store=consul://$(docker-machine ip keystore):8500" \
--engine-opt="cluster-advertise=eth1:2376" worker01
$ eval $(docker-machine env worker01)
$ docker run -d swarm join --addr=$(docker-machine ip worker01):2376 consul://$(docker-machine ip keystore):8500
  1. dbstore 仮想マシンを作成します。
$ docker-machine create -d virtualbox --virtualbox-memory "2000" \
--engine-opt="label=com.function=dbstore" \
--engine-opt="cluster-store=consul://$(docker-machine ip keystore):8500" \
--engine-opt="cluster-advertise=eth1:2376" dbstore
$ eval $(docker-machine env dbstore)
$ docker run -d swarm join --addr=$(docker-machine ip dbstore):2376 consul://$(docker-machine ip keystore):8500
  1. 動作確認をします。

この時点では、アプリケーションが必要なインフラをデプロイ完了しました。テストは、次のようにマシンが実行しているか一覧表示します。

$ docker-machine ls
NAME           ACTIVE   DRIVER       STATE     URL                         SWARM   DOCKER    ERRORS
dbstore        -        virtualbox   Running   tcp://192.168.99.111:2376           v1.10.3
frontend01     -        virtualbox   Running   tcp://192.168.99.108:2376           v1.10.3
frontend02     -        virtualbox   Running   tcp://192.168.99.109:2376           v1.10.3
keystore       -        virtualbox   Running   tcp://192.168.99.100:2376           v1.10.3
loadbalancer   -        virtualbox   Running   tcp://192.168.99.107:2376           v1.10.3
manager        -        virtualbox   Running   tcp://192.168.99.101:2376           v1.10.3
worker01       *        virtualbox   Running   tcp://192.168.99.110:2376           v1.10.3
  1. Swarm マネージャが全てのノードを一覧表示するのを確認します。
$ docker -H $(docker-machine ip manager):3376 info
Containers: 4
 Running: 4
 Paused: 0
 Stopped: 0
Images: 3
Server Version: swarm/1.1.3
Role: primary
Strategy: spread
Filters: health, port, dependency, affinity, constraint
Nodes: 4
 dbstore: 192.168.99.111:2376
  └ Status: Healthy
  └ Containers: 1
  └ Reserved CPUs: 0 / 1
  └ Reserved Memory: 0 B / 2.004 GiB
  └ Labels: com.function=dbstore, executiondriver=native-0.2, kernelversion=4.1.19-boot2docker, operatingsystem=Boot2Docker 1.10.3 (TCL 6.4.1); master : 625117e - Thu Mar 10 22:09:02 UTC 2016, provider=virtualbox, storagedriver=aufs
  └ Error: (none)
  └ UpdatedAt: 2016-04-07T18:25:37Z
 frontend01: 192.168.99.108:2376
  └ Status: Healthy
  └ Containers: 1
  └ Reserved CPUs: 0 / 1
  └ Reserved Memory: 0 B / 2.004 GiB
  └ Labels: com.function=frontend01, executiondriver=native-0.2, kernelversion=4.1.19-boot2docker, operatingsystem=Boot2Docker 1.10.3 (TCL 6.4.1); master : 625117e - Thu Mar 10 22:09:02 UTC 2016, provider=virtualbox, storagedriver=aufs
  └ Error: (none)
  └ UpdatedAt: 2016-04-07T18:26:10Z
 frontend02: 192.168.99.109:2376
  └ Status: Healthy
  └ Containers: 1
  └ Reserved CPUs: 0 / 1
  └ Reserved Memory: 0 B / 2.004 GiB
  └ Labels: com.function=frontend02, executiondriver=native-0.2, kernelversion=4.1.19-boot2docker, operatingsystem=Boot2Docker 1.10.3 (TCL 6.4.1); master : 625117e - Thu Mar 10 22:09:02 UTC 2016, provider=virtualbox, storagedriver=aufs
  └ Error: (none)
  └ UpdatedAt: 2016-04-07T18:25:43Z
 worker01: 192.168.99.110:2376
  └ Status: Healthy
  └ Containers: 1
  └ Reserved CPUs: 0 / 1
  └ Reserved Memory: 0 B / 2.004 GiB
  └ Labels: com.function=worker01, executiondriver=native-0.2, kernelversion=4.1.19-boot2docker, operatingsystem=Boot2Docker 1.10.3 (TCL 6.4.1); master : 625117e - Thu Mar 10 22:09:02 UTC 2016, provider=virtualbox, storagedriver=aufs
  └ Error: (none)
  └ UpdatedAt: 2016-04-07T18:25:56Z
Plugins:
 Volume:
 Network:
Kernel Version: 4.1.19-boot2docker
Operating System: linux
Architecture: amd64
CPUs: 4
Total Memory: 8.017 GiB
Name: bb13b7cf80e8

このコマンドは Swarm ポートに対して処理しているため、クラスタ全体の情報を返します。操作対象Swarm マネージャあり、ノードではありません。

次のステップ

キーストア、ロードバランサ、Swarm クラスタのインフラが動きました。これで 投票アプリケーションの構築と実行 ができます。