LoginSignup
77
59

More than 5 years have passed since last update.

Rails5 + Pumaを実現する上で気をつけるべき5つの設定

Last updated at Posted at 2016-08-01

今の設定ファイル

require 'yaml'

def local_conf
  conf = YAML.load_file('path_to_settings.local.yml')
  conf['puma']
end

directory 'path_to_app_directory'

workers local_conf['workers']
threads_count = local_conf['threads']
threads threads_count, threads_count
phased_restart = local_conf['phased_restart']

preload_app! unless phased_restart
prune_bundler if phased_restart

rackup DefaultRackup
port 3000
worker_timeout local_conf['worker_timeout']
pidfile File.expand_path('tmp/pids/puma.pid')
state_path File.expand_path('tmp/pids/puma.state')
stdout_redirect File.expand_path('log/puma_access.log'), File.expand_path('log/puma_error.log'), true

before_fork do
  ActiveRecord::Base.connection_pool.disconnect! unless phased_restart
end

on_worker_boot do
  unless phased_restart
    ActiveSupport.on_load(:active_record) do
      ActiveRecord::Base.establish_connection
    end
  end
end

plugin :tmp_restart


5つの設定ポイント

  • zero downtimeを実現するための clustered mode設定
  • database connection poolings
  • logrotate設定
  • capistrano-pumaの設定
  • prune_bundlerの設定

zero downtimeを実現するための clustered mode

pumaにおいて、zero downtime、 zero hanging requestを実現するためには、
clustered modeを使う必要がある。clustered modeはworker数を指定するだけで起動する。

puma.rb
# clustered modeを使わない場合、workersは0とする
workers 2
threads_count = 5
threads threads_count, threads_count

workerの数はCPUのコア数と一致させる。

なお、2 worker 5 threadでがっつり負荷をかけたら、2GB以上のメモリを消費した。
少ないメモリで zero downtimeを実現したい場合、unicornの方が適しているかも知れない。
(もちろん、実行しているアプリケーションによって異なる)

database connection pools

ActiveRecordのconnectionはworker毎に設定されている。
そのためdatabase connection poolsの値には、1 workerごとの thread数を設定する。
rails デフォルトでは、ENV['RAILS_MAX_THREADS']に値を設定することがデフォルトとなっている。

logrotate設定について

※ RailsのLoggerにて、rotate & 削除をしている人は気にする必要が無いです.

unicornと違い、Pumaはsignalを送っても puma自体のLogのRotateしか行いません。
そのため、chrono_loggerを使うなり、copytruncateを利用するなり、
今までとは異なる方法で Logrotateを行う必要があります。

capistrano-pumaの設定について

puma_preload_appをtrueにすると、phased-restartが出来ない。
ので、cluster modeのときは、puma_preload_appをfalseにしないといけない。

#lib/capistrano/tasks/puma.rake
  task :smart_restart do
    if !puma_preload_app? && puma_workers.to_i > 1
      invoke 'puma:phased-restart'
    else
      invoke 'puma:restart'
    end
  end

Prune Bundlerの設定

ここ多分いちばん大切。
prune bundler は cluster processが読みに行く GEM_FILEを指定するもの。
これをしないと、Gemfile を書き換えても読み込んでくれなくなるし、正しく current directoryに
存在する Gemfile を見てくれず、過去のGemfileが参照できなくなったタイミングで puma のプロセスが落ちてしまう。

# launcher.rb
    def prune_bundler
      return unless defined?(Bundler)
      puma = Bundler.rubygems.loaded_specs("puma")
      dirs = puma.require_paths.map { |x| File.join(puma.full_gem_path, x) }
      puma_lib_dir = dirs.detect { |x| File.exist? File.join(x, '../bin/puma-wild') }

      unless puma_lib_dir
        log "! Unable to prune Bundler environment, continuing"
        return
      end

      deps = puma.runtime_dependencies.map do |d|
        spec = Bundler.rubygems.loaded_specs(d.name)
        "#{d.name}:#{spec.version.to_s}"
      end

      log '* Pruning Bundler environment'
      home = ENV['GEM_HOME']
      Bundler.with_clean_env do
        ENV['GEM_HOME'] = home
        ENV['PUMA_BUNDLER_PRUNED'] = '1'
        wild = File.expand_path(File.join(puma_lib_dir, "../bin/puma-wild"))
        args = [Gem.ruby, wild, '-I', dirs.join(':'), deps.join(',')] + @original_argv
        Kernel.exec(*args)
      end
    end

処理はこんな感じ。
Bundler.with_clean_env は BUNDLE_ の環境変数を消して yieldを実行するので、
新しいBundlerを読み込もうとしていることが分かる。

なお、prune_bundlerを使いたいときは、preload_app をfalseにして、
workersを2以上にする必要がある。

# launcher.rb
    def prune_bundler?
      @options[:prune_bundler] && clustered? && !@options[:preload_app]
    end

    def clustered?
      (@options[:workers] || 0) > 0
    end
# 確認方法 (by capistrano)
cap staging puma:start
DEBUG [xxxx]       * Pruning Bundler environment
DEBUG [xxxx]       [xxxx] Puma starting in cluster mode...

参考資料:
https://devcenter.heroku.com/articles/deploying-rails-applications-with-the-puma-web-server#database-connections
https://devcenter.heroku.com/articles/concurrency-and-database-connections
http://qiita.com/ma2ge/items/1fba78a08e8d3b82b3c2#logger--logrotate
https://github.com/puma/puma/blob/master/DEPLOYMENT.md#restarting

77
59
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
77
59