やりたかったこと

サーバ構成
サーバ構成

通常はnginxを乗せたWEBサーバからサービス用のAPサーバへとつながる内部バランサへプロクシしていますが、メンテナンス時にはこれをメンテナンス用のAPサーバへと振り分けます。

メンテナンス用のサーバ群は必要?

Sorryを表示するだけの静的ページであれば、nginxからHTMLを返却すればよいのですが、要件として メンテナンス中の文言を容易に変更できる ことが挙げられており、プログラムを介する必要がありました。

検討1:サービス用のAPサーバにメンテナンス機能をのせる?

従来はAPサーバにメンテナンスの機能を持たせ、設定によってメンテナンスについて表示する方法をとっていましたが、APサーバの停止や、そもそもメンテナンス機能の実装による内部の複雑化を避けるため、 メンテナンス機能を外部に出すという判断をしました。

役割分担を明確にしたかったということと、メンテナンス作業中は 絶対にサービス側のAPサーバにアクセスされることがない という前提条件を設けることで、更新手順をシンプルにできると考えました。

検討2:WEBサーバ上にAPサーバをのせる?

次にメンテナンス機能を「どのサーバに持たせるか」を検討しました。

WEBサーバ上にAPサーバを同居することも考えましたが、こちらはNGです。
WEBサーバにはnginxを採用しましたが、WEBサーバにAPサーバを同居するとで発生する次のデメリットが大きいと感じました。

  • APサーバを起動させるためにサーバリソースを増強しなくてはならない
  • 障害発生時の原因特定が難しくなる (切り分けがやりにくくなる)

検討3:メンテナンス用のAPサーバを用意する?

以上について検討し、新しくAPサーバ群を用意することが妥当という結論に至りました。

メリットとして、小さなプログラムのため見通しが確保できる点、メンテナンス中はAPサーバを自由に操作できる点が挙げられます。
デメリットとして、別途サーバを用意しるため、その分のコストがかかる点が挙げられます。

プログラムがシンプルとはいえ、ここが単一障害点(SPOF)になってはいけませんので、LBを挟んで冗長化します。

メンテナンスの切り替え方法とnginxの設定

APサーバのプロクシを動的に変更し、メンテナンスへ切り替える方法は 「特定ファイルの有無」 で判別することとしました。

nginx.confを次のように設定します。

 1upstream service_ap_server {
 2    server lb.ap.example.com:80;
 3}
 4upstream maintenance_ap_server {
 5    server lb.maintenance.example.com:80;
 6}
 7server {
 8    listen 80;
 9    server_name ap.example.com
10    proxy_set_header Host $host;
11    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
12    proxy_set_header X-Forwarded-Host $host;
13    proxy_set_header X-Forwarded-Server $host;
14
15    set $maintenance false;
16    if (-e /path/to/dir/.maintenance ) {
17        set $maintenance true;
18    }
19
20    location / {
21        if ($maintenance = true) {
22            proxy_pass http://maintenance_ap_server;
23        }
24        if ($maintenance = false) {
25            proxy_pass http://service_ap_server;
26        }
27    }
28}

/path/to/dir/.maintenanceにファイルが存在すればメンテナンスモードとして、メンテナンス用のAPサーバにプロクシするという簡単な仕組みです。
また、このファイルはJenkinsから作成、削除できるようにしておきます。

nginxのif文は制約が多いため、野暮ったい書き方になります。

nginxのifは次のような制約があります。

  • AND条件が設定できない
  • ネストできない
  • elseが設定できない

特定の環境でメンテナンスを通過する

メンテナンス中のサービス確認のため、特定のIPアドレスでアクセスした場合はメンテナンスを通過する仕組みを足します。
最終的なnginx.confは以下です。

 1set_real_ip_from xxx.xxx.xxx.xxx;
 2real_ip_header   X-Forwarded-For;
 3
 4upstream service_ap_server {
 5    server lb.ap.example.com:80;
 6}
 7upstream maintenance_ap_server {
 8    server lb.maintenance.example.com:80;
 9}
10server {
11    listen 80;
12    server_name ap.example.com
13    proxy_set_header Host $host;
14    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
15    proxy_set_header X-Forwarded-Host $host;
16    proxy_set_header X-Forwarded-Server $host;
17    proxy_set_header X-Real-IP $remote_addr;
18
19    set $maintenance false;
20    if (-e /path/to/dir/.maintenance ) {
21        set $maintenance true;
22    }
23    if ($remote_addr = "xxx.xxx.xxx.xxx") {
24        set $maintenance false;
25    }
26    if ($remote_addr = "xxx.xxx.xxx.xxx") {
27        set $maintenance false;
28    }
29
30    location / {
31        if ($maintenance = true) {
32            proxy_pass http://maintenance_ap_server;
33        }
34        if ($maintenance = false) {
35            proxy_pass http://service_ap_server;
36        }
37    }
38}

LBを設定しているのでreal_ip_headerで接続元のIPを取得し、特定IPアドレスからアクセスされた場合は変数$maintenanceをfalseで上書きするといった設定です。
もうちょっとうまいやり方がありそうですね。


nginx実践ガイド (impress top gear)

渡辺高志
出版社:インプレス  発売日:2017-02-16

Amazonで詳細を見る

ハイパフォーマンスHTTPサーバ Nginx入門

Clement Nedelcu
出版社:アスキー・メディアワークス  発売日:2011-04-21

Amazonで詳細を見る

マスタリングNginx

Dimitri Aivaliotis
出版社:オライリージャパン  発売日:2013-10-26

Amazonで詳細を見る

この記事の著者 Webrow (うぇぶろう)
Web アプリ開発、 Web 顧問 エンジニア、WordPress サポートいたします。