Edited at

capistrano3でpumaの運用ー設定ファイルの管理どうする?

railsユーザの多くがつかってるのではないかと思われるcapistranoとpuma。この組み合わせで、どうやってpuma起動時に任意の設定を指定するか、意外とわけわからんのでまとめた。


pumaのデフォルト仕様

なにも指定せずにpuma起動すると、 config/puma/{env}.rb, config/puma.rb, の優先順位で設定ファイルが読まれる。

puma/configuration.rb at 0cd28f373a245eca4e717bff1415b97bbc2356d2 · puma/puma · GitHub

ちなみに、ローカル環境などでrails server したときはどうも、puma, thin, webrickの順にrackハンドラを探して見つかったやつで起動するっぽい。最近のrailsではrails newするとpumaが入るので、特にいじらないかぎりrails serverしたときにはpumaが起動すると思っていい。

rack/handler.rb at 2.0.7 · rack/rack · GitHub

% rails server

=> Booting Puma
=> Rails 5.2.3 application starting in development
=> Run `rails server -h` for more startup options
Puma starting in single mode...

つまり、ローカルで起動するpumaの挙動を変更したい場合は、config/puma/development.rbをつくるか、config/puma.rbをつくってrails server起動すればいい。


capistranoをつかう場合

しかし、capistrano3-pumaをデフォルト設定でつかう場合、config/puma/{env}.rb, config/puma.rbは完全無視される。1

代わりに、capistrano3-pumaが持つ設定ファイル生成機能(後述)をもとに新規設定ファイルを作成し、デプロイサーバにアップロードし、それをpuma起動時に読むようになる。

ここでcapistrano & pumaの設定ファイルの運用方針が、大きく2つの道にわかれる。


pumaオリジナルconfig/puma/{env}.rb, config/puma.rbで運用する

capistrano3-pumaが提供する設定ファイル機能をつかわずに、従来のpumaの運用方法でやる方法。設定を緻密に詳細に指定したいときはこっちのほうがたぶんやりやすい。

必要な設定を逐一記述して、リポジトリにpushしておく。2

あとは、deploy.rbまたはdeploy/{env}.rbに、設定ファイルのパスpuma_confを設定すればいい。

Why it builds a new puma.config instead of using config/puma/production.rb? (question) · Issue #244 · seuros/capistrano-puma · GitHub

具体的にはこういう設定。

set :deploy_to, '/path/to/app'

set :puma_conf, "#{current_path}/config/puma/staging.rb"

# `current_path`の値は`deploy_to`によって決まるので、
# `puma_conf`は`deploy_to`よりあとに指定する点に注意!

こうすると、capistrano経由でpumaを起動するときに、-C /path/to/app/current/config/puma/staging.rbが指定されてくれる。

% bundle exec cap staging puma:start

00:00 puma:start
using conf file /path/to/app/current/config/puma/staging.rb
01 bundle exec puma -C /path/to/app/current/config/puma/staging.rb --daemon
...


capistrano3-pumaの設定ファイル生成機能をつかって運用する

capistrano3-pumaには設定ファイル生成機能puma:configがある。pumaの設定をがっちりかけなくてもそれなりの設定で動いてくれる。

cap {env} deploy:checkが実行されるときに、存在しなければ/path/to/app/shared/puma.rb(またはpuma_confに指定されたパス)を自動生成して各サーバにアップロードする。3具体的にはこのとき、cap {env} puma:configというタスクが実行されている。

cap {env} puma:configを単体で実行すると、既存ファイルが存在するしないに関係なくcapistrano管理上の設定で上書きされる

ふだんcapistrano運用者がつかうコマンドはcap {env} deploy(内部的にdeploy:checkを実行する)のはずなので、設定ファイルははじめの一回だけ自動生成され、その後都度更新されることはない。

だから、サーバ上の設定を手動で直接かきかえる運用も可能。ただし、そういう運用をおこなう場合は、cap {env} puma:configの実行に注意。手動で加えた設定は消されてしまうので。

% bundle exec cap staging puma:config

00:00 puma:config
Uploading /path/to/app/shared/puma.rb 100.0%

# /path/to/app/shared/puma.rb
# を確認すると、設定ファイルが追加されてたり、上書きされてるのがわかる

# /path/to/app/shared/puma.rb
# ちなみに、このパスを変えたい場合は、前述した要領で`puma_conf`を指定すればいい


capistrano管理上のpuma設定を上書きする

cap {env} puma:configが実行されたときに生成されるファイルは、デフォルトでは以下に定義された値がコピーされる。

capistrano-puma/puma.rb at v3.1.1 · seuros/capistrano-puma · GitHub

これらの設定値はもちろんcapistrano設定で全部上書きできる。

config/deploy.rbconfig/deploy/{env}.rbに以下のように記述する。(すでに紹介したpuma_confの上書きと同じ要領)

set :puma_env, 'staging'

set :puma_threads, [4, 4]
set :puma_workers, 0
set :puma_preload_app, false

設定可能な設定値はこちらのリストも参考

GitHub - seuros/capistrano-puma: Puma integration for Capistrano

念のためもう一度言うと、ここで設定した設定値はpuma起動時に直接読まれるわけではない。puma:configが実行されて、shared/puma.rb(またはpuma_confで指定されたパス)ファイルにその値がハードコードされ、それが起動時(cap {env} puma:start)に読み込まれることによってpumaに渡る。

つまり、puma_threads, puma_workersなどのcapistrano管理上のpuma設定は、cap {env} puma:configが実行されてはじめてリリースされる。


capistrano管理上のpuma設定をプログラムする

ここまで見ると、設定ファイル生成機能はライトにつかえる。しかしpumaのチューニングに余念がない人々にとっては、これだけでは足りない点がある。

puma設定には、設定の「値」だけではなく、「プログラム」がかけるのである。たとえば、pumaプロセスがフォークしたときのコールバックなどをカスタム設定できる仕組みになっている。

GitHub - puma/puma: A Ruby/Rack web server built for concurrency

こういうかんじで。

on_worker_boot do

# configuration here
end

before_fork do
# configuration here
end

こういうことをやりたくなったら4、はじめのpumaオリジナル設定ファイルの運用に切り替えるのをおすすめするが、一応設定ファイル生成機能をつかってもできる。

cap {env} puma:configが実行されるときに、生成される設定ファイルのテンプレートがこれ。

capistrano-puma/puma.rb.erb at v3.1.1 · seuros/capistrano-puma · GitHub

つまり、このテンプレートを差し替えることができれば、設定だけじゃなくてプログラムも自由にかける。

上記のテンプレートを参考に、config/deploy/templatesまたはlib/capistrano/templates以下にカスタムテンプレートをおけばいい。これらのディレクトリがどこで決まるのかは以下でわかる。

capistrano-puma/puma.rb at v3.1.1 · seuros/capistrano-puma · GitHub

config/deploy/templates/puma.rbに以下のようなテンプレートを用意して(このテンプレートはbundle exec capが実行されるローカルになければいけない点に注意)

#!/usr/bin/env puma

puts "Hello!! This is custom puma template!!"

directory '<%= current_path %>'
rackup "<%=fetch(:puma_rackup)%>"
environment '<%= fetch(:puma_env) %>'
pidfile "<%=fetch(:puma_pid)%>"
state_path "<%=fetch(:puma_state)%>"

puma:configを実行

% be cap staging puma:config

00:00 puma:config
Uploading /path/to/app/current/config/puma/staging.rb 100.0%

shared/puma.rbにリリースされてる。

#!/usr/bin/env puma

puts "Hello!! This is custom puma template!!"

directory '/path/to/app/current'
rackup "/path/to/app/current/config.ru"
environment 'staging'
pidfile "/path/to/app/shared/tmp/pids/puma.pid"
state_path "/path/to/app/shared/tmp/pids/puma.state"





  1. pumaのふるまいにはぱっと見変更が反映されたのかされてないのかわからない類のものも多いので、これではまる人は多い気がする。そして設定ファイルのパスに環境差分がおきてしまうのも歯がゆい! 



  2. もしサーバごとに異なるpuma設定で運用したい場合、git pushするのはよくない。 



  3. deploy:check時に自動生成する機能は、たぶんCapfileinstall_plugin Capistrano::Pumaと書いていれば有効になっている。 



  4. よくおこなわれるカスタム設定はすでにデフォルトのテンプレートに組み込まれてるので、ここを参照。capistrano-puma/puma.rb.erb at v3.1.1 · seuros/capistrano-puma · GitHub