Docker

Swarm mode クラスタを構築して動かしてみる

More than 1 year has passed since last update.


この記事で得られるもの


  • 作業端末上の docker-machine コマンドだけで Swarm mode クラスタを構築する手順。

  • 手元の VirtualBox で試せる Swarm mode クラスタ環境

先に完成品だけ見たい方はこちら。

GitHub - hidekuro/swarm-cluster-sample


概要

Docker 1.12 以降は、とてもシンプルな手順で Swarm クラスタを構築できます。

名前解決や負荷分散は Swarm mode に組み込まれ、外部の KVS はもはや不要です。


過去の Swarm クラスタ

以前の記事 ではこんなクラスタを構築しました。

Consul にも気を配る必要があり、大変でした。

cluster_legacy.png


Docker 1.12 以降の Swarm クラスタ

現在の Docker では、 KVS が不要になりました。

cluster.png

cluster の範囲に manager も入っていますが、作図ミスではありません。

アプリケーションのデプロイ時に指定する条件次第で、 manager もクラスタリソースとなりえます。


実践


要約


  1. VM をガーッと作成

  2. manager (プライマリ) を init してトークンを生成

  3. manager (レプリカ) を join させる

  4. worker を join させる

manager とは、サービス名解決や負荷分散、コンテナ配置スケジューリングなどを行う管理ノードです。

Swarm クラスタにおいては、各種コマンドは manager のいずれかに対して実行します。

プライマリ manager は、クラスタの最初のリーダーです。

レプリカは、プライマリがダウンした際に、次のリーダーとして昇格する候補です。

worker とは、コンテナを実行するためのリソースとして manager から利用されるノードです。


構築作業


(1) VM をガーッと作成


bash

# create machines

for i in 0 1 2; do
# create manager machines
docker-machine create \
-d virtualbox \
--virtualbox-memory="512" \
--virtualbox-hostonly-cidr="10.20.${i}.1/24" \
manager-${i}

# create worker machines
for n in 0 1; do
docker-machine create \
-d virtualbox \
--virtualbox-memory="512" \
--virtualbox-hostonly-cidr="10.20.${i}.1/24" \
worker-${i}-${n}
done
done


swarm_machines.png

とりあえず VM が作成され、こんな状態になります。

まだ Swarm mode にもなっておらず、単に Docker Engine ホストが9個ある状態です。

パブリッククラウドでおなじみの zone に見立てて、サブネットを分けています。



  • zone-0 ... 10.20.0.0/24


  • zone-1 ... 10.20.1.0/24


  • zone-2 ... 10.20.2.0/24

今回の例は VirtualBox ですが、 docker-machine create のオプションを適宜設定すれば各種パブリッククラウドにも直接配備できます。


(2) manager (プライマリ) を init してトークンを生成


bash

# init primary manager

docker-machine ssh manager-0 \
docker swarm init \
--advertise-addr="$(docker-machine ip manager-0)" \
--listen-addr=0.0.0.0:2377

swarm_swarm-init.png

docker swarm init コマンド で Swarm mode に移行します。

新しいクラスタの開始なので、 manager ノードとなります。



  • --advertise-addr ... クラスタ内でこのノードを指し示すための IP を教えます。


  • --listen-addr ... manager ノードからのインバウンド通信を Listen するアドレスを指定します。

--listen-addr を指定しない場合のデフォルトは 0.0.0.0:2377 です。

(ここでは「そのポート番号どこからきたの?」とならないように明示することにします)


次に、他のノードが manager または worker として参加するために使うトークンを取得します。

# get tokens

MANAGER_TOKEN="$(docker-machine ssh manager-0 docker swarm join-token -q manager)"
WORKER_TOKEN="$(docker-machine ssh manager-0 docker swarm join-token -q worker)"

swarm_swarm-join-token.png

join-token-q オプションはトークン文字列のみを表示します。

-q を指定しない場合は Usage のようなものが表示されます)

MANAGER_TOKEN を使って join すれば manager レプリカとしてクラスタに参加します。

同様に、 WORKER_TOKEN を使えば worker としてクラスタに参加することになります。


(3) manager (レプリカ) を join させる


bash

# join replicas to cluster

for i in 1 2; do
docker-machine ssh manager-${i} \
docker swarm join \
--advertise-addr="$(docker-machine ip manager-${i})" \
--token="${MANAGER_TOKEN}" \
$(docker-machine ip manager-0):2377
done

swarm_managers-joined.png

ここまでで manager がレプリケーション構成になった状態です。

docker swarm join コマンド で、レプリカを join させます。



  • --advertise-addr ... プライマリを init した時と同じ意味です。


  • --token ... manager として参加するので、 manager 用のトークンを指定します。

引数で、参加先であるプライマリ manager ノードの IP と2377番ポートを指定します。


(4) worker を join させる


bash

# join workers to cluster

for i in 0 1 2; do
for
n in 0 1; do
docker-machine ssh worker-${i}-${n} \
docker swarm join \
--advertise-addr="$(docker-machine ip worker-${i}-${n})" \
--token="${WORKER_TOKEN}" \
$(docker-machine ip manager-${i}):2377
done
done


今度は worker 用のトークンを使って join することで、 worker ノードを参加させます。

cluster.png

これで完成形になりました。

ちなみに、どの manager ノードに対して join しても結果は同じです。

(あくまで参加先クラスタはトークンで識別され、逆にクラスタから見て参加してくるノードは --advertise-addr で表明した IP で宛て名されるため)


NOTE

Swarm mode クラスタでは、基本的に --advertise-addr で示した IP アドレスでノードを識別します。

docker-machine のドライバに VirtualBox を使っている場合(多くの場合はそうです)、 起動・停止だけでも IP が変わることがある ため、起動・停止の順序には注意が必要です。

もし IP が変わってクラスタの動作がおかしくなってしまったら、次のように docker swarm leave してから join しなおせば OK です。


bash

# MACHINE_NAME, TOKEN は適宜設定

docker-machine ssh ${MACHINE_NAME} \
docker swarm leave --force

docker-machine ssh ${MACHINE_NAME} \
docker swarm join \
--advertise-addr="$(docker-machine ip ${MACHINE_NAME})" \
--token="${TOKEN}" \
$(docker-machine ip manager-0):2377


パブリッククラウドの VM インスタンスでも内部 IP を利用してクラスタリングするかと思いますが、変動した場合は同様です。


もう一度概要図

cluster.png


  • zone ごとに3台ずつ、合計9台のマシンがあります。

  • manager が合計3台あり、レプリケーション構成になっています。

  • worker が合計6台あり、 manager を通してひとつのクラスタに参加しています。


    • 各 manager の直接的配下というわけではありません。

    • 例えば manager-1 がダウンしても worker-1-0, worker-1-1 はクラスタの worker として有効です。



  • デプロイの条件記述しだいで、 manager もクラスタのリソースになりえます。

  • オーバーレイネットワークを利用して、マシンや zone をまたいだ透過的な通信が可能です。

  • ノードへの外部アクセスは、 manager によってクラスタ内のノードにロードバランシングされます。


試してみる

docker-machine を使った作業でおなじみの env をやります。

Windows の方は適宜読み替えて下さい。


bash

eval $(docker-machine env manager-0)


ノード一覧を見てみます。


bash

docker node ls


ID                           HOSTNAME    STATUS  AVAILABILITY  MANAGER STATUS

0w0hi49opinwoilkudr4x0h1b worker-2-1 Ready Active
1sc2qbqsfa0a6tqrbe4uz8in2 worker-1-1 Ready Active
557xwsctmlx6waag2hnguhwu8 worker-0-1 Ready Active
7mdku2sk5pyaji72k64p6o2qd worker-1-0 Ready Active
f4vh1htdhb1pltu75nhdmqprl manager-1 Ready Active Reachable
m92mx3d5i4ga119al7xomle3x * manager-0 Ready Active Leader
miys28yroy4p5ac15l708o7px worker-2-0 Ready Active
pv9rwwkzkxp50ch9rmv5qsmt6 manager-2 Ready Active Reachable
xhavctvvhm8yfnyjbnk56gzk1 worker-0-0 Ready Active

ID 昇順に並ぶので、表示順序は環境によって異なります。

このクラスタに、チュートリアルでありがちな nginx を立ててみましょう。

Compose file としてサービスを定義します。


bash

mkdir myapp

cd myapp
touch docker-compose.yml


myapp/docker-compose.yml

version: "3"

services:
web:
image: "nginx"
ports:
- "80:80"
networks:
- "my-net"
deploy:
replicas: 6
placement:
constraints:
- node.role == worker

networks:
my-net:
driver: "overlay"


要点は次の通り


  • version: "3"


  • deploy: でデプロイ時のルールを指定


    • worker ノードにのみ配備されるようにしています



  • トップレベル networks: でオーバーレイネットワークを定義

詳しくは docker service create とか Compose file リファレンスの deploy あたりを参照

これを Docker Stack としてデプロイしてみます。


bash

docker stack deploy -c ./docker-compose.yml myapp


-c で Compose file を指定し、引数で名前を指定します。(例えば myapp

Stack リストを見てみましょう。


bash

docker stack ls


NAME   SERVICES

myapp 1

この Stack に含まれる Service 一覧を確認します。


bash

docker stack services myapp


ID            NAME       MODE        REPLICAS  IMAGE

50v9f08afwra myapp_web replicated 6/6 nginx:latest

1つのサービスが定義されています。

Compose file の定義で replicas: 6 としたので、6つのレプリカが worker ノードに分散配備されているようです。

この Stack で動いているタスク(コンテナ)を表示してみます。


bash

docker stack ps myapp


ID            NAME         IMAGE         NODE        DESIRED STATE  CURRENT STATE          ERROR  PORTS

02fxtfu5myrf myapp_web.1 nginx:latest worker-0-1 Running Running 2 minutes ago
lv4orevn0a4q myapp_web.2 nginx:latest worker-0-0 Running Running 2 minutes ago
o6g6oo6aw3bb myapp_web.3 nginx:latest worker-0-1 Running Running 2 minutes ago
qqtas7mixk43 myapp_web.4 nginx:latest worker-1-0 Running Running 2 minutes ago
vyivx6seef5l myapp_web.5 nginx:latest worker-1-1 Running Running 2 minutes ago
wdo0cmntp43q myapp_web.6 nginx:latest worker-0-0 Running Running 2 minutes ago

constraintsnode.role == worker としたとおり、 worker ノードにのみ配備されています。

web サービスをスケールイン(台数を削減)してみましょう。


bash

docker service scale myapp_web=4

docker stack ps myapp

ID            NAME         IMAGE         NODE        DESIRED STATE  CURRENT STATE          ERROR  PORTS

02fxtfu5myrf myapp_web.1 nginx:latest worker-0-1 Running Running 6 minutes ago
qqtas7mixk43 myapp_web.4 nginx:latest worker-1-0 Running Running 6 minutes ago
vyivx6seef5l myapp_web.5 nginx:latest worker-1-1 Running Running 6 minutes ago
wdo0cmntp43q myapp_web.6 nginx:latest worker-0-0 Running Running 6 minutes ago

指定した数までコンテナが減りました。

同じようにスケールアウト(台数を拡充)します。


bash

docker service scale myapp_web=8


ID            NAME         IMAGE         NODE        DESIRED STATE  CURRENT STATE                     ERROR  PORTS

02fxtfu5myrf myapp_web.1 nginx:latest worker-0-1 Running Running 7 minutes ago
vbvxupix5d3x myapp_web.2 nginx:latest worker-1-0 Running Running less than a second ago
152c9w3u3nu1 myapp_web.3 nginx:latest worker-2-1 Running Preparing less than a second ago
qqtas7mixk43 myapp_web.4 nginx:latest worker-1-0 Running Running 7 minutes ago
vyivx6seef5l myapp_web.5 nginx:latest worker-1-1 Running Running 7 minutes ago
wdo0cmntp43q myapp_web.6 nginx:latest worker-0-0 Running Running 7 minutes ago
ricrj2w54i7x myapp_web.7 nginx:latest worker-2-0 Running Preparing less than a second ago
vh7x3bv3cw90 myapp_web.8 nginx:latest worker-2-0 Running Preparing less than a second ago

コンテナが8つに増えていることがわかります。

(ちょっと確認が早すぎて Preparing が混ざってますが、そのうち Running になります)


おわりに

Swarm mode クラスタの雰囲気がつかめたでしょうか。

今回の例では Stack "myapp" のサービス "myapp_web" が worker ノードに配備されるようにしました。

Stack 内のサービスへのアクセスは manager によってロードバランシングされる ため、クラスタ内のどのノード IP にアクセスしても Nginx のページが表示されます。

また 前回の記事のクラスタ では、配備されるコンテナの公開ポートがぶつからないように配慮する必要がありました。

Swarm mode クラスタでは、 配備されるコンテナには空いてるポートが割り当てられ、公開ポートへのアクセスが自動的に振り分けられます。

これによってポートの衝突を気にする必要がなくなり、気軽にノードの追加・削除やスケールイン・アウトができます。


Swarm mode の統合により、コンテナオーケストレーションという点では、標準機能でもかなり高度な運用ができるようになりました。残る大きな課題はデータボリュームの透過的なクラスタ内での共有と運搬ですかね。

現時点では Swarm クラスタを組む本来の目的から目をそらしてノードを集中的にするか、 NFS など外部的な手段に頼るしか無いと思われます。 Volume Plugins で提供されているような分散ストレージのサポートが組み込まれることを期待しましょう。



参考リンク

以上です。