3 行でまとめると
- Traefik を使って Docker Compose だけで無停止デプロイを実現
- docker-compose.yml に定義するだけ、他の設定ファイルは不要のお手軽さ
- コンテナは graceful shutdown するようにしておく
docker-compose.yml
ざっくり作り上げた docker-compose.yml がこちらです。
これだけで動きます。リクエストを投げながら nginx-blue, nginx-green のどちらか一方を落としてもエラーになることはありません。

Traefik とは
Traefik はある種のリバースプロキシです。ただし、 HAProxy や nginx と違ってサービス検出機能が備わっているほか、環境変数やメタデータ (Docker の場合は labels) で設定ができるのも特徴的です。
サービス検出機能と 仲良く なる必要はあるものの、設定の記述量が少なくて済みます。 Docker の場合は、 Traefik 自身が Docker デーモンにアクセス出来るようにします。
volumes: - "/var/run/docker.sock:/var/run/docker.sock:ro"
ちょっと気をつけたいところ
- 公式ドキュメントのみを頼ったほうが良いです。Google 検索で調べても情報は出てきますが、
frontendやbackendといった v1.x 系列の設定を取り上げた記事がヒットして非常に混乱します。 - EntryPoints, Routers, Services と複数段定義する必要があり、とっつきにくい印象があるかもしれません。めちゃくちゃ端折って Nginx と対比すると下記の通りです、難しくないと思い込みましょう。
| Traefik | Nginx |
|---|---|
| EntryPoints | listen ディレクティブ |
| Routers | server ディレクティブ(ホスト名) と location ディレクティブ |
| Services | upstream ディレクティブ |
無停止デプロイする
さて、無停止デプロイをしましょう。ポイントは 2 点です。
- コンテナが graceful shutdown *1 するか、リクエストが冪等であること。
- Retry middleware を使う
冒頭の docker-compose.yml では、 nginx に SIGQUIT を投げることで graceful shutdown を実現しています。
image: "nginx:alpine" stop_signal: SIGQUIT labels: (略) - "traefik.http.middlewares.nginx-retry.retry.attempts=4"
healthcheck はなくても大丈夫
traefik には healthcheck 機能があり、これを使ったコンテナの切り離しも可能です。
labels: (略) - "traefik.http.services.nginx-bg.loadbalancer.healthcheck.path=/" - "traefik.http.services.nginx-bg.loadbalancer.healthcheck.interval=1s" - "traefik.http.services.nginx-bg.loadbalancer.healthcheck.timeout=1s"
ただし、 Retry middleware を使うことで下記の動きとなるため、 healthcheck を設定しなくても問題はありません。
- Listen をやめた停止中のコンテナにルーティング
- コネクションが確立出来ないため、即座にリトライ
- 実行中のコンテナにルーティング
メモリ使用量
かなり雑ですが、 1MB のファイルを 100 同時接続で計 10,000 リクエスト、 10GB 分の転送をさせました。
$ ab -n 10000 -c 100 -H "Host: localhost" http://localhost/
# 起動直後 $ docker stats CONTAINER ID NAME CPU % MEM USAGE / LIMIT MEM % NET I/O BLOCK I/O PIDS 903a420aa79c traefik_nginx-blue_1 0.00% 1.898MiB / 592.5MiB 0.32% 800B / 42B 6.21MB / 0B 2 bb10478ecb4b traefik_traefik_1 0.03% 10.18MiB / 592.5MiB 1.72% 800B / 0B 46.3MB / 0B 6 7f123e90926c traefik_nginx-green_1 0.00% 1.777MiB / 592.5MiB 0.30% 578B / 0B 348kB / 0B 2 # リクエスト後 $ docker stats CONTAINER ID NAME CPU % MEM USAGE / LIMIT MEM % NET I/O BLOCK I/O PIDS 903a420aa79c traefik_nginx-blue_1 0.00% 2.191MiB / 592.5MiB 0.37% 4.28MB / 5.21GB 7.27MB / 0B 2 bb10478ecb4b traefik_traefik_1 0.00% 25.42MiB / 592.5MiB 4.29% 10.5GB / 10.5GB 46.8MB / 0B 19 7f123e90926c traefik_nginx-green_1 0.00% 2.293MiB / 592.5MiB 0.39% 4.22MB / 5.22GB 1.4MB / 0B 2
nginx のメモリ使用量の少なさが際立ちますが、 traefik も 25MB に収まっています。実行中も 30MB を超えることはありませんでした。
参考
*1:明確な定義はないですが、ここでは、 Listen をやめ新たなリクエストを受け付けないようにした後、既存のリクエストを処理しきってから終了する振る舞いを指します。参考: Apache HTTP Server の停止と再起動 - Apache HTTP サーバ バージョン 2.4