Docker Compose & Nginx で実現する「ゼロダウンタイム」Blue-Green Deployment の実装ガイド

Docker tutorial - IT technology blog
Docker tutorial - IT technology blog

デプロイのたびに発生する「502 Bad Gateway」という悪夢

エンジニアの皆さんなら、午前2時に本番環境へのデプロイコマンドを打つときの、あの心臓がバクバクする感覚をよくご知でしょう。従来のデプロイ方法では、古いコンテナを停止して新しいバージョンを起動する際に、どうしても「ダウンタイム」が発生してしまいます。たとえ30秒であっても、100人のユーザーが同時に決済を行っているようなシステムでは、Connection Refusedエラーはユーザー体験において致命的です。実際のプロジェクトにおいて、このような中断はプロフェッショナリズムの欠如と見なされかねません。

Blue-Green Deploymentこそが、その解決策です。古い店の看板を外して新しい看板を掛け替える(その間、客を外で待たせる)のではなく、すぐ隣に全く同じ新しい店を建てることを想像してみてください。中の準備が整ったら、客の流れを新しい店に誘導するだけです。顧客は普段通り買い物を続け、裏で大きな変更があったことには全く気づきません。

Docker ComposeにはKubernetesのような高度なオーケストレーション機能はありませんが、Nginxをリバースプロキシとして利用することで、非常に安定したゼロダウンタイムシステムを自前で構築することが可能です。

ディレクトリ構造の準備

まず、サーバーにDockerとDocker Composeがインストールされている必要があります。管理を容易にするため、私は通常以下のようなディレクトリ構造にしています:

/my-app/
├── docker-compose.yml
├── nginx/
│   └── default.conf
└── app/
    └── (ソースコード)

この戦略の鍵は、Blue(安定稼働中のバージョン)とGreen(これから公開する最新バージョン)の2つの環境を並行して維持することにあります。

Docker ComposeとNginxの設定

1. docker-compose.yml ファイルの設定

ほぼ同じ構成の2つのサービスを定義しますが、名前だけ変えます。重要な注意点として、ポートの競合を避けるためにアプリケーションのポートをホストに直接マップしないでください。すべてのトラフィックは「ゲートウェイ」であるNginxを経由させる必要があります。

version: '3.8'
services:
  app_blue:
    image: my-app:v1
    networks:
      - app_network

  app_green:
    image: my-app:v2
    networks:
      - app_network

  nginx:
    image: nginx:alpine
    ports:
      - "80:80"
    volumes:
      - ./nginx/default.conf:/etc/nginx/conf.d/default.conf
    depends_on:
      - app_blue
      - app_green
    networks:
      - app_network

networks:
  app_network:
    driver: bridge

2. Nginxによるトラフィック制御

default.confファイルは、インテリジェントな門番の役割を果たします。特定のコンテナを直接指定するのではなく、upstreamブロックを使用することで、必要に応じてトラフィックの向きを簡単に切り替えられるようにします。

upstream my_app {
    server app_blue:8080; # 現在のトラフィックはBlueに流れている
}

server {
    listen 80;
    server_name localhost;

    location / {
        proxy_pass http://my_app;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
    }
}

実際のデプロイ手順

新しいコードがある場合、プロセスは非常にスムーズに進みます:

  1. Green版の最新Dockerイメージをビルドする。
  2. Greenコンテナを起動する(この時点では、ユーザーはまだ古いBlue版を使用しています)。
  3. curlを使用してGreen版の準備ができているか確認する(ヘルスチェック)。
  4. Nginxファイルを修正する:app_blue:8080app_green:8080 に書き換える。
  5. Nginxをリロードする:docker exec nginx nginx -s reload

Nginxのリロードは一瞬で完了します。古い接続は最後まで処理され、新しいリクエストは直接Green版に誘導されます。もしGreen版に予期せぬエラーが発生した場合は?Nginxの設定を元に戻してリロードするだけです。非常に安全です。

新しいコンテナからのJSONレスポンスをデバッグしてデータ形式が正しいか確認する場合、私はよく toolcraft.app/ja/tools/developer/json-formatter のフォーマッタを使用します。拡張機能をインストールしたり重いIDEを開いたりするよりも速くて軽量です。

監視と最適化

トラフィックを切り替えた直後にBlue版を停止しないでください。docker logs -f app_greenコマンドで5〜10分ほどログを観察しましょう。リクエスト数が安定して増え、5xxエラーが発生していなければ、そこで初めて古いコンテナを削除します。

ちょっとしたコツ:DatabaseやRedisの状態を返す/healthエンドポイントを作成しましょう。Nginxの設定を変更する前に、このコマンドを実行してすべてが「ウォームアップ」されていることを確認してください:

docker exec nginx curl http://app_green:8080/health

HTTP 200が返ってくれば、自信を持って切り替えボタンを押せます。この方法は手動ですが、中小規模のプロジェクトでは非常に安定しており、リソースを浪費する複雑なCI/CDシステムの構築を避けることができます。

時には、シンプルさこそが安定の鍵となります。Docker ComposeとNginxの巧みな連携だけで、DevOpsのベテラン専門家でなくても、完璧なゼロダウンタイムシステムを実現するには十分なのです。

Share: