概要

既存のアプリケーションサーバをファイル等でメンテナンスモードに切り替えるのではなく、メンテナンスモード専用のサーバを構築する方法です。いわゆるソーリーページを返すだけのサーバですね。
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を置いておきます。