背景

個人のサービスをdockerで本番運用を行うにあたって

  • コンテナアップデート時のダウンタイムは無くしたい
  • 1つのサーバーで完結したい(別途ロードバランサーを用意したくない)

みたいなことを考えていたのですが、docker swarmを利用すると、とても簡単に実現できたので共有したいと思います。

検証環境

Docker version 17.10.0-ce, build f4ffd25

docker swarm の作成

managerのnodeが1つだけある環境を用意します。

swarmの作成
$ docker swarm init --advertise-addr="リモートのIPアドレス"
nodeの起動を確認
$ docker node ls
ID                            HOSTNAME            STATUS              AVAILABILITY        MANAGER STATUS
qjdzkq17bxva02am04rou51dw *   digitalocean        Ready               Active              Leader

Serviceの作成

1つのサービスを2つのコンテナ(レプリカ)で作成します。

Serviceの作成
$ docker service create --name demo --replicas 2 --update-delay 10s -p 8080:8080 hashicorp/http-echo -listen=:8080 -text="demo-1"

今回の検証にはhashicorp/http-echoのイメージを使用しています。
これは起動時に引数で渡した文字列を表示してくれるWebサーバーのコンテナです。
https://hub.docker.com/r/hashicorp/http-echo/

Service起動の確認

--replicas 2を指定したのでコンテナが2つ作成できています。

コンテナの確認
$ docker ps
CONTAINER ID        IMAGE                        COMMAND                  CREATED             STATUS              PORTS                    NAMES
a0dcff10b6c8        hashicorp/http-echo:latest   "/http-echo -text=..."   10 hours ago        Up 10 hours         5678/tcp                 demo.3.9zba9ygmdnjo764rt8fgprcip
baffe41b305d        hashicorp/http-echo:latest   "/http-echo -text=..."   24 hours ago        Up 24 hours         5678/tcp                 demo.2.ydki5edzc2r1henjtw343el6f
動作の確認
$ curl http://サーバーIPアドレス:8080/
demo-1

Serviceのアップデート

docker service updateコマンドを利用すると、各コンテナを順にローリングアップデートしてくれます。アップデート中も一方のコンテナが起動しているので、サービスを継続しながらのアップデートが可能です。

起動時とは引数を変えてイメージをアップデートします。

Serviceのアップデート
$ docker service update --image hashicorp/http-echo --args  "-text="demo-2" -listen=:8080" demo
demo
overall progress: 2 out of 2 tasks
1/2: running   [==================================================>]
2/2: running   [==================================================>]
verify: Service converged

外部からウォッチしながらアップデートをすると次のようなログになります。

$ while true; do curl http://サーバーIPアドレス:8080/; sleep 1s; done
demo-1
demo-1
demo-1
demo-2 ←片方のコンテナがアップデート完了
demo-1
demo-2
demo-1
demo-2
demo-1
demo-2
demo-1
demo-2
demo-1
demo-2
demo-1
demo-2
demo-2 ←両方のコンテナがアップデート完了
demo-2
demo-2

※今回はService作成時に--update-delay 10sを指定しているので、各コンテナのアップデートには10秒の間隔が空きます。

アップデートのロールバック

docker swarm rollbackコマンドを使えば、デプロイ後に問題が見つかった場合も、サービスを1世代前に戻すことができます。

ロールバック
$ docker service rollback demo
rollback: manually requested rollback
overall progress: rolling back update: 2 out of 2 tasks
1/2: running   [>                                                  ]
2/2: running   [>                                                  ]
verify: Service converged
動作の確認
$ curl http://サーバーIPアドレス:8080/
demo-1

rollbackコマンドは、Docker 17.09.0-ce以降で利用できます
https://docs.docker.com/release-notes/docker-ce/

参考

Apply rolling updates to a service
https://docs.docker.com/engine/swarm/swarm-tutorial/rolling-update/