docker swarmでサービスの冗長化を試す

Created 2018年6月27日12:20
Updated 2018年7月6日11:24
Categories Docker 自宅サーバー swarm

以前からdocker swarmについてまとめようと思っていたので、この機会にまとめておきます。

とはいえ今はKubernetesがオーケストレーションツールとしてはデファクトになっているので、ガッツリ使うには向かないかもしれません。

しかし、Dockerのデフォルトの機能として入っており、なおかつ手軽に使えるという点ではかなり便利です。

我が家の自宅サーバーも冗長化にdocker swarmを使っており、かなり手軽に冗長構成を実現できますので、ライトな自宅サーバー勢には十分おススメできると思います。

本稿では、とりあえずdocker swarmでクラスタを作成してサービスを展開してみるところまでを紹介したいと思います。

環境

  • Ubuntu 18.04 64bit
  • Docker 18.03.1-ce
  • docker-machine 0.14.0

swarmクラスタを作成する

まず、構築するノードを4台用意します。

物理サーバーを準備するのは面倒なのでdocker-machineコマンドを使って仮想マシンを作ります。

docker-machine create --driver virtualbox swarm1
docker-machine create --driver virtualbox swarm2
docker-machine create --driver virtualbox swarm3
docker-machine create --driver virtualbox swarm4

作った仮想マシンを確認してみましょう。

$ docker-machine ls
NAME      ACTIVE   DRIVER       STATE     URL                         SWARM   DOCKER        ERRORS
swarm1    -        virtualbox   Running   tcp://192.168.99.100:2376           v18.05.0-ce
swarm2    -        virtualbox   Running   tcp://192.168.99.101:2376           v18.05.0-ce
swarm3    -        virtualbox   Running   tcp://192.168.99.102:2376           v18.05.0-ce
swarm4    -        virtualbox   Running   tcp://192.168.99.103:2376           v18.05.0-ce

続いてswarmクラスタを初期化します。

初期化にはマシンのIPアドレスが必要なので、docker-machine ipコマンドで調べます。

$ docker-machine ip swarm1
192.168.99.100

swarm1のDockerデーモンに接続して、swarmクラスタの初期化を行います。

$ eval "$(docker-machine env swarm1)"
$ docker swarm init --advertise-addr 192.168.99.100
Swarm initialized: current node (ucjwdjmhay5yoneclus2ocdfm) is now a manager.

To add a worker to this swarm, run the following command:

    docker swarm join --token SWMTKN-1-4sf90z050mwii6mtq599fk2d6cma51tuxulz8txiwomh6jsued-6wffxdh7cyxc0ei4lj9ycdf27 192.168.99.100:2377

To add a manager to this swarm, run 'docker swarm join-token manager' and follow the instructions.

swarm1がリーダーノードとして初期化されました。

ただ、このままではswarm1がダウンした際にクラスタがダウンしてしまうので、リーダーノードをもう1台追加します。

リーダーを追加する場合は、次のコマンドでトークンを調べます。

$ docker swarm join-token manager
To add a manager to this swarm, run the following command:

docker swarm join --token SWMTKN-1-4sf90z050mwii6mtq599fk2d6cma51tuxulz8txiwomh6jsued-62ihat1ahqs06tzuzzznfwg8q 192.168.99.100:2377

丁寧に実行するコマンドまで教えてくれるので、swarm2に接続してこちらを実行しましょう。

$ eval "$(docker-machine env swarm2)"
$ docker swarm join --token SWMTKN-1-4sf90z050mwii6mtq599fk2d6cma51tuxulz8txiwomh6jsued-62ihat1ahqs06tzuzzznfwg8q 192.168.99.100:2377
This node joined a swarm as a manager.

これでリーダーノードが2台に増えました。どちらかが落ちた場合は、残っている方がリーダーとなります。

続いてワーカーを追加します。

追加するにはswarm1でのdocker swarm init時に表示されていたコマンドをswarm3とswarm4で実行します。

$ eval "$(docker-machine env swarm3)"
$ docker swarm join --token SWMTKN-1-4sf90z050mwii6mtq599fk2d6cma51tuxulz8txiwomh6jsued-6wffxdh7cyxc0ei4lj9ycdf27 192.168.99.100:2377
This node joined a swarm as a worker.
$ eval "$(docker-machine env swarm4)"
$ docker swarm join --token SWMTKN-1-4sf90z050mwii6mtq599fk2d6cma51tuxulz8txiwomh6jsued-6wffxdh7cyxc0ei4lj9ycdf27 192.168.99.100:2377
This node joined a swarm as a worker.

ここまでの作業でリーダー2台、ワーカー2台のswarmクラスタが作成できました。

再びswarm1に接続し、クラスタの内容を確認してみましょう。

$ eval "$(docker-machine env swarm1)"
$ docker node ls
ID                            HOSTNAME            STATUS              AVAILABILITY        MANAGER STATUS      ENGINE VERSION
ucjwdjmhay5yoneclus2ocdfm *   swarm1              Ready               Active              Leader              18.05.0-ce
llgmuqlmpj7cqusxjamv16rdg     swarm2              Ready               Active              Reachable           18.05.0-ce
ba994vu4dksynpfkt4eqd0hji     swarm3              Ready               Active                                  18.05.0-ce
jldw462nkzy9maowqm9gpeb7w     swarm4              Ready               Active                                  18.05.0-ce

クラスタが作成できたので、いよいよサービスを展開していきます。

サービスを展開する

今回はシンプルにHTMLを表示するWebサイトをnginxコンテナとして建ててみましょう。

まず、ローカルにhtmlリソースとなるディレクトリを作成し、swarm1~4にマウントポイントを作成します。

$ mkdir -p mnt/html
$ docker-machine ssh swarm1 mkdir html
$ docker-machine ssh swarm2 mkdir html
$ docker-machine ssh swarm3 mkdir html
$ docker-machine ssh swarm4 mkdir html

ちなみに、docker-machineで作成される仮想マシンはboot2dockerというイメージを使っており、ホームディレクトリは/home/dockerとなります。

なので、このコマンドでは/home/docker/htmlが作成されています。

続いて、mnt/html内にindex.htmlを配置します。

$ nano mnt/html/index.html
Hello swarm!!

最後にdocker-machine mountコマンドを用いてmnt/htmlをswarm1~4の/home/docker/htmlにマウントします。

sudo docker-machine mount swarm1:/home/docker/html mnt/html
sudo docker-machine mount swarm2:/home/docker/html mnt/html
sudo docker-machine mount swarm3:/home/docker/html mnt/html
sudo docker-machine mount swarm4:/home/docker/html mnt/html

これですべてのノードから同一のリソースを参照できるようになりました。

続いて、クラスタに展開するサービスの設定を書いていきます。

サービスの設定はdocker-composeと同じ書式でstack.ymlというファイルに記述します。

今回は以下の条件の設定を記述しました。

  • 8080番ポートをnginxに仕向ける
  • /home/docker/htmlをリソースとしてマウント
  • クラスタ内の全てのノードに展開
$ nano stack.yml 
version: '3.3'

services:

  nginx:
    image: nginx
    ports:
      # 外部からの8080番へのアクセスをnginxの80番に仕向ける
      - 8080:80
    deploy:
      # 全てのノードに展開する
      mode: global
    volumes:
      # htmlディレクトリをマウントしてnginxのリソースとして使用する
      - /home/docker/html:/usr/share/nginx/html:ro

設定ができたので、docker stack deployコマンドでtestというstack名でクラスタに展開します。

展開したら、docker service lsでサービスが無事にデプロイできているか確認してください。

$ docker stack deploy -c stack.yml test
$ docker service ls
ID                  NAME                MODE                REPLICAS            IMAGE               PORTS
rvr59910pl3p        test_nginx          global              4/4                 nginx:latest        *:8080->80/tcp

REPLICASが4/4となっているので、4ノード全てに展開できていることが確認できました。

スケールやノードダウン時の挙動を試す

続いて、クラスタの一部のノードにサービスを展開してみます。

docker stack rmで先ほど展開したサービスを削除して、stack.ymlを少し編集します。

$ docker stack rm test
$ nano stack.yml
...
    deploy:
      # クラスタ中の2台に展開
      mode: replicated
      replicas: 2
...

この設定だと、クラスタ内から選ばれた2台にnginxが展開されます。

もう一度docker stack deployでサービスを展開してください。

$ docker stack deploy -c stack.yml test
$ docker service ls
ID                  NAME                MODE                REPLICAS            IMAGE               PORTS
nxe74tu11s1j        test_nginx          replicated          2/2                 nginx:latest        *:8080->80/tcp

今度はREPLICASの値が2になっていますね。

どのノードでサービスを展開しているか調べます。

$ docker stack ps test
ID                  NAME                IMAGE               NODE                DESIRED STATE       CURRENT STATE           ERROR               PORTS
u52pbqn4xdjn        test_nginx.1        nginx:latest        swarm2              Running             Running 8 minutes ago                       
q8ralejxn1oe        test_nginx.2        nginx:latest        swarm1              Running             Running 8 minutes ago

swarm1とswarm2に展開されているようですね。

docker service scaleコマンドでnginxをクラスタ内の3台に展開するよう設定してみましょう。

$ docker service scale test_nginx=3
test_nginx scaled to 3
overall progress: 3 out of 3 tasks 
1/3: running   [==================================================>] 
2/3: running   [==================================================>] 
3/3: running   [==================================================>] 
verify: Service converged
$ docker stack ps test
ID                  NAME                IMAGE               NODE                DESIRED STATE       CURRENT STATE            ERROR               PORTS
u52pbqn4xdjn        test_nginx.1        nginx:latest        swarm2              Running             Running 9 minutes ago                        
q8ralejxn1oe        test_nginx.2        nginx:latest        swarm1              Running             Running 9 minutes ago                        
215vusgx0dpm        test_nginx.3        nginx:latest        swarm3              Running             Running 20 seconds ago

swarm3にもnginxが展開され、3台の冗長構成になりました。

手軽にサービスをスケールできてとても良いですね。

さて、せっかく冗長構成にしたので、ノードを1台落としてフェイルケースを試してみましょう。

現在、swarm1~3の3台にnginxが展開されていますが、もしswarm3が何かしらの理由でシャットダウンしてしまったらどうなるでしょうか。

$ docker-machine stop swarm3
(少し時間をおきます)
$ docker stack ps test
ID                  NAME                IMAGE               NODE                DESIRED STATE       CURRENT STATE            ERROR               PORTS
u52pbqn4xdjn        test_nginx.1        nginx:latest        swarm2              Running             Running 12 minutes ago                       
q8ralejxn1oe        test_nginx.2        nginx:latest        swarm1              Running             Running 12 minutes ago                       
szpb28c5uorv        test_nginx.3        nginx:latest        swarm4              Running             Running 2 seconds ago                        
215vusgx0dpm         \_ test_nginx.3    nginx:latest        swarm3              Shutdown            Running 18 seconds ago

swarm3がシャットダウンとなり、代わりにswarm4でnginxが実行されています。

クラスタ内のノードが落ちてしまった場合にもしっかりとスケールの規模が維持されることが確認できました。

とりあえず今回はここまでです。今後も複雑なサービスの展開や続きの記事を書きたいと思いますので、その際はこちらに引用を追加したいと思います。

あとがき

今回紹介した手法は物理マシンにも同じように適用可能です(セキュリティや共有ファイルの設定についてはちゃんと考える必要がありますが・・・)。

自宅サーバーは定期的に掃除したりシステムをアップデートしたりする際にどうしてもダウンタイムが発生しがちですが、docker swarmをうまく使ってあげればダウンタイム無しでのクラスタのアップデートも夢ではありません。

何より、手軽にクラスタが連携してる感を味わえますのでとても楽しいです。

docker swarmを使って自宅サーバーライフを楽しんでいただければ幸いです。

コメントを投稿

コメント

けろっぴ

試してみました。とても参考になります。 1点、 docker-machine mount swarm1:/home/docker/html mnt/html のところで、以下のようなエラーになり実行できません。 You must have a copy of the sshfs binary locally to use the mount feature なにかヒントがあればよろしくお願い致します。

サカキ

管理人です。返信が遅れてしまいすみません。けろっぴさんはおそらくWindowsで実行されていると思うのですが、WindowsではDocker Swarmを動かすにあたって不都合が多いのだと思います。ネットでけろっぴさんが遭遇したエラー文を検索するといくつか解決策のような書き込みが見つかりましたが、UbuntuなどのLinuxで試すというのが全体的に成功率が高くておススメです(DockerはもともとLinux向けに作られたものなので、Linuxと相性が良いのだと思います)。