人生リアルタイムアタック

当面はPython学習帳

DB接続を伴わないRailsのヘルスチェックを実装する

あらすじ

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の場合)。

f:id:ShineSpark:20170822003416p:plain

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
=> #<ActionDispatch::MiddlewareStack:0x00563448c71938 @middlewares=[Rack::Sendfile, ActionDispatch::Static, ActionDispatch::Executor, ActiveSupport::Cache::Strategy::LocalCache::Middleware, Rack::Runtime, Rack::MethodOverride, ActionDispatch::RequestId, Sprockets::Rails::QuietAssets, Rails::Rack::Logger, ActionDispatch::ShowExceptions, WebConsole::Middleware, ActionDispatch::DebugExceptions, ActionDispatch::RemoteIp, ActionDispatch::Reloader, ActionDispatch::Callbacks, ActiveRecord::Migration::CheckPending, ActionDispatch::Cookies, ActionDispatch::Session::CookieStore, ActionDispatch::Flash, Rack::Head, Rack::ConditionalGet, Rack::ETag, Warden::Manager]>

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 にアクセスしてみる。

f:id:ShineSpark:20170822010607p:plain

OK。

参考