LB配下にメンテナンスサーバをNginxで構築する

Contents
  1. 概要
  2. 実装
  3. ヘルスチェック
  4. メンテナンスページ
  5. 503
  6. 503.html

概要

既存のアプリケーションサーバをファイル等でメンテナンスモードに切り替えるのではなく、メンテナンスモード専用のサーバを構築する方法です。いわゆるソーリーページを返すだけのサーバですね。 LB にメンテナンスサーバを追加して、アプリケーションサーバを LB の分散先から外すことでメンテナンスモードに移行します。

既存のサーバに手を加えずにメンテナンス機能を追加する必要があり構築しました。

実装

といっても Nginx の設定のみです。こんな感じです。

server {
  listen 80;
  root /usr/share/nginx/html;

  location = /healthcheck {
    empty_gif;
    access_log off;
    break;
  }

  error_page 503 @maintenance;
  location @maintenance {
    internal;
    if ($host ~* api) {
      return 503 "{}";
    }
    rewrite ^(.*)$ /503.html break;
    break;
  }

  return 503;
}

それぞれ見ていきます。

ヘルスチェック

  location = /healthcheck {
    empty_gif;
    access_log off;
    break;
  }

メンテナンスページは 503 で返しますが、LB からのヘルスチェックは成功ステータスで返してやる必要があります。 empty_gif は Nginx がメモリ上に保持している 1x1 ピクセルの透過 GIF を返すモジュールです。

Module ngx_http_empty_gif_module

メンテナンスページ

  error_page 503 @maintenance;

503 ステータスを名前付きロケーション @maintenance に内部リダイレクトしています。

  location @maintenance {
    internal;
    if ($host ~* api) {
      return 503 "{}";
    }
    rewrite ^(.*)$ /503.html break;
    break;
  }

メンテナンスページの名前付きロケーションです。 内部リダイレクト専用のロケーションなのでお行儀よく internal を書いています。internal が記述されたロケーションは外部から直接アクセスすることはできません。

今回のシステムはホスト名によって JSON API か通常 Web かを判断しており、API ホストへのリクエストであれば 503 ステータスで空オブジェクトの JSON を返すようにしています。 今回はたまたまそんな感じでしたが、example.com/api/v1/foobar のようにパスで分岐させているなら if ($uri ~* ^/api)、ちゃんとヘッダをつけてくれるなら if ($http_accept ~* json) みたいな感じにすれば OK です。

そして、すべてのパスに対してメンテナンスサーバを表示させたいのですべてのパスを /503.html に書き換えています。

503

  return 503;

ヘルスチェック以外のすべてのリクエストに対して 503 を返してメンテナンスページを表示します。

503.html

今回は専用のメンテナンスページを用意したので、/usr/share/nginx/html/503.html に HTML を置いておきます。