DockerとNginxによるブルーグリーンなデプロイメント
2016年8月26日公開 2016年8月30日更新

Klokan Technologiesでは、主にソフトウェア・サービスを開発しており、それらをDockerコンテナとして運用しています。新バージョンのリリースには、ブルーグリーン・デプロイメント手法を用いています。これにより、ダウンタイムを減らし、設定ミスによる影響を抑えることができます。 基本的な考え方はシンプルです。本番環境を1つだけ用意するのではなく、ブルーとグリーンの2つの同じ環境を用意します。この2つの環境で交互にサービスを実行します。新しいバージョンは、現在アクティブではない環境にデプロイされます。その環境でサービスが正常に起動し、すべてのヘルスチェックに合格した後に、その環境がアクティブであるとマークされます。その後、古いアクティブな環境は停止し、次のデプロイメントを受け取る準備をします。 ある環境をアクティブにしてから、別の環境に切り替えることは重要なステップです。アトミックでなければ、どちらの環境もアクティブではない短い期間が発生し、その間に入ってくるリクエストは失敗してしまいます。この目的のためにNginxを使用しているのは、Nginxにはこの機能があり、また、リバースプロキシとして使用しているからです。
例
全体の流れを説明するために、例を考えてみましょう。ここでは、Pythonアプリケーションをデプロイするために、ファイル名を hello.py
以下のようなものがあります。9000番ポートでHTTPリクエストを待ち、グリーティングで応答し、シンプルなサービスを提供します。
$ cat hello.py from wsgiref.simple_server import make_server GREETING = b'Hello, world!\n' def hello(environ, start_response): start_response('200 OK', [('Content-Type', 'text/plain')]) return [GREETING]. make_server('0.0.0.0', 9000, hello).serve_forever()
アプリケーション用のコンテナと、Nginx用のコンテナを用意します。両者を同じDockerネットワーク上に配置して、お互いに見えるようにします。本番環境ではバックエンドとフロントエンドのネットワークを分けていますが、ここではその必要はありません。ここでは公式のDockerイメージを使用します。
$ docker network create example $ docker pull nginx $ docker pull python:3
まず、BLUE環境でアプリケーション自体を起動します。コンテナに名前を付けていることに注目してください。 hello-BLUE
.
$ docker run eldest --name hello-BLUE v $(pwd)/hello.py:/usr/local/src/hello.py --net=example -d \ python:3 python /usr/local/src/hello.py
そして、Nginxのアプリケーション設定を、以下のファイルに記述します。 nginx-conf.d/hello.conf
.これは、すべてのリクエストをアプリケーションに転送するHTTPプロキシを設定します。ここでも、アプリケーション・コンテナのことを hello-BLUE
.
$ cat nginx-conf.d/hello.conf server { listen 80; location / { proxy_pass http://hello-BLUE:9000; } }
これでNginxを起動することができました。Nginxは先ほど作成した設定ファイルを読み込んで、8080番ポートにアプリケーションプロキシを発行します。
$ docker run eldest --name nginx -v $(pwd)/nginx-conf.d:/etc/nginx/conf.d net=example -p 8080:80 \ -d \ nginx
動作を確認する。
$ curl http://localhost:8080 Hello, world!
での挨拶を変更して、新しいバージョンのサービスを作ります。 hello.py
ファイルを作成します。
$ cat hello.py from wsgiref.simple_server import make_server GREETING = b'Hello, world! (VERSION 2)\n' # <=== Change here def hello(environ, start_response): start_response('200 OK', [('Content-Type', 'text/plain')]) return [GREETING] make_server('0.0.0.0', 9000, hello).serve_forever()
今度はGREEN環境でアプリケーションを再起動します。コンテナ名が変わっていることに注目してください。それ以外のコマンドは以前と同じです。
$ docker run \ --name hello-GREEN \ # <=== Change here. -v $(pwd)/hello.py:/usr/local/src/hello.py \ --net=example \ -d \ python:3 \ python /usr/local/src/hello.py
Nginxの設定でも、アプリケーションコンテナの名前を変更する必要があります。
$ cat nginx-conf.d/hello.conf server { listen 80; location / { proxy_pass http://hello-GREEN:9000; # <=== Change here } }
アクティブな環境を切り替える前に、設定が正しく、アプリケーションに到達できるかどうかを確認する必要があります。そのためのNginxコマンドライン・オプションがあるので、それを一時的なコンテナで実行します。
$ docker run eldest rm urchin -v $(pwd)/nginx-conf.d:/etc/nginx/conf.d -v $(pwd)/nginx-conf.d:/etc/nginx/conf.d --net=example nginx nginx -t nginx: 設定ファイル /etc/nginx/nginx.conf のシンタックスは OK です。 nginx: 設定ファイル /etc/nginx/nginx.conf のテストに成功しました。
チェックが成功した場合は、NginxコンテナにSIGHUPシグナルを送ります。これにより、Nginxコンテナは設定を読み直し、リクエストを落とさずに再起動します。
$ docker kill -s HUP nginx
そして、新しいバージョンがデプロイされていることがわかります。
$ curl http://localhost:8080 Hello, world!(バージョン2)
BLUEの環境はもう必要ないので、削除します。
$ docker stop hello-BLUE $ docker rm hello-BLUE
結論
今回の例では、サービス用のアプリケーションコンテナは1つしかありませんでした。この例では、サービス用のアプリケーションコンテナが1つしかありませんでしたが、もっと多くのコンテナがあり、それらはすべて一緒に操作されます。また、同じマシン上で複数のサービスを実行することもできます。それらはそれぞれ青と緑の環境を持つことになります。 Nginxコンテナや、ステートを持つその他のインフラストラクチャコンテナは、この手法ではデプロイされません。これには、データベースやほとんどのキャッシュが含まれます。データベースは、切り替え時にもアクティブな環境で使用されているため、停止や再起動はできませんし、通常はデプロイごとにキャッシュをクリアする必要はありません。 この方法を自動化されたデプロイメントで実行可能にするには、さらに多くのことが必要です。ここでは、特定のサービスやマシンに対してどの環境が現在アクティブであるかを判断する方法について触れていません。また、各デプロイメントの前に設定ファイルを手動で編集することは、明らかに間違った方法です。Klokan Technologiesでは、この2つの問題を解決するためにAnsibleとカスタムモジュールを使用していますが、それはまた別の機会にご紹介します。また、同じマシン上でより多くのサービスを実行することも可能です。それらはそれぞれ青と緑の環境を持つことになります。