はじめに
サーバーのメンテナンス中には、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」と検索するといい感じの記事がいくつかヒットするので参考にしてみてください。
- Capistrano で Rails アプリケーションの自動デプロイ
- Capistrano3でUnicorn+Nginxな環境にRailsをデプロイする:初心者向け
- rails + 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
に追加します。
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
の末尾に追加して下さい。
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の設定をします。
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
を実行すると、元に戻ります。
ぜひ参考にしてみてください。