あらすじ
Rails でDBがコケていたとしても、単体では動作していることをヘルスチェックで確認したかった。
Controller での実装の場合
こんなコントローラを書いたとする。
class HealthcheckController < ApplicationController
def index
User.first
render plain: 'Still Alive'
rescue => e
render plain: 'Not Still Alive', status: :internal_server_error
end
end
が、DBに接続できない時には、これ以前のMySQL serverへの接続の所でコケる(DBがMySQLの場合)。
Action Dispatcherのミドルウェアスタック
Rails では Action Dispatcher を通して middleware の仕組みがある。
Rails on Rack — Ruby on Rails Guides
その為、MySQLに接続の前の middleware にヘルスチェックをする為のロジックを挿入すればよい。
ちなみに今呼ばれている middleware の一覧は、rails console から以下のコマンドで確認できる。
$ rails middleware
use Rack::Sendfile
use ActionDispatch::Static
use ActionDispatch::Executor
use ActiveSupport::Cache::Strategy::LocalCache::Middleware
use Rack::Runtime
use Rack::MethodOverride
use ActionDispatch::RequestId
use Sprockets::Rails::QuietAssets
use Rails::Rack::Logger
use ActionDispatch::ShowExceptions
use WebConsole::Middleware
use ActionDispatch::DebugExceptions
use ActionDispatch::RemoteIp
use ActionDispatch::Reloader
use ActionDispatch::Callbacks
use ActiveRecord::Migration::CheckPending
use ActionDispatch::Cookies
use ActionDispatch::Session::CookieStore
use ActionDispatch::Flash
use Rack::Head
use Rack::ConditionalGet
use Rack::ETag
use Warden::Manager
run <Railsプロジェクト名>::Application.routes
または
irb(main):010:0> <Railsプロジェクト名>::Application.config.middleware
=>
Action Dispatcherのミドルウェアスタックを利用したヘルスチェックの実装
コントローラーを特に書かずに、 app/middleware/healthcheck.rb
を作成し、以下のように記述する
class Healthcheck
OK_RESPONSE = [ 200, { 'Content-Type' => 'text/plain' }, "Still Alive" ]
def initialize(app)
@app = app
end
def call(env)
if env['PATH_INFO'] == '/health'
return OK_RESPONSE
else
@app.call(env)
end
end
end
config/application.rb
に以下を追記する。
config.middleware.insert_before 'ActionDispatch::ShowExceptions', 'Healthcheck'
ここでは、例外を発生させる ActionDispatch:ShowExceptions より前にこの middleware を追加している。 middleware の中では、 /health
パスの時に限り、以降の処理を行わず Still Alive
を返し、他にパスの時には通常通り処理を継続するようにする。
routes.rb に /health を書く必要はないが、何らかコメントして置いた方が他の人がハマりにくいと思う。
確認
Rails 再起動してDBだけ停止させた上で localhost/health にアクセスしてみる。
OK。
参考