1. Qiita
  2. 投稿
  3. docker

本番環境にも使える高可用性かつスケーラブルな Docker Swarm + Consul クラスタを構築

  • 2
    いいね
  • 0
    コメント

TL;DR

コマンド見れば分かる人向けの完成品はこちら → hidekuro/swarm-cluster-sample

  • High-Availability & Scalable
  • 作業端末からの docker, docker-machine コマンドで、すっきりスマートに構築する。
  • docker.com のホステッド・ディスカバリ・サービスに頼らない。

下図のようなクラスタをローカル VM で作るお話です。

cluster.png

本番でも通じる構成です。

docker-machine が対応するパブリッククラウドであれば、 create 時にオプションを足せばいける…?(未実施)

はじめに

公式にあるチュートリアルやガイド文書は、 スッキリしない印象でした。

  • swarm の準備が「SSH で入って、このコマンドを叩いて…」と泥臭い作業がある。
  • join-token を利用している例は docker.com のホステッドサービスに依存する。
  • manager 自身も agent として(アプリが動くリソースとして)クラスタに参加している。

そこで、なるべく Official な感じを守りつつ、スッキリ立てる方法を模索して実践しました。
完成予想図は冒頭の画像の通りになります。

手順

大まかな流れは次の通り。

  1. ディスカバリサービスとして consul クラスタを構築
  2. プライマリ&レプリカ swarm manager を構築
  3. アプリが動くリソースとなる 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 servicedocker-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回、図を見てみましょう。

cluster.png

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 をビルドした独自イメージを使うにはプライベートレジストリが必要です。

なので、理想を言えば
docker-registry.png

こんなプライベートレジストリを立てるか、 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 関連の優れた記事にお任せしたいと思います。

参考リンク


以上です。

Comments Loading...