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

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

## pumaのデフォルト仕様

なにも指定せずにpuma起動すると、 `config/puma/{env}.rb`, `config/puma.rb`,  の優先順位で設定ファイルが読まれる。
[puma/configuration.rb at 0cd28f373a245eca4e717bff1415b97bbc2356d2 · puma/puma · GitHub](https://github.com/puma/puma/blob/0cd28f373a245eca4e717bff1415b97bbc2356d2/lib/puma/configuration.rb#L206)

ちなみに、ローカル環境などで`rails server` したときはどうも、`puma`, `thin`, `webrick`の順にrackハンドラを探して見つかったやつで起動するっぽい。最近のrailsでは`rails new`するとpumaが入るので、特にいじらないかぎり`rails server`したときにはpumaが起動すると思っていい。
[rack/handler.rb at 2.0.7 · rack/rack · GitHub](https://github.com/rack/rack/blob/2.0.7/lib/rack/handler.rb#L55)

```
% 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](https://github.com/seuros/capistrano-puma)をデフォルト設定でつかう場合、`config/puma/{env}.rb`, `config/puma.rb`は完全無視される。[^1]
代わりに、capistrano3-pumaが持つ設定ファイル生成機能（後述）をもとに新規設定ファイルを作成し、デプロイサーバにアップロードし、それをpuma起動時に読むようになる。

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

### 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](https://github.com/seuros/capistrano-puma/issues/244#issuecomment-318380419)

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

```ruby
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`の実行に注意。手動で加えた設定は消されてしまうので。

```ruby
% 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](https://github.com/seuros/capistrano-puma/blob/v3.1.1/lib/capistrano/puma.rb#L59)

これらの設定値はもちろんcapistrano設定で全部上書きできる。
`config/deploy.rb`や`config/deploy/{env}.rb`に以下のように記述する。（すでに紹介した`puma_conf`の上書きと同じ要領）

```ruby
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](https://github.com/seuros/capistrano-puma#other-configs)

念のためもう一度言うと、ここで設定した設定値は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](https://github.com/puma/puma#clustered-mode)

こういうかんじで。

```ruby
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](https://github.com/seuros/capistrano-puma/blob/v3.1.1/lib/capistrano/templates/puma.rb.erb)

つまり、このテンプレートを差し替えることができれば、設定だけじゃなくてプログラムも自由にかける。
上記のテンプレートを参考に、`config/deploy/templates`または`lib/capistrano/templates`以下にカスタムテンプレートをおけばいい。これらのディレクトリがどこで決まるのかは以下でわかる。
[capistrano-puma/puma.rb at v3.1.1 · seuros/capistrano-puma · GitHub](https://github.com/seuros/capistrano-puma/blob/v3.1.1/lib/capistrano/puma.rb#L32)

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

```ruby
#!/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`を実行

```bash
% be cap staging puma:config
00:00 puma:config
      Uploading /path/to/app/current/config/puma/staging.rb 100.0%
```

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

```ruby
#!/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`時に自動生成する機能は、たぶん`Capfile`に`install_plugin Capistrano::Puma`と書いていれば有効になっている。
[^4]: よくおこなわれるカスタム設定はすでにデフォルトのテンプレートに組み込まれてるので、ここを参照。[capistrano-puma/puma.rb.erb at v3.1.1 · seuros/capistrano-puma · GitHub](https://github.com/seuros/capistrano-puma/blob/v3.1.1/lib/capistrano/templates/puma.rb.erb#L42)