Docker Swarm モードはわかりやすい!
最近はDockerの勉強をしています。
以下の本(おすすめです!)の内容に沿う形で、Docker Swarm モード の動きを試してみたいと思います。
Swarm モードってなんじゃい? という方は、こちらのドキュメントを読んでくださるのが早いと思います。
Docker Swarm 概要 — Docker-docs-ja 17.06.Beta ドキュメント
かんたんに言うなら、複数のホスト(サーバー)で起動しているDockerを、
1つの司令塔となるサーバーから指示を出すことにより管理できる仕組みです。
1. VirtualBox を利用して複数ホストの構築
最初、「サーバー複数立てるとなるとお金が……」って思いましたが、
本の中で VirtualBox に複数ホストを立てる方法が案内されていました。
なるほど、それならゼロ円!
参考記事 : Docker Machine を使って VirtualBox に Dockerホストを立てる
docker-machine create -d virtualbox <machine-name>
のコマンドで、
Docker がインストール済みの Linux イメージを持つ仮想マシンが、 Virtual Box 上に作られるそうです。便利!
しかし、やってみると boot2docker.iso のダウンロードがなぜかなかなか終わらず進めなかったので、
あきらめて https://github.com/boot2docker/boot2docker/releases から最新版をダウンロードしてきて
~/.docker/machine/cache
ディレクトリ下に置き、再度コマンドを実行しました。
docker-machine create -d virtualbox manager1 docker-machine create -d virtualbox worker1 docker-machine create -d virtualbox worker2
に成功した後、 docker-machine ls
で
docker-machine ls NAME ACTIVE DRIVER STATE URL SWARM DOCKER ERRORS manager1 - virtualbox Running tcp://192.168.99.100:2376 v18.06.1-ce worker1 - virtualbox Running tcp://192.168.99.101:2376 v18.06.1-ce worker2 - virtualbox Running tcp://192.168.99.102:2376 v18.06.1-ce
それぞれのホストのプライベートIPアドレスが確認できますね。
2. manager1 を Swarm マネージャーに
それぞれのホストにて、Docker の Swarm モードを有効にするため、 swarm init をやりに行きます。
docker-machine ssh manager1
でSSHログインした後、
docker@manager1:~$ docker swarm init Error response from daemon: could not choose an IP address to advertise since this system has multiple addresses on different interfaces (12.34.56.78 on eth0 and 192.168.99.100 on eth1) - specify one with --advertise-addr
単純に docker swarm init
をおこなうとエラーが出てしまいました。
ifconfig
コマンドで確認するとわかるのですが、
このマシンには複数のネットワークインターフェイスが接続されており、
どのIPアドレスを外部との通信に使うのか確定ができないのです。
それを解決するため、 advertise-addr オプションを使います。
プライベートIPアドレスでホスト間通信をおこなってほしいので、
さっき docker-machine ls
で見たこのマシンのプライベートIPアドレス 192.168.99.100
をここでは指定します。
docker@manager1:~$ docker swarm init --advertise-addr 192.168.99.100 Swarm initialized: current node (hkixhjon23s2xd1pvx57csa7q) is now a manager. To add a worker to this swarm, run the following command: docker swarm join --token SWMTKN-1-57m3j6npg9ip54992xdo8pg9pt2yekmh3io92nji2nhysa5b6d-74ml40yfeahd2htj6rpk2hbx2 192.168.99.100:2377 To add a manager to this swarm, run 'docker swarm join-token manager' and follow the instructions.
これで Swarm モードが有効になりました。
docker@manager1:~$ docker node ls
ID HOSTNAME STATUS AVAILABILITY MANAGER STATUS ENGINE VERSION
xxkdyatgiikov50oxy0qh1ir9 * manager1 Ready Active Leader 18.06.1-ce
なお、逆に Swarm モードを無効にしたい場合は、 swarm leave をおこないます。
現在はマネージャーが 1 ノードのみですので、 force オプションを使う必要があります。
docker@manager1:~$ docker swarm leave --force
Node left the swarm.
docker node ls
は、 Swarm マネージャーがクラスタ一覧を見るためのコマンドですので、
このホストが Swarm モードを無効にしマネージャーでもなくなった今、以下のようにエラーが出るようになります。
docker@manager1:~$ docker node ls Error response from daemon: This node is not a swarm manager. Use "docker swarm init" or "docker swarm join" to connect this node to swarm and try again.
もう一度 Swarm モードを有効にするため docker swarm init --advertise-addr 192.168.99.100
をしておきます。
3. worker1, worker2 でも swarm init
worker1 および worker2 でも Swarm モードを有効にしたいのですが、
先ほどと同じようなコマンドをすると、新たな独立した Swarm マネージャーが出来てしまうだけです。
今回はもうマネージャーは必要ないので、別のコマンドを用います。
manager1 にて docker swarm join-token worker
コマンドを使います。
docker@manager1:~$ docker swarm join-token worker To add a worker to this swarm, run the following command: docker swarm join --token SWMTKN-1-57m3j6npg9ip54992xdo8pg9pt2yekmh3io92nji2nhysa5b6d-74ml40yfeahd2htj6rpk2hbx2 192.168.99.100:2377
最終行にコマンドがありますね。これを使います。
まずはターミナルの別タブを開いて worker1 へログイン。
docker-machine ssh worker1
さっきのコマンドを使ってみます。
docker@worker1:~$ docker swarm join --token SWMTKN-1-57m3j6npg9ip54992xdo8pg9pt2yekmh3io92nji2nhysa5b6d-74ml40yfeahd2htj6rpk2hbx2 192.168.99.100:2377 This node joined a swarm as a worker.
無事に Swarm worker として加わってくれたようです。同じことを worker2 にもおこないます。
4. manager が docker node ls で確認
3. の手順が終わったら、 Swarm manager である manager1 が、
Swarm worker である worker1, worker2 を監視できる状態になっているかを確認します。
manager1 で docker node ls
をおこないます。
docker@manager1:~$ docker node ls
ID HOSTNAME STATUS AVAILABILITY MANAGER STATUS ENGINE VERSION
xxkdyatgiikov50oxy0qh1ir9 * manager1 Ready Active Leader 18.06.1-ce
ra1nagb73mbusbpyxralb4fzj worker1 Ready Active 18.06.1-ce
ics08k65tcmwj1soe8y8c4887 worker2 Ready Active 18.06.1-ce
逆に worker1 で docker node ls
をおこなっても、 Swarm manager でないのでエラーが出るだけで終わります。
docker@worker1:~$ docker node ls Error response from daemon: This node is not a swarm manager. Worker nodes can't be used to view or modify cluster state. Please run this command on a manager node or promote the current node to a manager.
Swarm は日本語で「群れ」の意味です。
その群れに参加している manager1, worker1, worker2 はそれぞれ Node と呼びます。日本語で「ひとつの点」を意味します。
Swarm manager となる Node を manager node、
Swarm worker となる Node を worker node と言うので覚えておくと有利です。
docker node ls
コマンドは Node をすべて表示するコマンドだと理解すると覚えやすいでしょう。
5. Docker image を展開する
manager, worker それぞれの準備が整ったので Docker image を展開させてみましょう。
以下の2記事を参考にしました、ありがとうございます。
Docker Hub に存在する nginx のイメージを今回は利用します。
docker@manager1:~$ docker service create --name nginx_sample --replicas 2 --publish 8080:80 nginx:1.14 om5gg86083bfiwsi88ugcenm8 overall progress: 2 out of 2 tasks 1/2: running [==================================================>] 2/2: running [==================================================>] verify: Service converged
どこのホストに作成されたか確認します。
docker@manager1:~$ docker service ps nginx_sample ID NAME IMAGE NODE DESIRED STATE CURRENT STATE ERROR PORTS 0afwga4oks2q nginx_sample.1 nginx:1.14 worker1 Running Running 44 seconds ago dcs7l9e3cdxq nginx_sample.2 nginx:1.14 manager1 Running Running 44 seconds ago
worker1, manager1 に作成されていました。
worker に優先的に作られると思っていたので、これは意外な結果でした。
6. Swarm Load Balancer
さて、nginx が動いていると思いますので、Webブラウザで確認してみましょう。
1. の手順で docker-machine ls
をして見たプライベートIPアドレスを用います。
今回は8080番ポートで動かしてるので、 192.168.99.100:8080 にアクセスしてみます。
良いですね。
192.168.99.101:8080 , 192.168.99.102:8080 にも同様にアクセスできます。
worker2 では動いていないはずなのに 192.168.99.102:8080 でアクセスできるのは不思議ですが、
これは routing mesh のおかげです。
Swarmモードにした全てのノードは、リクエストがあると
現在 Active であるノードに、その要求を横流しし処理してもらいます。
いわば Swarm Node 間の Load Balancer です。
この図を見ると「内部で網目状(=mesh)につながるから routing mesh と呼ぶのか〜」とわかりやすいですね。
7. コンテナを増やす
さて、せっかくサーバーが3つあるのにDockerプロセスを2つのサーバーだけで動かしているようではもったいありません。
コンテナ数を増やしてみましょう。 docker service scale
を使います。
縁起良く 7 つにしてみます。
docker@manager1:~$ docker service scale nginx_sample=7 nginx_sample scaled to 7 overall progress: 7 out of 7 tasks 1/7: running [==================================================>] 2/7: running [==================================================>] 3/7: running [==================================================>] 4/7: running [==================================================>] 5/7: running [==================================================>] 6/7: running [==================================================>] 7/7: running [==================================================>] verify: Service converged
どのホストで動いてるか確認します。
docker@manager1:~$ docker service ps nginx_sample ID NAME IMAGE NODE DESIRED STATE CURRENT STATE ERROR PORTS 0afwga4oks2q nginx_sample.1 nginx:1.14 worker1 Running Running 44 minutes ago dcs7l9e3cdxq nginx_sample.2 nginx:1.14 manager1 Running Running 44 minutes ago tpxmli6n5xcy nginx_sample.3 nginx:1.14 worker1 Running Running 8 seconds ago 01x8uej3mabt nginx_sample.4 nginx:1.14 manager1 Running Running 8 seconds ago pxn03gq3d2hu nginx_sample.5 nginx:1.14 worker2 Running Running 7 seconds ago 25kpj1yw9gsz nginx_sample.6 nginx:1.14 worker2 Running Running 7 seconds ago bz5648esiqg7 nginx_sample.7 nginx:1.14 worker2 Running Running 7 seconds ago
manager1 に2つ, worker1に2つ, worker2に3つ, おおむね均等になるように割り振られてますね。
8. プロセスを殺してみる
試しに worker1 での nginx を殺してみます。
docker@worker1:~$ ps ax | grep nginx 12902 ? Ss 0:00 nginx: master process nginx -g daemon off; 13031 ? S 0:00 nginx: worker process 13285 ? Ss 0:00 nginx: master process nginx -g daemon off; 13363 ? S 0:00 nginx: worker process 13619 pts/0 S+ 0:00 grep nginx docker@worker1:~$ sudo killall nginx docker@worker1:~$ ps ax | grep nginx 13985 pts/0 S+ 0:00 grep nginx
10秒程度待ってもう一度見ると、
docker@worker1:~$ ps ax | grep nginx 14006 ? Ss 0:00 nginx: master process nginx -g daemon off; 14069 ? Ss 0:00 nginx: master process nginx -g daemon off; 14143 ? S 0:00 nginx: worker process 14188 ? S 0:00 nginx: worker process 14274 pts/0 S+ 0:00 grep nginx
勝手に復活していました。
manager1 で docker service ps nginx_sample
コマンドから確認しましょう。
docker@manager1:~$ docker service ps nginx_sample ID NAME IMAGE NODE DESIRED STATE CURRENT STATE ERROR PORTS tfb84ly07nvb nginx_sample.1 nginx:1.14 worker1 Running Running 10 seconds ago 17slmexy1lry \_ nginx_sample.1 nginx:1.14 worker1 Shutdown Complete 16 seconds ago 〜中略〜 8eq2cnxa2mcz nginx_sample.3 nginx:1.14 worker1 Running Running 10 seconds ago v80g7g7jq6k3 \_ nginx_sample.3 nginx:1.14 worker1 Shutdown Complete 16 seconds ago 〜以下略〜
manager が、heartbeat の機能で worker1 でタスクの終了を確認したので、
期待状態(DESIRED STATE)を維持するために、worker1 に対し新しいタスクの開始を依頼しているのです。
9. nginx のバージョンを上げる
manager node で docker service update を用いて、nginx_sample service のアップデートをおこないたいと思います。
今回は、イメージを nginx:1.14 から nginx:1.15 にアップデートしたいと思います。
docker@manager1:~$ docker service update --image nginx:1.15 nginx_sample nginx_sample overall progress: 7 out of 7 tasks 1/7: running [==================================================>] 2/7: running [==================================================>] 3/7: running [==================================================>] 4/7: running [==================================================>] 5/7: running [==================================================>] 6/7: running [==================================================>] 7/7: running [==================================================>] verify: Service converged
このアップデート中も、 192.168.99.100:8080 などへのアクセスは変わらず可能です。
6. で説明したロードバランサーが上手く機能してくれているおかげです。
- タスクの停止
- アップデート
- タスクの再開
- タスクの開始が確認できるのであれば、少し時間を置いたのち、次のコンテナに対しこの一連の流れをおこなう
という流れでアップデート作業をしているようです。
この挙動(rolling update)であるなら、一定の時間はバージョンの混同が発生すると思いますので、
その点は留意が必要です。
10. service update を戻す
「あ、やべ、バージョンアップしたら挙動おかしくなったわ」という場合に1世代前に戻すことが出来ます。 docker service rollback です。
docker@manager1:~$ docker service rollback nginx_sample nginx_sample rollback: manually requested rollback overall progress: rolling back update: 7 out of 7 tasks 1/7: running [> ] 2/7: running [> ] 3/7: running [> ] 4/7: running [> ] 5/7: running [> ] 6/7: running [> ] 7/7: running [> ] verify: Service converged
11. 使うポート番号を変えたい場合
8080 を指定していましたが、「やっぱり普通に 80 番ポートを使うようにしようかな〜」というときも docker service update で対応可能です。
publish-add オプションや publish-rm オプションを使います。
docker@manager1:~$ docker service update --publish-rm 8080:80 --publish-add 80:80 nginx_sample nginx_sample overall progress: 7 out of 7 tasks 1/7: running [==================================================>] 2/7: running [==================================================>] 3/7: running [==================================================>] 4/7: running [==================================================>] 5/7: running [==================================================>] 6/7: running [==================================================>] 7/7: running [==================================================>] verify: Service converged
すべてが running になった段階で、
8080 番ポートへのアクセスはできなくなり、80 番ポートへのアクセスができるようになります。
192.168.99.100:8080 でなく、 192.168.99.100 (もしくは 192.168.99.100:80 )でアクセスできるようになっていることでしょう。
12. service を終了させる
ひととおり実験したので service を終了させます。 docker service rm でおこなえます。
docker@manager1:~$ docker service rm nginx_sample
nginx_sample
びっくりするほど一瞬で終わり、もう 192.168.99.100 などにはアクセスできなくなっています。
ついでに、VirtualBox を3台動かし続けているのもメモリの無駄遣いなので、
manager1 からログアウトしたら、以下のコマンドで仮想マシンを停止させちゃいましょう。
docker-machine stop manager1 worker1 worker2 Stopping "worker2"... Stopping "worker1"... Stopping "manager1"... Machine "worker2" was stopped. Machine "worker1" was stopped. Machine "manager1" was stopped.
スッキリですね。
13. Docker Swarm vs. Kubernetes ?
Docker Swarm が非常に手軽に、死活監視および自動再起動や、無停止更新をおこなってくれることを確認しました。
では、 Kubernetes より優れているのでしょうか?
Docker Swarm を推す記事もあれば、
Kubernetes を推す記事もあります。
Kubernetes は Docker で正式サポートされ、
AWSでも EKS こと Amazon Elastic Container Service for Kubernetes が登場したくらいですから、
Kubernetes がますますデファクトスタンダードになっていくでしょう。
ですので、業務で使うなら、長い目で考えて Kubernetes の方が良さそうです。
一方、特に新たな設定も必要なく、dockerコマンドだけで全てを終わらせることが出来る Docker Swarm は直感的でわかりやすく学習コストが大変少なく魅力的です。
個人プロジェクトなどでDocker管理をおこなう場合には、十分に選択肢のひとつとなり得るかと思います。
詳細な比較は「Docker Swarm vs. Kubernetes」で検索するとたくさん見つかるかと思います。
だいたいどれも、「簡単だけどカスタマイズ性の低いDocker Swarm、学習曲線が急だがカスタマイズ性が高く多機能なKubernetes」という結論に落ち着いています。