この記事では、Dockerは触ったことあるけど、k8sみたいなオーケストレーションツールは使ったことないよって人向けに、Dockerのswarm modeでマルチホストなクラスタを構築し、いくつかの代表的な機能(スケールアウトとかローリングアップデートとか)を使うところまでをハンズオン形式で紹介します。
オーケストレーションツール触ってみたいけど、k8sはチョット敷居が高い・・・って人向けです。swarm mode
、お手軽でいいよ
今回試す環境は以下のとおりです。
環境
- Ubuntu 18.04 LTS 3台
- Docker version 18.09.7
- docker-compose version 1.24.0
Docker swarm modeとは
Dockerのver 1.12以降で組み込まれたオーケストレーションツールです。
これを用いることで、Dockerの機能に加えて、ざっくり以下のようなことができるようになります。
- 複数ホスト間でのクラスタの構築
- コンテナレベルでのスケールアウト
- ローリングアップデートを用いた無停止デプロイ
- コンテナレベルでの迅速なロールバック
また、Swarmはdocker-composeとの相性が非常に良く、上記のような機能をdocker-compose.yml
ファイルベースで定義したコンテナ群に対して、一括で適用することができます。
(docker-composeのversion 3以降では、deployというswarm mode専用の記法が用意されているぐらいです)
これが非常に強力で、普段からdocker-composeを使っている人であれば既存のワークフローを壊すことなく、簡単にクラスタ環境を手に入れることができます。
コンテナオーケストレーションツールとしては、ココ最近はkubernetesが一強になってしまった感はありますが、Docker swarm modeは、CE版のDockerにデフォルトで組み込まれているので、Dockerをインストールするだけで簡単に使い始めることができます。
クラスタの構築もコマンドを数行打つだけの簡単なものなので、コンテナオーケストレーションとはなんぞや?というのを理解する入り口として、とりあえず使って見る分にはとても良いのではないでしょうか。
ちなみにEnterPrise版のDockerでは、kubernetesがswarmと同じようにDockerに組み込まれたようなので、そちらが使える環境であればそちらを使ってみても良いかもしれません。
今回は公式のswarmチュートリアルをベースに、わかりやすいよう多少内容を変えながらハンズオンを行っていきたいと思います。
詳しい人はそちらを見たほうがわかりやすいかもしれません。
環境準備
さて、今回は3台のLinuxサーバー(Ubuntu 18.04LTS)を使ってクラスタを組んでいきたいと思います。
なのでまずは、3台のLinuxマシンを用意しなければいけません。
AWSやAzureで仮想マシンを借りても良いのですが、今回はお試しなのでローカル環境にVirtualboxとVagrantを使って、3台の仮想マシンを立てちゃいたいと思います。
構成は以下のような感じです
なお、今回仮想マシンを立てるホストマシンはmacです。Windows環境での動作は検証していないのでご了承ください。。まあ結局の所、仮想マシンを3台建てられさえすれば良いので、方法は何でも良いと思います。
1. Virtual boxとVagrantのインストール
公式サイトからそれぞれインストーラーを落としてきてインストールします
2. 仮想マシンの作成
インストールが終わったら、仮想マシンを作成します。まず、Vagrant用のディレクトリを仮想マシンの台数分作ります。ここではnode1``node2``node3
と名付けることにします
.
├── node1
│ └── data // VMとホストの共有フォルダ docker-compose.ymlなどの構築資材を置く
├── node2
│ └── data
└── node3
└── data
それぞれのnodeディレクトリの直下に移動し、以下のコマンドを実行します。
$ cd ./node1
$ vagrant init ubuntu/bionic64
すると以下のようなVagrantfile
が作成されると思うので、それぞれのnodeにIPアドレスを割り振るために以下のように書き換えます。IPアドレスはnodeごとに別のものを設定するようにしてください
# -*- mode: ruby -*-
# vi: set ft=ruby :
# All Vagrant configuration is done below. The "2" in Vagrant.configure
# configures the configuration version (we support older styles for
# backwards compatibility). Please don't change it unless you know what
# you're doing.
Vagrant.configure("2") do |config|
# The most common configuration options are documented and commented below.
# For a complete reference, please see the online documentation at
# https://docs.vagrantup.com.
# Every Vagrant development environment requires a box. You can search for
# boxes at https://vagrantcloud.com/search.
config.vm.box = "ubuntu/bionic64"
# Disable automatic box update checking. If you disable this, then
# boxes will only be checked for updates when the user runs
# `vagrant box outdated`. This is not recommended.
# config.vm.box_check_update = false
# Create a forwarded port mapping which allows access to a specific port
# within the machine from a port on the host machine. In the example below,
# accessing "localhost:8080" will access port 80 on the guest machine.
# NOTE: This will enable public access to the opened port
# config.vm.network "forwarded_port", guest: 80, host: 8080
# Create a forwarded port mapping which allows access to a specific port
# within the machine from a port on the host machine and only allow access
# via 127.0.0.1 to disable public access
# config.vm.network "forwarded_port", guest: 80, host: 8080, host_ip: "127.0.0.1"
# Create a private network, which allows host-only access to the machine
# using a specific IP.
config.vm.network "private_network", ip: "192.168.33.10" ## ここのコメントアウトを外し、一意なipアドレスを各nodeに割り振る
# Create a public network, which generally matched to bridged network.
# Bridged networks make the machine appear as another physical device on
# your network.
# config.vm.network "public_network"
# Share an additional folder to the guest VM. The first argument is
# the path on the host to the actual folder. The second argument is
# the path on the guest to mount the folder. And the optional third
# argument is a set of non-required options.
# dataディレクトリをVMにマウントする
config.vm.synced_folder "./data", "/home/vagrant/vagrant_data"
# Provider-specific configuration so you can fine-tune various
# backing providers for Vagrant. These expose provider-specific options.
# Example for VirtualBox:
#
# config.vm.provider "virtualbox" do |vb|
# # Display the VirtualBox GUI when booting the machine
# vb.gui = true
#
# # Customize the amount of memory on the VM:
# vb.memory = "1024"
# end
#
# View the documentation for the provider you are using for more
# information on available options.
# Enable provisioning with a shell script. Additional provisioners such as
# Puppet, Chef, Ansible, Salt, and Docker are also available. Please see the
# documentation for more information about their specific syntax and use.
# config.vm.provision "shell", inline: <<-SHELL
# apt-get update
# apt-get install -y apache2
# SHELL
end
最後に以下を実行することで仮想マシンの作成と立ち上げを行います。
$ vagrant up
立ち上げた仮想マシンには以下のコマンドでssh接続することができます
$ vagrant ssh
サーバーに接続できたら、わかりやすいように各Nodeサーバーのhostnameをnode1
node2
node3
に変更しておきましょう
$ sudo hostnamectl set-hostname ubuntu-node1 // node1の場合
$ hostname
ubuntu-node1 // 変わったことを確認
以上の操作を3つのNode全てに行ってください。最終的なディレクトリ構成は以下のような感じになると思います。
.
├── node1
│ ├── data
│ ├── Vagrantfile
│ └── ubuntu-bionic-18.04-cloudimg-console.log
├── node2
│ ├── data
│ ├── Vagrantfile
│ └── ubuntu-bionic-18.04-cloudimg-console.log
└── node3
├── data
├── Vagrantfile
└── ubuntu-bionic-18.04-cloudimg-console.log
DockerとDockerComposeのインストール
swarmを動作させるために各NodeにDockerとdocker-composeをインストールします。
docker-composeは入れなくても使えるのですが、Dockerのswarm modeではdocker-composeのyaml記法で定義されたコンテナ群をまとめてデプロイできるので、あったほうがいろいろ便利です。
Dockerのインストール
基本的には公式サイトに従ってインストールするのが一番間違いがないです。一応現時点(2019/07)でのインストール方法をメモしておきます
- インストールされている古いDockerの削除
$ sudo apt-get remove docker docker-engine docker.io containerd runc
- Dockerのインストール
$ sudo apt-get update
$ sudo apt-get install \
apt-transport-https \
ca-certificates \
curl \
gnupg-agent \
software-properties-common
$ curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo apt-key add -
$ sudo apt-key fingerprint 0EBFCD88
$ sudo add-apt-repository \
"deb [arch=amd64] https://download.docker.com/linux/ubuntu \
$(lsb_release -cs) \
stable"
$ sudo apt-get update
$ sudo apt-get install docker-ce docker-ce-cli containerd.io
最後に現在のユーザー(vagrant)をdocker groupに所属させ、docker
コマンドをsudoなしで実行できるようにします。
# dockerグループがなければ作る
$ sudo groupadd docker
# 現行ユーザをdockerグループに所属させる
$ sudo gpasswd -a $USER docker
# dockerをrestert
$ sudo service docker restart
# 一度ログアウトして入り直す
$ exit
$ vagrant ssh
# dockerコマンドが実行できることを確認
$ docker ps -a
Docker composeのインストール
こちらも公式サイトのやり方に従うのが一番良いでしょう。
一応現時点(2019/07)でのインストール方法をメモしておきます。
$ sudo curl -L "https://github.com/docker/compose/releases/download/1.24.0/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose
$ sudo chmod +x /usr/local/bin/docker-compose
$ sudo ln -s /usr/local/bin/docker-compose /usr/bin/docker-compose
上記の作業を3つのNode全てに行います。
Docker Swarmの初期設定とクラスタの構築
まずはNode1をManagerNodeとしてswarmに登録する必要があります。
Node1にsshでログインし、以下のコマンドを実行しましょう。
$ docker swarm init --advertise-addr="192.168.33.10" --listen-addr=0.0.0.0:2377
Swarm initialized: current node (ql8aefbw7rg8nfmznejnzfeus) is now a manager.
To add a worker to this swarm, run the following command:
docker swarm join --token SWMTKN-1-66qe5cyf31fd8rktwvjbrtpd63l32789aqpbfheauwgfh38g8wrf2ey-bob77wfdnrkpcmqtxfgkiu47a 192.168.33.10:2377
To add a manager to this swarm, run 'docker swarm join-token manager' and follow the instructions.
--advertise-addr
には自分自身のIPアドレス。--listen-addr
にはクラスタ間の通信に使用するport番号を指定しましょう。このポートでクラスタ間の通信を行うので、事前にVM上で、各クラスタに対してこのポートを開けておく必要があります。
これでSwarmのクラスタが作成されます。今後は、上記で作ったクラスタに他のNodeをjoinさせることで、クラスタをどんどん広げていくことができます。
クラスタに参加しているnodeの一覧を見るには以下を実行します。
今は自分自身だけですね
$ docker node ls
ID HOSTNAME STATUS AVAILABILITY MANAGER STATUS ENGINE VERSION
ql8aefbw7rg8nfmznejnzfeus * ubuntu-node1 Ready Active Leader 18.09.7
さっそくNode2
とNode3
をworker nodeとしてjoinさせてみましょう。
swarm init時に発行されたtoken
を使ってクラスタにjoinすることができます。
$ docker swarm join --token SWMTKN-1-66qe5cyf31fd8rktwvjbrtpd63l32789aqpbfheauwgfh38g8wrf2ey-bob77wfdnrkpcmqtxfgkiu47a 192.168.33.10:2377
もしもtokenを忘れてしまった場合は以下のコマンドで見ることができます。
$ docker swarm join-token -q worker
もう一度docker node ls
を実行すると、クラスタに参加しているNodeが増えていることがわかります。
$ docker node ls
ID HOSTNAME STATUS AVAILABILITY MANAGER STATUS ENGINE VERSION
ql8aefbw7rg8nfmznejnzfeus * ubuntu-node1 Ready Active Leader 18.09.7
uusz619ly0y6zp3zsdh0br31y ubuntu-node2 Ready Active 18.09.7
pgouhk6cndlxddjhll1vo1rfv ubuntu-node3 Ready Active 18.09.7
これで、manager node1個とworker node2個の計3個のVMでクラスタを組むことができました。
(補足) manager nodeとworker nodeについて
Swarmでは、クラスタを構成するDocker Engineインスタンスのことをノードと呼びます。
今までnode = LinuxVMみたいな書き方をしてきましたが、正確にはLinuxの上で動いているDocker Engineでクラスタを組んでいる。という表現が正しいです。
このクラスタを構成するnodeには、manager nodeとworker nodeの2種類が存在し、それぞれ以下のような役割を担います。
- maneger nodeは一つのリーダー(leader node)を選出し、コンテナのオーケストレーションや管理指示をworker nodeに送信する。デフォルトではmanager nodeもworker nodeとして動作する
- worker nodeはmanager nodeからの指示を受け取り、コンテナをデプロイしたり削除したりする。
詳しくは以下を読んでみてください
Portainerの構築
Swarmの構築には必要ないのですが、コンテナの状態をGUI上から監視できるPortainerというツールがあるのでこれを各Nodeに構築しておきます。 portainerというディレクトリを作り、以下のdocker-compose.yml
を作ります。
.
├── node1
│ ├── Vagrantfile
│ └── data
│ └── portainer
│ └── docker-compose.yml // 追加
├── node2
│ ├── Vagrantfile
│ └── data
│ └── portainer
│ └── docker-compose.yml // 追加
└── node3
├── Vagrantfile
└── data
└── portainer
└── docker-compose.yml // 追加
version: '3'
services:
portainer:
container_name: my-portainer
hostname: my_portainer
image: portainer/portainer
ports:
- "9000:9000"
command: -H unix:///var/run/docker.sock
volumes:
- /var/run/docker.sock:/var/run/docker.sock
- data:/data
restart: always
volumes:
data:
最後に以下のコマンドを実行して、Portainerのコンテナを立ち上げましょう
$ cd ./<Portainerのdocker-composeファイルをおいたディレクトリ>
$ docker-compose up -d
以下のURLにアクセスするとPortainerにログインできます。
http://<NodeのIP>:9000
コンテナのデプロイ
さて、では先ほど作成したクラスタ上でコンテナをデプロイしましょう。コンテナのデプロイはdocker service
コマンドを用いて行う事ができますが、複雑な構成のコンテナをデプロイする場合は、コマンドが長くなってしまって使いにくいです。
swarmはdocker-composeのyaml
形式で定義されたファイルを使ってコンテナのデプロイを行うことができるので、こちらを使うようにしましょう。
まず、Node1の配下に以下のようなdocker-compose.yml
ファイルを用意します
version: '3.7'
services:
nginx:
image: nginx
ports:
- target: 80
published: 8080
protocol: tcp
mode: ingress
.
├── node1
│ ├── Vagrantfile
│ └── data
│ ├── nginx
│ │ └── docker-compose.yml // 追加
│ └── portainer
│ └── docker-compose.yml
├── node2
│ ├── Vagrantfile
│ └── data
│ └── portainer
│ └── docker-compose.yml
└── node3
├── Vagrantfile
└── data
└── portainer
└── docker-compose.yml
Docker composeを使い慣れた人にとっては見慣れた形式のファイルだと思います。
今回は簡単のために空のnginx
コンテナを使って動作確認することにします。
以下のコマンドをNode1上で実行しましょう。
$ cd <docker-compose.ymlを用意した場所>
$ docker stack deploy nginx --compose-file ./docker-compose.yml
以下にアクセスしてコンテナが起動したことを確認します。nginxのデフォルトの画面が表示されれば成功です。
http://<Node1のIP>:8080
(上記の手順に従ってNodeServerを作った場合は http://192.168.33.10:8080 のハズ)
さて今回はingress
モードでコンテナのポートを公開しました。この場合、コンテナが立ち上がっているhost(今回はNode1)以外のNodeからでもこのコンテナにアクセスできます。
以下のURLにも接続してみて、ページが表示されることを確かめてみましょう。
http://<Node2のIP>:8080
http://<Node3のIP>:8080
Portainerを使うと、swarmクラスタ上でどのようにコンテナがデプロイされているかを見ることができます。
サイドバーから「swarm」を選び、「Go to cluster visualizer」のリンクをクリックしてみましょう
今はコンテナをひとつだけデプロイしたので以下のような感じですね
ちなみに今回は、簡単のためにServiceを1種類しか定義しませんでしたが、swarmではdocker-compose.yml
で使える記法はよほど特殊なものでない限りすべてサポートしているので、serviceを複数定義すれば、複数のコンテナを連携させてデプロイすることももちろん可能です。
既存のdocker-compose.yml
ファイルがそのまま使えるのは嬉しいですね
スケールアウト
現在、1個だけコンテナが立ち上がっているわけですが、実際のサービスでは、利用状況によってコンテナをスケールアウトしたくなる場合があります。
その場合は以下のコマンド打つことで簡単スケールアウトすることができます。
まずはスケールアウトしたいService(docker-composeで定義したService)の名前を見つけます
$ docker stack ps nginx
ID NAME IMAGE NODE DESIRED STATE CURRENT STATE ERROR PORTS
p1syqxgwanpy nginx_nginx.1 nginx:latest ubuntu-node1 Running Running 27 minutes ago
docker-composeを使ってコンテナをデプロした場合、各Serviceの名前はcomposefileのあるディレクトリ名_composeで定義したservice名
になります。今回の場合はnginx
ディレクトリの下にdocker-compose.yml
を作っちゃったのでnginx_nginx
ですね。(なんでこの名前にしちゃったんだろ?)
上のコマンドでは.1
が末尾についていますが、これはServiceがスケールアウトして同じコンテナが複数立った場合に見分けるためのものです。なので実際のサービス名はnginx_nginx
ですね
このサービスをスケールアウトするには以下のservice scale
コマンドをNode1で実行します。
$ docker service scale nginx_nginx=5 // コンテナの数を5個にスケールアウトする
nginx_nginx scaled to 5
overall progress: 5 out of 5 tasks
1/5: running
2/5: running
3/5: running
4/5: running
5/5: running
verify: Service converged
Portainerで確認すると、コンテナが5つに増えていることがわかります。
composefileを使ったスケールアウト
上記の方法でもスケールアウトはできるのですが、compose fileに予め、作成するコンテナ数を定義する事もできます。docker-compose.yml
を以下のように書き換えてみましょう
version: '3.7'
services:
nginx:
image: nginx
ports:
- target: 80
published: 8080
protocol: tcp
mode: ingress
deploy:
mode: replicated
replicas: 3 ## コンテナを3つ作成する
docker-composeを書き換えた後、もう一度stack deploy
を実行することで、docker-compose.yml
の差分を自動的に検知して、その状態にコンテナをデプロイし直してくれます。
宣言的で良い感じですね
$ cd <docker-compose.ymlを用意した場所>
$ docker stack deploy nginx --compose-file ./docker-compose.yml
リカバリー
さて、これで同じコンテナを複数個立てて、ロードバランシングすることができました。
じゃあこの状態で、コンテナやNodeを落とすとどうなってしまうのでしょう?
ちょっとやってみたいと思います。
コンテナを削除する
まず削除対象のコンテナを探します
$ docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
7e504778aab7 nginx:latest "nginx -g 'daemon of…" About an hour ago Up About an hour 80/tcp nginx_nginx.1.p1syqxgwanpys3c64kx9dlwsl
今回はnode1のコンテナを削除してみましょう
$ docker rm 7e504778aab7
すると、以下のように削除したはずのコンテナが復活します。
実はDocker swarmはdocker-compose.yml
のreplicas
で指定した数のコンテナが起動するように常に維持しようとします。なので、もしもコンテナが止まったり、削除されたりした場合は、新しいコンテナを作成してこれを補おうとします。上では一度コンテナの作成に失敗しているようですが、最終的には起動しているコンテナが3つになるように常に調整してくれます。
Worker Nodeを減らす
では今度はworker nodeを減らして挙動を見てみましょう。この状態でNode3
のVMの電源を落とします
$ sudo init 0 // Node3の電源を落とす
すると以下のような状態になります。Node3がdown状態になって、Node1にもう一つコンテナが立ち上がっているのがわかります。
このようにNodeがダウンすると、Swarmはそこにデプロイされていたコンテナを別のNodeで起動して数を調整します。賢いですね
この状態でもう一度Node3を起動すると以下のような状態になります。
あれ、Node3にデプロイされていたコンテナがshatdownされちゃいましたね。なんだかNode1にデプロイ先が偏っていて微妙な感じです。うーん。。。
SwarmがNodeの再接続を検知しても、現在のコンテナの状態が正しい場合は、既存のコンテナを移動してrebalanceするようなことはしてくれないみたいですね。
なのでもしも偏っちゃったコンテナをrelocateしたい場合は以下のコマンドを実行してください。
$ docker service update --force nginx_nginx
でもこのコマンドって、既存のコンテナを順番に破棄して、全て新しく入れ替えるコマンドなんですよね。主にあとで紹介するローリングアップデートとかに使うやつです。なので停止したコンテナが大量にできちゃってますね
Manager Nodeを落とす
ではworker nodeではなくmanager nodeを落とすとどうなってしまうのでしょう?
Swarmでは現在リーダーとなっているノード(manager nodeの中から一つ選ばれる)が落ちると、残りのmanager nodeの中から一つを選出し、そのnodeがleader node(Swarmをコントロールするノード)になります。
現在はmanager node1、 worker node2の環境なので、まずはmanager nodeの数を増やします。
このとき、manager nodeの数は最低でも3つないと新しいリーダーを選出できません。なのでまずは今回作ったNode2とNode3を両方共manager nodeに昇格させます。
昇格させる場合は、Node1で以下を実行します。
$ docker node promote ubuntu-node2 // node2をManagerに昇格
$ docker node promote ubuntu-node3 // node3をManagerに昇格
docker node ls
を実行すると、MANAGER STATUSがReachable
になっているのがわかるでしょうか?
$ docker node ls
ID HOSTNAME STATUS AVAILABILITY MANAGER STATUS ENGINE VERSION
ql8aefbw7rg8nfmznejnzfeus * ubuntu-node1 Ready Active Leader 18.09.7
uusz619ly0y6zp3zsdh0br31y ubuntu-node2 Ready Active Reachable 18.09.7
pgouhk6cndlxddjhll1vo1rfv ubuntu-node3 Ready Active Reachable 18.09.7
この状態でNode1を落としてみます。
Node2の上でdocker node ls
を実行してみるとnode3
がLeaderに昇格していることがわかります。
$ docker node ls
ID HOSTNAME STATUS AVAILABILITY MANAGER STATUS ENGINE VERSION
ql8aefbw7rg8nfmznejnzfeus ubuntu-node1 Unknown Active Unreachable 18.09.7
uusz619ly0y6zp3zsdh0br31y * ubuntu-node2 Ready Active Reachable 18.09.7
pgouhk6cndlxddjhll1vo1rfv ubuntu-node3 Ready Active Leader 18.09.7
Node3のPortainerを見てみると、以下のようになっています。
ダウンしたNode1の代わりに、Node3が適切にコンテナを起動して、コンテナの数を維持していることがわかります。
(補足) Leader Node選出ロジックについて
Swarmでは、Swarmを管理するleader nodeが落ちてしまった場合、新しいleader nodeをRaftと呼ばれるロジックに従って選出します。
この場合、ロスト可能なNodeの最大数は、manager nodeの数をNとして(N-1)/2
で計算されます。
3台の場合は(3-1)/2 = 1
なので1台までなら落ちても大丈夫です。
ただし多ければ良いというものでもないみたいで、主にパフォーマンス上の理由から、Docker swarmではmanager nodeの数は最大でも7台までに抑えることが推奨されています。
詳しくは以下を参照して見てください
https://docs.docker.com/engine/swarm/how-swarm-mode-works/nodes/
・A three-manager swarm tolerates a maximum loss of one manager.
・A five-manager swarm tolerates a maximum simultaneous loss of two manager nodes.
・An N manager cluster tolerates the loss of at most (N-1)/2 managers.
・Docker recommends a maximum of seven manager nodes for a swarm.
ローリングアップデート
さて、個人的にSwarmの目玉機能だと思っているローリングアップデートを紹介したいと思います。今まで見てきたように、Swarmでは同じイメージのコンテナを複数立ててロードバランシングすることができます。
そして複数コンテナを立てている環境では、デプロイされているコンテナを順番に更新していくことで、無停止デプロイを実現することができます。俗に言うローリングアップデートと言うやつですね。
さっそく試してみましょうdocker-compose.yml
を以下のように書き換えます
version: '3.7'
services:
nginx:
image: httpd ## => イメージをnginxからApacheに変更
ports:
- target: 80
published: 8080
protocol: tcp
mode: ingress
deploy:
mode: replicated
replicas: 3
そして再びdocker stack deploy
を実行しましょう。これだけでローリングアップデートによって、3つのコンテナが順番にApache serverに差し替わります。
$ cd <docker-compose.ymlを用意した場所>
$ docker stack deploy nginx --compose-file ./docker-compose.yml
実行した後、今までnginxが動いていたURLを更新してみてください。どこかのタイミングでnginxがhttpdに差し替わっているのが確認できると思います。
http://<NodeのIP>:8080
httpdの初期画面
ちなみにdocker stack deploy
は、実行時にdocker-compose.yml
の差分を検知して、差分のあったserviceだけをdocker service update
するような動きになっています。
なので実際に実行されているのはdocker service update
ですね。
ロールバック
上で無事nginx
をhttpd
に「アップデート」できました。
しかし世は大nginx時代、皆さんの中にはこのアップデートを快く思わない人もいるでしょう。今すぐ、Apacheを窓から投げ捨て、サーバーのアップデートをなかったコトにして、もとのnginxに戻したくなるかもしれません
そんなときでもSwarmなら安心です。SwarmにはService単位でのrollback機能が実装されています。
以下のコマンドを実行してみてください。
アップデートはなかったことにされ、再びnginxの初期画面を目にすることができるでしょう
$ docker service rollback nginx_nginx
スタックの削除
最後にデプロイしたスタックをまるごと削除します。以下を実行してみましょう
$ docker stack rm nginx
Removing service nginx_nginx
Removing network nginx_default
デプロイされたコンテナがすべて削除され、docker-stack deploy
を行う前の状態に戻った事がわかるかと思います。Swarmの場合は、このように、デプロイしているサービス群を、docker-compose.yml
で定義された単位でまるごと削除することができます。docker-composeで言うところの、$ docker-compose down
と同じようなものだと思えばよいかと思います。
こういう取り回しの良さもdocker swarm
の魅力です。
余談
作成されるコンテナの数を制限したい
ローリングアップデートを繰り返していると、過去のイメージで作られた停止済みコンテナが大量にできてしまいます。
それが嫌な場合は、swarmのoptionでこれを制限することができます。
$ docker swarm update --task-history-limit 5 // 一つのServiceについてコンテナを5世代前まで残す
この設定が効果を発揮するのは次回のdocker service update
時であることに注意してください。
つまりこの設定を有効にしただけでは古いコンテナは削除されません。
もしも古いコンテナを削除したいのであれば、以下のコマンドを実行してみてください。
現在存在する古いコンテナが削除され、現時点のコンテナだけになることがわかると思います
$ docker swarm update --task-history-limit 0 // コンテナ履歴を残さない
$ docker service update --force nginx_nginx
ただし、停止されたコンテナが大量にできたからと言って、大量のストレージが消費されているわけではありません。
以下のコマンドを実行して、停止されているコンテナのストレージ容量を見てみてください
$ docker ps -a -s
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES SIZE
3bb6e4eac2a7 nginx:latest "nginx -g 'daemon of…" 42 seconds ago Up 35 seconds 80/tcp nginx_nginx.2.nd8v1m97djhykqhe1jd7xoopb 2B (virtual 109MB)
cf9d34de148f nginx:latest "nginx -g 'daemon of…" About a minute ago Exited (0) 38 seconds ago nginx_nginx.2.z7h14vx534milfsml9zddyvu7 0B (virtual 109MB)
1369522e3aca portainer/portainer "/portainer -H unix:…" 30 hours ago Up About a minute 0.0.0.0:9000->9000/tcp my-portainer 0B (virtual 75.4MB)
size 0B (virtual 109MB)
となっているのがわかるでしょうか?
実はDockerのコンテナのストレージは元となったcontainer imageとの差分のみを保持する仕組みになっています。なので同じイメージのコンテナがいくら増えようが、使われる容量は0Bです。
むしろDockerを運用するときにはconatiner imageの方を気にするようにしましょう。特に自分が作っているアプリのconatiner imageがエンハンスによって増えていくと、新たなイメージをデプロイするたびにどんどんストレージ容量が使われている可能性があります。
その場合は、定期的にストレージに保存されている古いイメージを削除するなどしたほうが良いでしょう。
まとめ
以上でDocker Swarm modeのハンズオンは終わりです。
ここで紹介した以外にも、様々な機能がswarmには備わっているので興味がある人は調べてみてください。
ちなみにここでは紹介しませんでしたが、DockerのSwarm modeはシングルノードでも実行できます。
その場合は、引数なしでdocker swarm init
と打つだけで、単一のサーバーの上でローリングアップデートやロールバックなどの魅力的な機能を利用することができます。
クラスタ組むほどじゃないんだけど、ローリングアップデートはしたいんだよな〜ってときはとてもお手軽で便利です。(Single node docker swarm でお手軽 rolling updateなんかは参考になります)
ココらへんはk8sにはない利点なんじゃないでしょうか?
以上