CUBE SUGAR CONTAINER

技術系のこと書きます。

CentOS7 で Docker Swarm を試してみる

先日リリースされた Docker 1.12 から Docker Swarm が本体に同梱されるようになった。 この Docker Swarm というのは、複数の Docker ホストを束ねて使えるようにするオーケストレーションツールになっている。 今回は、その Docker Swarm がどういったものなのかを一通り触って試してみることにする。

今回使った環境は次の通り。 CentOS 7 を Docker ホストの OS に使う。

$ cat /etc/redhat-release
CentOS Linux release 7.2.1511 (Core)
$ uname -r
3.10.0-327.el7.x86_64

なお、Docker ホストは 3 台構成で、それぞれ node1, node2, node3 と呼ぶことにする。 各ノードはお互いに通信する必要があるので IP アドレスを 192.168.33.11, 192.168.33.12, 192.168.33.13 と振っておく。

ただ、この初期構築は結構めんどくさい。 なので、今回は Vagrant で途中まで自動化したものも用意した。 具体的には Docker のインストールと必要なポートを開けるところまで。

Vagrantfile for Docker Swarm · GitHub

使い方は次の通り。

$ git clone https://gist.github.com/1dc33c45e47c75d03408a44e63c7daa7.git vagrant-docker-swarm
$ cd vagrant-docker-swarm
$ vagrant up

以下は手動で構築する場合の手順になっている。 また、上記で構築できるのは Docker のインストールまで。 だから、クラスタの構築やサービスの定義はいずれにせよ手動で実施する必要がある。

初期設定

それでは各ホストを手動で構築していこう。 まずはホスト名を設定する。

$ sudo hostname node1.example.com
$ cat << 'EOF' | sudo tee /etc/hostname > /dev/null
node1.example.com
EOF

上記で設定する値 (nodeX) は各ホストごとに異なるので注意してほしい。

通信用のポートを開ける

次に Docker Swarm がクラスタ内で通信するのに使うポートを開ける。 具体的には 2377/TCP, 7946/TCP, 4789/TCP, 7946/UDP, 4789/UDP を開ける必要がある。

ただし、ひとつ注意点があって CentOS7 標準の firewalld ではなく iptables を直接使う必要があるようだ。

github.com

そこで、まずは firewalld のサービスを止める。

$ sudo systemctl stop firewalld
$ sudo systemctl disable firewalld

そして、代わりに iptables をインストールする。

$ sudo yum -y install iptables-services
$ sudo systemctl start iptables
$ sudo systemctl enable iptables

必要なポートを開けていこう。 先ほど挙げたポート以外に 80/TCP も開けている。 これは後ほど Docker コンテナ内で立ち上げるサービスへのアクセスに使う。

$ for i in 80 2377 7946 4789; do sudo iptables -I INPUT -j ACCEPT -p tcp --dport $i; done;
$ for i in 7946 4789; do sudo iptables -I INPUT -j ACCEPT -p udp --dport $i; done;

ポートを開けることができたら設定を保存しておく。

$ sudo service iptables save

Docker をインストールする

次に Docker 本体をインストールする。

その前に、まずはパッケージを最新の状態にしておこう。

$ sudo yum -y update

次に Docker の提供するリポジトリを yum に登録する。 CentOS7 が標準で使える Docker は、公式が提供するそれよりも古いため。

$ cat << 'EOF' | sudo tee /etc/yum.repos.d/docker.repo > /dev/null
[dockerrepo]
name=Docker Repository
baseurl=https://yum.dockerproject.org/repo/main/centos/7/
enabled=1
gpgcheck=1
gpgkey=https://yum.dockerproject.org/gpg
EOF

一旦 yum のキャッシュをクリアする。

$ sudo yum clean all

そして Docker 本体をインストールする。 Docker Swarm はバージョン 1.12 から本体に同梱されるようになった。 そのため、これだけで使えるようになる。

$ sudo yum -y install docker-engine

インストールできたら Docker のサービスを起動する。

$ sudo systemctl start docker
$ sudo systemctl enable docker

インストールできたら docker version コマンドを実行しよう。 次のようにクライアントとサーバの両方でエラーが出ていなければ問題ない。

$ sudo docker version
Client:
 Version:      1.12.0
 API version:  1.24
 Go version:   go1.6.3
 Git commit:   8eab29e
 Built:
 OS/Arch:      linux/amd64

Server:
 Version:      1.12.0
 API version:  1.24
 Go version:   go1.6.3
 Git commit:   8eab29e
 Built:
 OS/Arch:      linux/amd64

ついでに Docker コンテナが実行できることも確認しておこう。 hello-world イメージを使って、次のようなメッセージになれば大丈夫。

$ sudo docker run hello-world
Unable to find image 'hello-world:latest' locally
latest: Pulling from library/hello-world

c04b14da8d14: Pull complete
Digest: sha256:0256e8a36e2070f7bf2d0b0763dbabdd67798512411de4cdcf9431a1feb60fd9
Status: Downloaded newer image for hello-world:latest

Hello from Docker!
This message shows that your installation appears to be working correctly.

To generate this message, Docker took the following steps:
 1. The Docker client contacted the Docker daemon.
 2. The Docker daemon pulled the "hello-world" image from the Docker Hub.
 3. The Docker daemon created a new container from that image which runs the
    executable that produces the output you are currently reading.
 4. The Docker daemon streamed that output to the Docker client, which sent it
    to your terminal.

To try something more ambitious, you can run an Ubuntu container with:
 $ docker run -it ubuntu bash

Share images, automate workflows, and more with a free Docker Hub account:
 https://hub.docker.com

For more examples and ideas, visit:
 https://docs.docker.com/engine/userguide/

上記の作業をすべての Docker ホストで実行しよう。 そして、先ほど紹介した Vagrantfile で実行できるのは、ここまで。

また、次の作業からは各 Docker ホストで実行する内容が異なる。

クラスタを作る

さて、ここからはいよいよ Docker Swarm の機能を使っていく。 各ホストで実行する内容が異なるのでターミナルの先頭にノード名を記述することにする。

まずは Docker ホストを束ねたクラスタを作る。 クラスタにはマネージャとワーカというふたつの役割がある。 ワーカは Docker コンテナが動作するだけの Docker ホストになっている。 マネージャは、それに加えてクラスタの管理などを行う。

今回は node1 をマネージャにして、その他のホスト node2 と node3 をワーカにしてクラスタを組んでみよう。

まずはマネージャとして動作する node1 で docker swarm init コマンドを実行する。 これでクラスタを作成できる。 オプションの --listen-addr と --advertise-addr にはクラスタの通信で使う IP アドレスとポートを指定する。

node1 $ sudo docker swarm init --listen-addr 192.168.33.11:2377 --advertise-addr 192.168.33.11:2377
Swarm initialized: current node (37v1n4u827kqfv2iwuh0u395r) is now a manager.

To add a worker to this swarm, run the following command:
    docker swarm join \
    --token SWMTKN-1-1q6s0gycuxtt3hjqlrd02pmnu555fju2m62vx0ivvmnytbp9hs-83fccs5u4bb9lsb40fo9gbd69 \
    192.168.33.11:2377

To add a manager to this swarm, run the following command:
    docker swarm join \
    --token SWMTKN-1-1q6s0gycuxtt3hjqlrd02pmnu555fju2m62vx0ivvmnytbp9hs-c2co7kyo54yc9z3vnvz0ykbhf \
    192.168.33.11:2377

何やら色々と表示されているけど、これは他のノードがクラスタに参加するためのコマンドになっている。 このトークンを知らないとクラスタには参加できないというわけ。

トークンにはマネージャ用とワーカ用のふたつがある。 つまりマネージャもワーカも複数台をクラスタに追加できるということ。

クラスタに参加しているノードの状態は docker node ls コマンドで確認できる。 今はクラスタを作成した直後なので、参加しているノードは node1 だけ。

node1 $ sudo docker node ls
ID                           HOSTNAME           STATUS  AVAILABILITY  MANAGER STATUS
37v1n4u827kqfv2iwuh0u395r *  node1.example.com  Ready   Active        Leader

クラスタにノードを追加する

先ほど作ったクラスタにノードを追加しよう。 追加するには、先ほどクラスタを作成したときに表示されたコマンドを使う。

まずは node2 をクラスタに参加させる。

node2 $ sudo docker swarm join --token SWMTKN-1-1q6s0gycuxtt3hjqlrd02pmnu555fju2m62vx0ivvmnytbp9hs-83fccs5u4bb9lsb40fo9gbd69 192.168.33.11:2377
This node joined a swarm as a worker.

そして node3 も同じようにクラスタに参加させる。

node3 $ sudo docker swarm join --token SWMTKN-1-1q6s0gycuxtt3hjqlrd02pmnu555fju2m62vx0ivvmnytbp9hs-83fccs5u4bb9lsb40fo9gbd69 192.168.33.11:2377
This node joined a swarm as a worker.

上記が実行できたら node1 で再び docker node ls コマンドを実行してみよう。 今度は 3 台が表示されるはず。

node1 $ sudo docker node ls
ID                           HOSTNAME           STATUS  AVAILABILITY  MANAGER STATUS
33mmomv1up2azt0e38h959a48    node2.example.com  Ready   Active
37v1n4u827kqfv2iwuh0u395r *  node1.example.com  Ready   Active        Leader
5opby7sfnpovoqshnjkymxe0s    node3.example.com  Ready   Active

動作確認用の Docker イメージをダウンロードする

次に Docker Swarm の動作を確認するための Docker イメージをダウンロードしよう。 このイメージは Docker コンテナの ID を HTTP (80/TCP) で返すだけのシンプルなものになっている。

https://hub.docker.com/r/momijiame/greeting/

なぜ、このようなものが必要かというと Docker Swarm にはロードバランス機能が備わっているため。 詳しくは後述するが、クラスタ内で動作する各コンテナにアクセスを振り分けることができる。 そこで、アクセス先のコンテナを確認するために上記のようなイメージを用意した。

このイメージを、それぞれの Docker ホストでダウンロードしておこう。 一応、これはやらなくても必要なときにダウンロードされるので動作はする。 とはいえ、あらかじめやっておいたほうが必要な時間の短縮につながるはず。

まずは node1 でダウンロードする。 バージョン (タグ) が複数あるのは、あとで Docker Swarm のローリングアップデート機能を試すため。

node1 $ sudo docker pull momijiame/greeting:1.0
node1 $ sudo docker pull momijiame/greeting:2.0
node1 $ sudo docker pull momijiame/greeting:latest

同じように node2 でもダウンロードする。

node2 $ sudo docker pull momijiame/greeting:1.0
node2 $ sudo docker pull momijiame/greeting:2.0
node2 $ sudo docker pull momijiame/greeting:latest

そして node3 でもダウンロードする。

node3 $ sudo docker pull momijiame/greeting:1.0
node3 $ sudo docker pull momijiame/greeting:2.0
node3 $ sudo docker pull momijiame/greeting:latest

サービスを定義する

さて、ここまでで Docker Swarm を使う準備が整った。 次はクラスタに対してサービスを定義する。 サービスというのは、ようするにクラスタの上で提供してほしい機能をいう。 これは、例えば HTML の静的なホスティングかもしれないし、Rails のアプリケーションだったりするかもしれない。 その内容は、サービスで使う Docker イメージに依存する。

それでは docker service create コマンドを使ってサービスを定義しよう。 名前は helloworld にする。 --replicas オプションは、いくつの Docker ホスト上でコンテナを動作させたいかを表している。 -p オプションは docker run コマンドのときと同じように、提供するポートフォワーディングの設定だ。 そして最後がサービスで使用する Docker イメージになっている。 この他にもオプションは多数あるけど、ここで使うのは上記だけ。

node1 $ sudo docker service create --name helloworld --replicas=2 -p 80:8000 momijiame/greeting:1.0
f44y32qiu9mlma2gcffi0o4xs

docker service ls コマンドでクラスタが提供しているサービスが見られる。

node1 $ sudo docker service ls
ID            NAME        REPLICAS  IMAGE                       COMMAND
f44y32qiu9ml  helloworld  2/2       momijiame/greeting:1.0

上記は概要なので、より詳しい内容は docker service inspect コマンドを使おう。

node1 $ sudo docker service inspect --pretty helloworld
ID:     f44y32qiu9mlma2gcffi0o4xs
Name:       helloworld
Mode:       Replicated
 Replicas:  2
Placement:
UpdateConfig:
 Parallelism:   1
 On failure:    pause
ContainerSpec:
 Image:     momijiame/greeting:1.0
Resources:
Ports:
 Protocol = tcp
 TargetPort = 8000
 PublishedPort = 80

サービスの稼働状況を調べるには docker service ps コマンドを使うと良い。

$ sudo docker service ps helloworld
ID                         NAME          IMAGE                       NODE               DESIRED STATE  CURRENT STATE           ERROR
7uaq5jpd7zu3wx4y0t4qrxzqr  helloworld.1  momijiame/greeting:1.0  node3.example.com  Running        Running 42 seconds ago
634or7vt454kja1xyzb5fxcqb  helloworld.2  momijiame/greeting:1.0  node2.example.com  Running        Running 41 seconds ago

上記は helloworld サービスにおいて Docker コンテナが node2 と node3 で実行されていることを表している。

アクセス

さて、これだけでコンテナが各 Docker ホスト上にばらまかれて稼働し始めている。 実際にコンテナにアクセスしてみよう。 アクセスする先はポートフォワーディングをしている Docker ホストになる。

また、アクセスする先の IP アドレスは、コンテナが実際に稼働している Docker ホストを意識する必要がない。 なぜなら、クラスタ内のどの Docker ホストにアクセスしても、そのアクセスは実際に稼働している Docker ホストに振り分けられる。 また、アクセス内容もロードバランシングされる。

実際に curl コマンドを使って Docker ホストにアクセスしてみよう。

$ curl http://192.168.33.11
Hello, 1cfe277ecd10

これで、ロードバランス先のコンテナの ID が得られる。

続けて実行すると、ラウンドロビン方式でアクセスがロードバランスされていることが見て取れる。

$ curl http://192.168.33.11
Hello, 094ab7b026d0
$ curl http://192.168.33.11
Hello, 1cfe277ecd10
$ curl http://192.168.33.11
Hello, 094ab7b026d0

オートヒーリング

さて、Docker Swarm には一部のコンテナが落ちたときに別の場所で上げなおす機能もある。

例えば node2 で稼働しているコンテナを docker kill コマンドで止めてみよう。

node2 $ sudo docker ps
CONTAINER ID        IMAGE                        COMMAND                  CREATED             STATUS              PORTS               NAMES
1cfe277ecd10        momijiame/greeting:1.0   "/usr/local/bin/gunic"   4 minutes ago       Up 4 minutes        8000/tcp            helloworld.2.634or7vt454kja1xyzb5fxcqb
node2 $ sudo docker kill 1cfe277ecd10
1cfe277ecd10

そして node1 で docker service ps コマンドを実行してサービスの稼働状況を確認する。 すると、node2 で動作していたコンテナがシャットダウンした代わりに node3 で新しいコンテナが動き始めている。

node1 $ sudo docker service ps helloworld
ID                         NAME              IMAGE                       NODE               DESIRED STATE  CURRENT STATE          ERROR
7uaq5jpd7zu3wx4y0t4qrxzqr  helloworld.1      momijiame/greeting:1.0  node3.example.com  Running        Running 4 minutes ago
9eeuo9ug1og7txh2stq4m8css  helloworld.2      momijiame/greeting:1.0  node1.example.com  Running        Running 5 seconds ago
634or7vt454kja1xyzb5fxcqb   \_ helloworld.2  momijiame/greeting:1.0  node2.example.com  Shutdown       Failed 10 seconds ago  "task: non-zero exit (137)"

node3 で確認すると、たしかにコンテナが生まれている。

node3 $ sudo docker ps
CONTAINER ID        IMAGE                        COMMAND                  CREATED             STATUS              PORTS               NAMES
15d48e89915a        momijiame/greeting:1.0   "/usr/local/bin/gunic"   30 seconds ago      Up 27 seconds       8000/tcp            helloworld.2.9eeuo9ug1og7txh2stq4m8css

レプリカ数を増やす

同時に稼働するコンテナ数 (レプリカ) を動的に増減させることもできる。 これには docker service scale コマンドを使う。

ここでは数を 2 から 3 に増やしてみよう。

node1 $ sudo docker service scale helloworld=3
helloworld scaled to 3

先ほどと同じように docker service ps コマンドを使って状況を確認しよう。 すると、すべてのノードでコンテナが稼働しはじめたことがわかる。

node1 $ sudo docker service ps helloworld
ID                         NAME              IMAGE                       NODE               DESIRED STATE  CURRENT STATE               ERROR
7uaq5jpd7zu3wx4y0t4qrxzqr  helloworld.1      momijiame/greeting:1.0  node3.example.com  Running        Running 5 minutes ago
9eeuo9ug1og7txh2stq4m8css  helloworld.2      momijiame/greeting:1.0  node1.example.com  Running        Running about a minute ago
634or7vt454kja1xyzb5fxcqb   \_ helloworld.2  momijiame/greeting:1.0  node2.example.com  Shutdown       Failed about a minute ago   "task: non-zero exit (137)"
43gr58ha2a2f44eyy146wccvi  helloworld.3      momijiame/greeting:1.0  node2.example.com  Running        Running 11 seconds ago

ちなみに、レプリカ数はクラスタのノード数よりも増やすことができる。 その場合は、ひとつのノードで複数のコンテナが起動することになる。

ローリングアップデート

Docker Swarm には、サービスで使うイメージを順番に更新する機能もある。 更新している最中は、そこにアクセスが振り分けられることがない。

試しに helloworld サービスで使うイメージのタグを 1.0 から 2.0 に変更してみよう。 これには docker service update コマンドを使う。

node1 $ sudo docker service update --image momijiame/greeting:2.0 helloworld
helloworld

実行してから docker service ps コマンドを使うと、タグ 1.0 を使っていたコンテナはシャットダウンされている。 そして、代わりにタグ 2.0 を使ったコンテナが各ノードで稼働し始めていることがわかる。

node1 $ sudo docker service ps helloworld
ID                         NAME              IMAGE                       NODE               DESIRED STATE  CURRENT STATE            ERROR
5qg8rxo0sw6ywsgs1jkdf22lo  helloworld.1      momijiame/greeting:2.0  node1.example.com  Running        Running 25 seconds ago
7uaq5jpd7zu3wx4y0t4qrxzqr   \_ helloworld.1  momijiame/greeting:1.0  node3.example.com  Shutdown       Shutdown 28 seconds ago
dr9wpc6kpftiet87m1y60nkpg  helloworld.2      momijiame/greeting:2.0  node3.example.com  Running        Running 22 seconds ago
9eeuo9ug1og7txh2stq4m8css   \_ helloworld.2  momijiame/greeting:1.0  node1.example.com  Shutdown       Shutdown 24 seconds ago
634or7vt454kja1xyzb5fxcqb   \_ helloworld.2  momijiame/greeting:1.0  node2.example.com  Shutdown       Failed 8 minutes ago     "task: non-zero exit (137)"
5sffoje8rnjg2uhpowoutvxk1  helloworld.3      momijiame/greeting:2.0  node2.example.com  Running        Running 28 seconds ago
43gr58ha2a2f44eyy146wccvi   \_ helloworld.3  momijiame/greeting:1.0  node2.example.com  Shutdown       Shutdown 35 seconds ago

もちろんサービスの提供は継続している。

node1 $ curl http://192.168.33.11
Hello, 330a354fa8ea
node1 $ curl http://192.168.33.11
Hello, cfd58c37c40d
node1 $ curl http://192.168.33.11
Hello, 189668ee58a6

ちなみに、各ノードの更新間隔を指定することもできる。 これには docker service update コマンドで --update-delay オプションを指定する。 これはサービスを定義するときに指定することもできる。

$ sudo docker service update --update-delay 1m helloworld
helloworld

更新間隔を 1 分に広げてイメージを変更してみよう。

$ sudo docker service update --image momijiame/greeting:latest helloworld
helloworld

すると node1 が即座に更新される。

$ sudo docker service ps helloworld
ID                         NAME              IMAGE                          NODE               DESIRED STATE  CURRENT STATE           ERROR
8co9b2rt9a5u1q1xnpkmnr4xo  helloworld.1      momijiame/greeting:latest  node3.example.com  Running        Running 2 seconds ago
5qg8rxo0sw6ywsgs1jkdf22lo   \_ helloworld.1  momijiame/greeting:2.0     node1.example.com  Shutdown       Shutdown 3 seconds ago
7uaq5jpd7zu3wx4y0t4qrxzqr   \_ helloworld.1  momijiame/greeting:1.0     node3.example.com  Shutdown       Shutdown 8 minutes ago
dr9wpc6kpftiet87m1y60nkpg  helloworld.2      momijiame/greeting:2.0     node3.example.com  Running        Running 8 minutes ago
9eeuo9ug1og7txh2stq4m8css   \_ helloworld.2  momijiame/greeting:1.0     node1.example.com  Shutdown       Shutdown 8 minutes ago
634or7vt454kja1xyzb5fxcqb   \_ helloworld.2  momijiame/greeting:1.0     node2.example.com  Shutdown       Failed 16 minutes ago   "task: non-zero exit (137)"
5sffoje8rnjg2uhpowoutvxk1  helloworld.3      momijiame/greeting:2.0     node2.example.com  Running        Running 8 minutes ago
43gr58ha2a2f44eyy146wccvi   \_ helloworld.3  momijiame/greeting:1.0     node2.example.com  Shutdown       Shutdown 9 minutes ago

この状態では新しいコンテナの node1 と、古いコンテナの node2 にアクセスが振り分けられているようだ。 コンテナの更新中の node3 にはアクセスが振り分けられない。

$ curl http://192.168.33.11
Hello, e1c8d02b9b98
$ curl http://192.168.33.11
Hello, 189668ee58a6
$ curl http://192.168.33.11
Hello, e1c8d02b9b98
$ curl http://192.168.33.11
Hello, 189668ee58a6

しばらくすると node3 の更新がおわる。

$ sudo docker service ps helloworld
ID                         NAME              IMAGE                          NODE               DESIRED STATE  CURRENT STATE                ERROR
8co9b2rt9a5u1q1xnpkmnr4xo  helloworld.1      momijiame/greeting:latest  node3.example.com  Running        Running about a minute ago
5qg8rxo0sw6ywsgs1jkdf22lo   \_ helloworld.1  momijiame/greeting:2.0     node1.example.com  Shutdown       Shutdown about a minute ago
7uaq5jpd7zu3wx4y0t4qrxzqr   \_ helloworld.1  momijiame/greeting:1.0     node3.example.com  Shutdown       Shutdown 9 minutes ago
dr9wpc6kpftiet87m1y60nkpg  helloworld.2      momijiame/greeting:2.0     node3.example.com  Running        Running 9 minutes ago
9eeuo9ug1og7txh2stq4m8css   \_ helloworld.2  momijiame/greeting:1.0     node1.example.com  Shutdown       Shutdown 9 minutes ago
634or7vt454kja1xyzb5fxcqb   \_ helloworld.2  momijiame/greeting:1.0     node2.example.com  Shutdown       Failed 17 minutes ago        "task: non-zero exit (137)"
4jwq9mphyed0zzc4pvaxrxu4i  helloworld.3      momijiame/greeting:latest  node1.example.com  Running        Preparing 1 seconds ago
5sffoje8rnjg2uhpowoutvxk1   \_ helloworld.3  momijiame/greeting:2.0     node2.example.com  Shutdown       Shutdown 1 seconds ago
43gr58ha2a2f44eyy146wccvi   \_ helloworld.3  momijiame/greeting:1.0     node2.example.com  Shutdown       Shutdown 10 minutes ago

この状況では新しいコンテナの node1 と node3 にアクセスが振り分けられている。 更新中の node2 にはアクセスが振り分けられない。

$ curl http://192.168.33.11
Hello, 0734903b8f27
$ curl http://192.168.33.11
Hello, e1c8d02b9b98
$ curl http://192.168.33.11
Hello, 0734903b8f27
$ curl http://192.168.33.11
Hello, e1c8d02b9b98

そして、さらにしばらくするとすべてのコンテナの更新がおわる。

$ sudo docker service ps helloworld
ID                         NAME              IMAGE                          NODE               DESIRED STATE  CURRENT STATE                ERROR
8co9b2rt9a5u1q1xnpkmnr4xo  helloworld.1      momijiame/greeting:latest  node3.example.com  Running        Running 2 minutes ago
5qg8rxo0sw6ywsgs1jkdf22lo   \_ helloworld.1  momijiame/greeting:2.0     node1.example.com  Shutdown       Shutdown 2 minutes ago
7uaq5jpd7zu3wx4y0t4qrxzqr   \_ helloworld.1  momijiame/greeting:1.0     node3.example.com  Shutdown       Shutdown 11 minutes ago
838oiqxd69e5xkf12inr4hnfw  helloworld.2      momijiame/greeting:latest  node2.example.com  Running        Preparing 1 seconds ago
dr9wpc6kpftiet87m1y60nkpg   \_ helloworld.2  momijiame/greeting:2.0     node3.example.com  Shutdown       Shutdown 2 seconds ago
9eeuo9ug1og7txh2stq4m8css   \_ helloworld.2  momijiame/greeting:1.0     node1.example.com  Shutdown       Shutdown 10 minutes ago
634or7vt454kja1xyzb5fxcqb   \_ helloworld.2  momijiame/greeting:1.0     node2.example.com  Shutdown       Failed 18 minutes ago        "task: non-zero exit (137)"
4jwq9mphyed0zzc4pvaxrxu4i  helloworld.3      momijiame/greeting:latest  node1.example.com  Running        Running about a minute ago
5sffoje8rnjg2uhpowoutvxk1   \_ helloworld.3  momijiame/greeting:2.0     node2.example.com  Shutdown       Shutdown about a minute ago
43gr58ha2a2f44eyy146wccvi   \_ helloworld.3  momijiame/greeting:1.0     node2.example.com  Shutdown       Shutdown 11 minutes ago

この状況では、すべてのコンテナにアクセスが振り分けられる。

$ curl http://192.168.33.11
Hello, 5df4881d10a3
$ curl http://192.168.33.11
Hello, 0734903b8f27
$ curl http://192.168.33.11
Hello, e1c8d02b9b98

サービスを削除する

最後に、サービスが不要になったら docker service rm コマンドで削除しよう。

$ sudo docker service rm helloworld
helloworld

まとめ

Docker 1.12 からオーケストレーションツールの Docker Swarm が本体に同梱されるようになった。 運用に必要そうな機能も一通り揃っていて、単独ですぐに使い始められるところが魅力的だと感じた。