TL;DR
コマンド見れば分かる人向けの完成品はこちら → hidekuro/swarm-cluster-sample
- High-Availability & Scalable
- 作業端末からの
docker
,docker-machine
コマンドで、すっきりスマートに構築する。 - docker.com のホステッド・ディスカバリ・サービスに頼らない。
下図のようなクラスタをローカル VM で作るお話です。
本番でも通じる構成です。
docker-machine
が対応するパブリッククラウドであれば、 create 時にオプションを足せばいける…?(未実施)
はじめに
公式にあるチュートリアルやガイド文書は、 スッキリしない印象でした。
- swarm の準備が「SSH で入って、このコマンドを叩いて…」と泥臭い作業がある。
- join-token を利用している例は docker.com のホステッドサービスに依存する。
- manager 自身も agent として(アプリが動くリソースとして)クラスタに参加している。
そこで、なるべく Official な感じを守りつつ、スッキリ立てる方法を模索して実践しました。
完成予想図は冒頭の画像の通りになります。
手順
大まかな流れは次の通り。
- ディスカバリサービスとして consul クラスタを構築
- プライマリ&レプリカ swarm manager を構築
- アプリが動くリソースとなる swarm agent を構築
0. 必要環境
- docker
- docker-machine
- VirtualBox
- お使いの PC のメモリ 8GB くらい
- 512MB の VM を12台、合計 6GB ほど使います。
- キツい場合、記事中の
--virtualbox-memory 512
の値を 256 くらいに下げても多分いけます。
ホスト OS は Win, macOS, Linux のどれでもいけると思います。
(ダメだったら教えて下さい)
1. Consul クラスタの作成
Swarm には、クラスタメンバーの情報を保管するためのディスカバリサービスが必要です。
公式チュートリアル中にある swarm join-token
を使う方法は docker.com がホスティングしているディスカバリサービスを利用していて、ここがダウンすると、構築した Swarm クラスタも使えなくなります。
(めったにないとは思いますが、有名なサービスは得てして DDoS 攻撃の標的になりますし…)
そこで、 VPC の内側に高可用性 Consul クラスタを構築します。
1.1. マシンの準備
各ゾーンに1台ずつそれぞれ consul0, consul1, consul2 の名前で作成します。
for i in 0 1 2; do
docker-machine create \
-d virtualbox \
--virtualbox-memory 512 \
--virtualbox-hostonly-cidr 10.20.$i.1/24 \
--engine-label zone=zone$i \
--engine-label role=consul \
consul$i
done
まだ Consul は動いていません。
--virtualbox-hostonly-cidr 10.20.$i.1/24
の指定で、ゾーンに見立てたプライベートネットワークを3つ作り、その中に立てています。
- 10.20.0.1/24 ... zone0
- 10.20.1.1/24 ... zone1
- 10.20.2.1/24 ... zone2
1.2. consul クラスタの開始
それぞれの VM で Consul Server を動かし、3台構成の高可用性クラスタを構築します。
詳細は Qiita | 公式の consul イメージで高可用性 Consul クラスタを試す で説明していますので、ここでは作業内容だけ書きます。
for i in 0 1 2; do
docker $(docker-machine config consul$i) run \
-d \
--name consul$i \
--restart always \
-p 8300-8302:8300-8302/tcp \
-p 8301-8302:8301-8302/udp \
-p 8400:8400/tcp \
-p 8500:8500/tcp \
-p 8600:8600/tcp \
-p 8600:8600/udp \
consul agent \
-server \
-node consul$i \
-ui \
-client 0.0.0.0 \
-advertise $(docker-machine ip consul$i) \
-retry-join $(docker-machine ip consul0) \
-bootstrap-expect 3
done
下記のような URL にアクセスすると同じ画面が見えるはずです。
http://$(docker-machine ip consul0)/ui
http://$(docker-machine ip consul1)/ui
http://$(docker-machine ip consul2)/ui
Consul クラスタメンバーはいずれも同位です。
「リーダーとフォロワー」の関係はあるものの、リーダーが潰れると、代わりに誰かがリーダーとなって動き続けます。
2. Swarm クラスタの作成
Consul が準備できたら、それをディスカバリサービスとして Swarm クラスタを構築します。
Swarm クラスタは manager と agent で構成されます。
種別 | 役割 |
---|---|
manager | Swarm クラスタの指揮者。コンテナを実行する agent を選出するスケジューラ機能、コンテナ間連携のためのディスカバリサービス機能など。 |
agent | 実際にコンテナが実行されるワーカー。 |
多分このイメージ図を見たほうが早い。
→ https://docs.docker.com/engine/swarm/how-swarm-mode-works/nodes/
今回は manager を3台と、各 manager に join
する agent を2台ずつ、合計12台の Swarm クラスタを形成します。
2.1. manager の作成
各ゾーンに1台ずつそれぞれ manager0, manager1, manager2 の名前で作成します。
各 manager は、同じゾーンにいる consul をディスカバリサービスに指定しています。
for i in 0 1 2; do
docker-machine create \
-d virtualbox \
--virtualbox-memory 512 \
--virtualbox-hostonly-cidr 10.20.$i.1/24 \
--engine-opt cluster-store=consul://$(docker-machine ip consul$i):8500 \
--engine-label zone=zone$i \
--engine-label role=manager \
--swarm-master \
--swarm-discovery consul://$(docker-machine ip consul$i):8500 \
--swarm-strategy spread \
--swarm-opt replication \
manager$i
done
docker-machine の swarm 系オプションにより、作成後まもなく swarm manager として利用できます。
0, 1, 2 の順で作成される都合上、 manager0 が primary 、 manager1, manager2 は replica となるでしょう。
別々の consul をディスカバリサービスにしているのにクラスタになれるのは、 consul 側がクラスタリングによって1つの大きな consul になっているからです。
各オプションの詳細は後述。
2.2. agent の作成
各ゾーンに2台ずつ、合計6台作成し、ゾーンごとの manager に join させます。
for i in 0 1 2; do # $i ... zone number
for n in 0 1; do # $n ... machine number in zone
docker-machine create \
-d virtualbox \
--engine-opt cluster-store=consul://$(docker-machine ip consul$i):8500 \
--engine-label zone=zone$i \
--engine-label role=agent \
--virtualbox-memory 512 \
--virtualbox-hostonly-cidr 10.20.$i.1/24 \
--swarm \
--swarm-discovery consul://$(docker-machine ip consul$i):8500 \
agent$i-$n
done
done
join 先はゾーンごとにいるそれぞれの manager ですが、 manager 同士がレプリケーション構成になっているので、1つの大きなクラスタになれます。
Swarm クラスタまわりのオプション解説
docker-machine create
の --swarm**
系オプションは、作成した Docker Engine ホストに Swarm mode 関連の設定を行い、 swarm イメージを実行してくれます。
- Swarm 専用の特別なマシンというわけではなく、あくまで Docker Engine ホストである
- swarm イメージのコンテナを実行して Swarm 機能を提供している
この2点はおさえておきましょう。
Docker Engine 関係
--engine-opt cluster-store
Docker のマルチホスト・オーバーレイネットワークのために必要な、ディスカバリサービスの指定です。
consul://$(docker-machine ip consul$i):8500
でゾーンごとの consul を指定しています。
これがないと docker network
や docker-compose.yml の networks:
が使えません。
--engine-label
Docker Engine ホストに任意のラベルを付けて、あとで docker service
や docker-compose
を使う時に、ノードの選定ヒントに利用できるようにしています。
今回の例では付ける必要はありませんが、マシンを立てるときはラベルを付ける習慣をつけておくと、後々役立ちます。
manager, agent 共通
--swarm-discovery
Swarm manager, agent がクラスタメンバーの情報を保管・取得するのに使用するディスカバリサービスの指定です。
ここでも consul://$(docker-machine ip consul$i):8500
でゾーンごとの consul を指定しています。
manager
--swarm-master
マシンを Swarm manager として構成します。
--swarm-strategy spread
Swarm manager のスケジューラが、コンテナを実行する時にどの agent を選ぶかの戦略を指定します。
(省略した場合のデフォルトは spread)
strategy | 説明 |
---|---|
spread | 均等分散。負荷が少ないマシンを選ぶ。マシンのダウン時に失われるコンテナ数が最小。 |
pinback | 詰め込み型。なるべくマシンのリソースを使い切ってから次のマシンを選ぶ。ダウン時に失われるコンテナが多いが、できるだけ少ないマシンに負荷が集中するようになる。 |
random | ランダム。特に計算をせずに無作為にマシンを選ぶ。 |
参考 : Docker Swarm strategies (和訳)
--swarm-opt replication
レプリケーションを有効にし、 manager 同士が自動フェイルオーバーするようにします。
これにより、どの manager に対しても同じように docker
, docker-compose
で指示することができ、障害発生時にもサービスを継続できます。
agent
--swarm
Swarm agent としてクラスタに参加します。
古いチュートリアルでは manager に --swarm --swarm-master
のように同時付与していたため、 manager 自身もワーカーとしてクラスタに参加してしまっていました。
この問題は docker/machine Issue#2302 で議論を経て解決され、現在は次の通り分離できるようになっています。
- manager には
--swarm-manager
をつけて manager 専任として振る舞う。 - agent には
--swarm
をつけてワーカーとして振る舞う。
ただし、やろうと思えば旧来と同じく --swarm --swarm-manager
のようにして manager 自身がワーカーとして振る舞うこともできます。(マシンの数を節約したい…とか)
このクラスタについて
もう1回、図を見てみましょう。
docker-machine ls
でみるとこんな感じです。
$ docker-machine ls
NAME ACTIVE DRIVER STATE URL SWARM DOCKER ERRORS
consul0 - virtualbox Running tcp://10.20.0.100:2376 v1.12.5
consul1 - virtualbox Running tcp://10.20.1.100:2376 v1.12.5
consul2 - virtualbox Running tcp://10.20.2.100:2376 v1.12.5
manager0 - virtualbox Running tcp://10.20.0.101:2376 manager0 (master) v1.12.5
manager1 - virtualbox Running tcp://10.20.1.101:2376 manager1 (master) v1.12.5
manager2 - virtualbox Running tcp://10.20.2.101:2376 manager2 (master) v1.12.5
agent0-0 - virtualbox Running tcp://10.20.0.102:2376 manager0 v1.12.5
agent0-1 - virtualbox Running tcp://10.20.0.103:2376 manager0 v1.12.5
agent1-0 - virtualbox Running tcp://10.20.1.102:2376 manager1 v1.12.5
agent1-1 - virtualbox Running tcp://10.20.1.103:2376 manager1 v1.12.5
agent2-0 - virtualbox Running tcp://10.20.2.102:2376 manager2 v1.12.5
agent2-1 - virtualbox Running tcp://10.20.2.103:2376 manager2 v1.12.5
これについての要点を見ていきます。
マシンの配置
- 1ゾーンあたりに1つの consul + Swarm manager と、複数の Swarm agent がいます。
- クラスタ全体のリソースを足したくなったら、 agent を増やして行けばいいでしょう。(スケーラブル!)
- 特定のゾーンを切り離したいときは、そのゾーンの agent を外部公開用のロードバランサーから外し、 manager と consul を Stop させればOKです。
サービスの公開
各ゾーンの agent を外部公開用のロードバランサー(別途作る)に参加させればいいでしょう。
多くの場合、 AWS ALB/ELB や GCE LB などパブリッククラウドのマネージドバランサーを使うと思いますので、ここでは追求しません。
ゾーンの死活
- ゾーン内の Swarm manager, agent のすべては、同一ゾーンにある consul に依存しています。
- consul が死んだらそのゾーンは死んだも同然ですので、ただちに同一ゾーンの manager も Stop させるべきです。
- agent はあくまで join した manager に所属するため、例えば manager1 が死んだら agent1-0, agent1-1 もクラスタから外れます。
要は ゾーン内の manager と consul は一蓮托生 にしておきましょう。
クラスタとして振る舞えている仕組み
- 各 manager がレプリケーション構成にあるため、どこの manager から join した agent でも、結果的に同じ Swarm クラスタに所属できています。
- 各 consul はクラスタ構成にあるため、 manager は自分と同じゾーンにある consul を参照するだけで、別ゾーンにいる agent の情報も参照することができています。
解決できていない課題
Swarm manager, agent にプライベートレジストリを設定していない
この例では、プライベートレジストリを指定していません。
docker service
や docker-compose.yml の build:
において、 Dockerfile をビルドした独自イメージを使うにはプライベートレジストリが必要です。
こんなプライベートレジストリを立てるか、 AWS ECR や GCP GKE のリポジトリを用意しましょう。
そして Swarm manager & agent を作成する時の docker-machine create
オプションに --engine-insecure-registry=$registry_lb_ip:5000
とかやっておくべきでしょう。
consul が死んだ時に手動で manager を落とす必要がある
ここまでで述べてきたとおり、ゾーン内の consul が死んだらそのゾーンが機能しなくなるため、ただちに切り離す必要がありますが、ここが自動化できていません。
Consul が持つ機能をうまく使えば、障害を検知して自動で manager を切り離すことができそうですが…(チラッ
おわりに
このクラスタを十分に活かすには、デプロイするアプリのサービス構成自体も、スケーラブルなクラスタ利用を前提とした構造にする必要があります。
そのへんは AWS ECS とか GCP GKE 関連の優れた記事にお任せしたいと思います。
参考リンク
- Plan for Swarm in production - Swarm 本番利用時の計画ガイド(Engine 統合前の文書)
- Build a Swarm cluster for production - Swarm 本番クラスタ構築ガイド(Engine 統合前の文書)
-
Docker Machine command-line reference -
docker-machine
リファレンス。 - Consul Documentation - Consul リファレンス。
- progrium/consul - やや古いが人気の consul イメージ。クラスタリングの参考に。
以上です。