Edited at

capistrano + nginx + Rails 環境で簡単にメンテナンス画面を表示する方法

More than 3 years have passed since last update.


はじめに

サーバーのメンテナンス中には、503レスポンスと一緒にメンテナンス画面をレスポンスすることが多いと思います。

nginxのレイヤーで503レスポンスとメンテナンス画面を表示する仕組みと、それをcapistranoで操作する仕組みを作りました。

今まで、nginx + unicorn な環境でメンテナンスモードにするときには、いちいちnginxのnginx.confをいじって出していました。

綺麗ではないですし、オートスケール環境の時は全てのサーバーのnginx.confを修正するのも現実的ではないです。

ということで、capistranoで、

bundle exec cap production maintenance:on

を実行すると、メンテナンス画面が表示されて、

bundle exec cap production maintenance:off

を実行すると、元に戻る機能を作ってみました。

turnoutというgemを使うとメンテナンス画面を簡単に出すことはできますが、turnoutの場合は、メンテナンスモードであってもRailsへアクセスしてメンテナンス画面を表示します。

例えば、database.ymlの設定を変えているタイミングでRailsからデータベースにアクセスできないような状態でアクセスするとメンテナンス画面ではなく500エラーがレスポンスされてしまい、正しくメンテナンス画面を表示できません。

また、できればメンテナンス中はRailsが稼働するunicornサーバーへのアクセスの受け入れは避けたいです。

そのため、nginx のレイヤーで503レスポンスとメンテナンス画面を表示する仕組みを作りました。


前提

すでにcapistrano + nginx + unicornを利用したデプロイの仕組みができているものとして説明します。

「capistrano nginx unicorn」と検索するといい感じの記事がいくつかヒットするので参考にしてみてください。


設定


maintenance.html の作成

まず、maintenance.htmlを作成して、/public/maintenance.htmlに配置してください。

maintenance.htmlは、CSSなどをstyleタグで埋め込んだ1枚だけの簡単なファイルにしておくといいと思います。


deploy.rb の設定

メンテナンス中であることは/public/tmp/maintenance.htmlが存在するかどうかで判断します。

デフォルトのままだと、リリースのたびに/public/tmpディレクトリは、リセットされてしまいます。

/public/tmpをshared_pathに退避させるために、:linked_dirsに追加します。


config/deploy.rb

set :linked_dirs, fetch(:linked_dirs, []).push('public/tmp')

# 'public/tmp'以外にも :linked_dirs に追加するものがある場合は追加してください。

次に、メンテナンスモードのオン・オフを操作する、maintenance:onタスクとmaintenance:offタスクを作成します。

/lib/capistrano/tasksにrakeファイルを追加してもいいのですが、今回は簡単のため、deploy.rbに追加します。

以下を、config/deploy.rbの末尾に追加して下さい。


config/deploy.rb

namespace :maintenance do

desc 'start maintenance'
task :on do
on roles(:web) do
target_dir = "#{shared_path}/public/tmp"
target_path = "#{target_dir}/maintenance.html"
source_path = "#{release_path}/public/maintenance.html"
execute :mkdir, '-p', target_dir
execute :cp, '-f', source_path, target_path
end
end

desc 'stop maintenance'
task :off do
on roles(:web) do
target = "#{shared_path}/public/tmp/maintenance.html"
execute :rm, target if test "[ -f #{target} ]"
end
end
end



.gitignore の設定

気になる人は、.gitignoreにpublic/tmpを追加してもいいと思います。

public/tmp/*


nginx.conf の設定

最後にnginx.confの設定をします。


nginx.conf

    upstream unicorn {

server unix:/var/www/rails/shared/tmp/sockets/unicorn.sock;
}

server {
listen 80;
server_name example.com;
root /var/www/rails/current/public;

proxy_connect_timeout 60;
proxy_read_timeout 60;
proxy_send_timeout 60;

set $maintenance false;

if ( -f $document_root/tmp/maintenance.html ) {
set $maintenance true;
}

if ( $args ~ "mode=hogehoge" ) {
set $maintenance false;
}

location @maintenance {
try_files /tmp/maintenance.html /maintenance.html;
}

location /healthcheck { # healthcheckをしている場合
if ( $maintenance = true ) {
return 200;
}
}

location / {
index index.html;

if ( $maintenance = true ) {
error_page 503 @maintenance; # メンテナンスページの設定
return 503;
}

try_files $uri @app;
}

location @app {
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $http_host;
proxy_redirect off;

proxy_pass http://unicorn;
}
}


これを設定して、

sudo service nginx configtest  # successful と表示されることを確認した上で

sudo service nginx reload

とするとnginx.confが反映されます。

/var/www/rails/current/public/tmp/maintenance.htmlが存在するときに$maintenanceをtrueにして、メンテナンス画面をレスポンスします。

また、実際のサイトを確認したい場合は、雑ですが、URLに?type=hogehogeを追加すると実際のサイトを確認できます。

AWSのELBなどでヘルスチェックをしている場合は、メンテナンスモード中は、200レスポンスを返すように設定してください。


最後に

設定は以上です。あとは、設定を含んだソースコードをデプロイした後に、

bundle exec cap production maintenance:on

を実行すると、メンテナンス画面が表示されて、

bundle exec cap production maintenance:off

を実行すると、元に戻ります。

ぜひ参考にしてみてください。


参考URL