[2020/04/20追記] MACアドレスを一部マスクしました。
Dockerは最もポピュラーなコンテナープラットフォームで、多くのユーザーに使われていると思います。 Dockerはアプリケーションの実行を迅速に行えるのがメリットです。万が一サービスに支障があった場合もDockerが正常に動くホストを用意して数行コマンドを実行するだけでアプリケーションを復旧可能な点がメリットです。
Docker 1.12以降のバージョンでは、Docker Swarm modeという機能が実装されるようになりました。おそらく現在Dockerを使っているユーザーはそれよりも新しいバージョンのDockerを使っていると思います。Docker Swarm modeを使えば簡単にコンテナークラスターを作ることができます。
本ブログでは主にKubernetesをよく取り上げていますが、今回はDocker Swarm mode(以下Swarm mode)の基本的な使い方についても紹介したいと思います。
以下はUbuntu Server 18.04.4 LTSで利用する流れを想定しています。ちなみに間も無くリリース予定のUbuntu 20.04 LTSでもDockerパッケージが用意されているので、今後もしばらくはDockerを使えそうで安心してます。
Swarm modeを使ったコンテナクラスターのセットアップ
Dockerのインストール
UbuntuにDockerをインストールします。バージョン19.03をインストールできます。
$ sudo apt update && sudo apt install -y docker.io && sudo systemctl enable --now docker
マネージャーノード作成
Swarm modeのマネージャーノードをセットアップします。listen-addrはそのノードのIPアドレスを指定します。 正常に実行できると、2台目以降のノードをクラスターに参加させるためのコマンドが表示されます。
$ sudo docker swarm init --listen-addr 172.17.28.99:2377
2台目以降のノード
2台目以降は docker swarm join
コマンドを実行します。
先に実行した際に表示された docker swarm join
コマンドを実行します。後から確認する場合は sudo docker swarm join-token (worker or manager)
コマンドを実行してトークンキーを含んだコマンドを確認します。
今回は残りのノードは全てWorkerにするので、2台目以降のノードで次のように実行します。トークンキーとIPアドレスについては実際の物に置き換えて実行してください。
$ sudo docker swarm join --token SWMTKN-1-4lg6fg368g9td1ysvdnmvmo27a9quekdl9juaukob97jpzn2q3-djifh3mqpg6rlknwienodbsrz 172.17.28.99:2377
ノード一覧を確認
クラスターにノードを追加し終わると、 sudo docker node ls
コマンドでノードの一覧が表示されます。
$ sudo docker node ls ID HOSTNAME STATUS AVAILABILITY MANAGER STATUS ENGINE VERSION 8r9erj75bvd1j88x9so24yfdt * docker1 Ready Active Leader 19.03.6 zqiptycwmkaqvxko08duxfp2o docker2 Ready Active 19.03.6 i5pzjf56cdk7er0mj3ar5gbv8 docker3 Ready Active 19.03.6 utrg0hx6robagpj1f4e0oxupb docker4 Ready Active 19.03.6
コンテナアプリを展開してみる
Dockerでアプリを展開するにはコマンドとオプション、パラメーターを使って行うか、Docker Composeを使ってYAMLファイルにデプロイしたいアプリケーションとパラメーターを記述して docker-compose up -d
コマンドなどでアプリケーションを展開しました。
Docker Composeは非常に便利なのですが、Swarm modeでもYAMLファイルに書いた内容でアプリケーションを展開できます。
プロジェクトディレクトリを作成
まずはプロジェクトディレクトリーを作成します。ここはDocker Composeを使う時と同じです。
$ mkdir demo $ cd demo
Compose YAMLを作成
まずは永続データという概念を忘れて、次のようなシンプルなYAMLファイルを書いてみます。
$ vi docker-compose.yml version: '3' services: nginx: image: nginx:alpine deploy: replicas: 2 ports: - 51080:80
アプリをデプロイ
docker stack deploy
コマンドを使ってアプリを展開します。
$ sudo docker stack deploy -c docker-compose.yml nginx
実行すると、ネットワークとサービスが作成されて、次のように標準出力されます。
Creating network nginx_default Creating service nginx_nginx
Stackの確認
コンテナーが作られたことが確認できます。
$ sudo docker stack ls NAME SERVICES ORCHESTRATOR nginx 1 Swarm $ sudo docker stack ps nginx ID NAME IMAGE NODE DESIRED STATE CURRENT STATE ERROR PORTS icjdfqwrkicr nginx_nginx.1 nginx:alpine docker2 Running Running 13 seconds ago netw17314xy6 nginx_nginx.2 nginx:alpine docker1 Running Running 13 seconds ago
Serviceの確認
サービスが作られたことがわかります。
$ sudo docker service ls ID NAME MODE REPLICAS IMAGE PORTS mfszzmpnyzqv nginx_nginx replicated 2/2 nginx:alpine *:51080->80/tcp $ sudo docker service ps nginx_nginx ID NAME IMAGE NODE DESIRED STATE CURRENT STATE ERROR PORTS icjdfqwrkicr nginx_nginx.1 nginx:alpine docker2 Running Running about a minute ago netw17314xy6 nginx_nginx.2 nginx:alpine docker1 Running Running about a minute ago
コンテナネットワークの確認
コンテナネットワークも確認してみましょう。
$ sudo docker network inspect nginx_default [ { "Name": "nginx_default", "Id": "wg8nyp0mcbjyoezn54yqhcb51", "Created": "2020-04-20T00:50:23.009084615Z", "Scope": "swarm", "Driver": "overlay", "EnableIPv6": false, "IPAM": { "Driver": "default", "Options": null, "Config": [ { "Subnet": "10.0.1.0/24", "Gateway": "10.0.1.1" } ] }, "Internal": false, "Attachable": false, "Ingress": false, "ConfigFrom": { "Network": "" }, "ConfigOnly": false, "Containers": { "19d71f6c4a7658fe7e20ae4cb60b6547c90c7743473cce78e0ba49fa14ad47c1": { "Name": "nginx_nginx.2.netw17314xy6hv8i5anh921lm", "EndpointID": "148110e6237f3f8b41362432af554a47e5aae19dcac15be268446b66eac3b128", "MacAddress": "02:42:0a:00:xx:zz", "IPv4Address": "10.0.1.4/24", "IPv6Address": "" }, "lb-nginx_default": { "Name": "nginx_default-endpoint", "EndpointID": "4bdd32c9a63429bf94543d3236ea09920443f453124808846c0e4ccfc60a58e8", "MacAddress": "02:42:0a:00:xx:zz", "IPv4Address": "10.0.1.6/24", "IPv6Address": "" } }, "Options": { "com.docker.network.driver.overlay.vxlanid_list": "4097" }, "Labels": { "com.docker.stack.namespace": "nginx" }, "Peers": [ { "Name": "70d40231e046", "IP": "172.17.28.99" }, { "Name": "9370d2b12ac9", "IP": "172.17.28.96" } ] } ]
Ingressの確認
Ingressの確認は次のように行います。Docker Swarm modeで複数のアプリケーションを実行する場合はこのIngressを使ってコンテナーアプリケーションにアクセスできます。
$ sudo docker network inspect ingress [ { "Name": "ingress", "Id": "kpgo1irlexh4hbewuy7ag8f5e", "Created": "2020-04-20T00:42:23.29114384Z", "Scope": "swarm", "Driver": "overlay", "EnableIPv6": false, "IPAM": { "Driver": "default", "Options": null, "Config": [ { "Subnet": "10.0.0.0/24", "Gateway": "10.0.0.1" } ] }, "Internal": false, "Attachable": false, "Ingress": true, "ConfigFrom": { "Network": "" }, "ConfigOnly": false, "Containers": { "19d71f6c4a7658fe7e20ae4cb60b6547c90c7743473cce78e0ba49fa14ad47c1": { "Name": "nginx_nginx.2.netw17314xy6hv8i5anh921lm", "EndpointID": "668e92dd068329b60fc9b6df6d174578209af3823893f19237077542400e207c", "MacAddress": "02:42:0a:00:xx:zz", "IPv4Address": "10.0.0.8/24", "IPv6Address": "" }, "ingress-sbox": { "Name": "ingress-endpoint", "EndpointID": "5608bfec46fdd5d0b138673c02499440ffe56fead5951a0b2e99adb29d0ea5ea", "MacAddress": "02:42:0a:00:xx:zz", "IPv4Address": "10.0.0.2/24", "IPv6Address": "" } }, "Options": { "com.docker.network.driver.overlay.vxlanid_list": "4096" }, "Labels": {}, "Peers": [ { "Name": "70d40231e046", "IP": "172.17.28.99" }, { "Name": "9370d2b12ac9", "IP": "172.17.28.96" }, { "Name": "e06b209d80ed", "IP": "172.17.28.102" }, { "Name": "9cf0109b5005", "IP": "172.17.28.186" } ] } ]
ブラウザでアクセスしてみる
マネージャーノードに指定したポート宛でアクセスしてみましょう。NGINXのデフォルトページが表示されるはずです。
永続データを考慮してみる
永続したデータの置き場としてDocker Volumeを利用する方法で説明します。 より詳しく知りたい場合は以下のドキュメント(Use Volumes)を確認してください。
- https://docs.docker.com/compose/compose-file/#volume-configuration-reference
- https://docs.docker.com/storage/volumes/
Volumeの作成
全てのノードで以下を実行し、Dockerボリュームを作成します。今回はLocalドライバーを用いていますが、Dockerには永続データの持たせ方としていくつかの方法がありますので、前のリンク先の情報で確認してください。
$ sudo docker volume create web-data
以下コマンドを実行して、ボリュームが作成できているか確認します。「web-data」という名前のボリュームが作られていることを確認します。
$ sudo docker volume ls DRIVER VOLUME NAME local web-data
永続データを考慮したCompose YAMLを書いてみる
前半の記述は前のものと一緒ですが、volumesの項目が少々増えているのがわかると思います。次例はLocalドライバーベースの「web-data」という名前のDocker Volumeをコンテナーのディレクトリー「/usr/share/nginx/html」にマウントするように記述しています。
ちなみに今回は一般に配布されているDockerイメージを使っていますが、本来はアプリケーションやHTMLなどのコンテンツを含めたカスタムDockerイメージを作った上でCompose YAMLにそのイメージを指定する必要があります。
version: '3' services: nginx: image: nginx:alpine deploy: replicas: 2 volumes: - web-data:/usr/share/nginx/html ports: - 52080:80 volumes: web-data: driver: local
アプリケーションの展開
YAMLファイルを準備できたら、前半同様にコマンドを実行して展開してみましょう。アプリケーション名は変更しておきます。
$ sudo docker stack deploy -c docker-compose.yml nginx-vol Creating network nginx-vol_default Creating service nginx-vol_nginx $ sudo docker service ls ID NAME MODE REPLICAS IMAGE PORTS v5va53z7fc35 nginx-vol_nginx replicated 2/2 nginx:alpine *:52080->80/tcp
同様に、サービスポートにアクセスすると、NGINXのデフォルトページが表示されます。
アプリケーションを編集してみる
デフォルトのページをみていてもしょうがないので、ちょっと編集してみましょう。 まずはどこのDockerホストにアプリケーションコンテナーが展開されたか確認します。
docker3とdocker4に展開されたようです。
$ sudo docker service ps nginx-vol_nginx ID NAME IMAGE NODE DESIRED STATE CURRENT STATE ERROR PORTS 7ytywp67l9py nginx-vol_nginx.1 nginx:alpine docker3 Running Running 8 minutes ago cqjfqxw9sb4j nginx-vol_nginx.2 nginx:alpine docker4 Running Running 8 minutes ago
sudo docker container ls
コマンドで正確なコンテナー名を確認して、次のようにコンテナーのシェルに入り、コンテンツを書き換えてみます。
ubuntu@docker3:~$ sudo docker container exec -it nginx-vol_nginx.1.7ytywp67l9pysgn5amtaljste sh / # echo "This site: nginx-vol_nginx.1.7ytywp67l9pysgn5amtaljste" > /usr/share/nginx/html/index.html ubuntu@docker4:~$ sudo docker container exec -it nginx-vol_nginx.2.cqjfqxw9sb4jcv9hur6twc515 sh / # echo "This site: nginx-vol_nginx.2.cqjfqxw9sb4jcv9hur6twc515" > /usr/share/nginx/html/index.html
ブラウザーでアクセスしてみます。すると次のように表示されるはずです。ブラウザーのキャッシュが存在するはずなので、確認の際はスーパーリロードを実行してください。
以上です。 Swarm modeの使い方についてはいろいろ情報があったのですが、適当なコンテナーアプリケーションをYAMLファイルを使ってデプロイして、ブラウザーでアクセスしてみる流れがまとまっている情報が少なかったので調べてその結果をブログにまとめてみました。誰かのお役に立てば幸いです。