Rails5のpumaをsystemdで起動するメモ

試した際のメモ。
Developmentで動かすことを想定したものなのでProductionは微妙に違うと思う。
試した環境はVMPlayerにインストールしたCentOS7でRubyは2.4.2をrbenvを使ってインストール。
Railsのバージョンは5.1.4でPumaは3.11.0。
起動するアプリは/var/local/rails配下にSampleという名前で設置されているものとする。


## 1. 起動用のスクリプトの準備
以下のようなファイルを用意する。
ファイル名は何でもいいけど拡張子は.serviceであることに注意。

```/etc/systemd/system/puma.service
[Unit]
Description=Puma Application Server
After=network.target

[Service]
Type=simple
User=root
WorkingDirectory=/var/local/rails/Sample
Environment="RAILS_ENV=development"
Environment="PORT=3000"
ExecStart=/usr/local/rbenv/versions/2.4.2/bin/bundle exec puma -C /var/local/rails/Sample/config/puma.rb
TimeoutSec=300
Restart=always

[Install]
WantedBy=multi-user.target

```

スクリプトで呼び出すbundleはSampleのbin/bundleではなく、システム側に入っているものをフルパスで指定すること。
Userは今回はrootで試しているけど、必要に応じて変更すること。

## 2. 起動
OSのsystemctlコマンドを使って起動する。

```
$ chmod +x /etc/systemd/system/puma.service
$ systemctl start puma.service
$ systemctl status puma.service
● puma.service - Puma Application Server
   Loaded: loaded (/etc/systemd/system/puma.service; disabled; vendor preset: disabled)
   Active: active (running) since 火 YYYY-MM-DD HH:mm:ss JST; 7s ago
 Main PID: 13719 (bundle)
   CGroup: /system.slice/puma.service
           └─13719 /var/local/rails/Sample/vendor/bundle/ruby/2.4.0/bin/puma -C /var/local/rails/Sample/config/puma.rb

 MM DD HH:mm:ss centos7.local systemd[1]: Started Puma Application Server.

```

## 3. 遭遇したトラブル
Sampleで使うgemをvender/bundleにインストールしている場合は、上記のスクリプトで問題なかった。
しかしvender/bundleにインストールしていない場合は以下のようなエラーで起動しない。
一瞬だけ起動するが、すぐに落ちるといったほうがいいかも？

```
$ systemctl status puma.service
● puma.service - Puma Application Server
   Loaded: loaded (/etc/systemd/system/puma.service; disabled; vendor preset: disabled)
   Active: failed (Result: start-limit) since 火 YYYY-MM-DD HH:mm:ss JST; 1min 20s ago
  Process: 13796 ExecStart=/usr/local/rbenv/versions/2.4.2/bin/bundle exec puma -C /var/local/rails/Sample/config/puma.rb (code=exited, status=127)
 Main PID: 13796 (code=exited, status=127)

 MM DD HH:mm:ss centos7.local systemd[1]: puma.service: main process exited, code=exited, status=127/n/a
 MM DD HH:mm:ss centos7.local systemd[1]: Unit puma.service entered failed state.
 MM DD HH:mm:ss centos7.local systemd[1]: puma.service failed.
 MM DD HH:mm:ss centos7.local systemd[1]: puma.service holdoff time over, scheduling restart.
 MM DD HH:mm:ss centos7.local systemd[1]: start request repeated too quickly for puma.service
 MM DD HH:mm:ss centos7.local systemd[1]: Failed to start Puma Application Server.
 MM DD HH:mm:ss centos7.local systemd[1]: Unit puma.service entered failed state.
 MM DD HH:mm:ss centos7.local systemd[1]: puma.service failed.
```

```journalctl -xe```というコマンドを使って原因を調べる以下のようなエラーが出ていた。

```
 MM DD HH:mm:ss centos7.local bundle[13796]: bundler: command not found: puma
 MM DD HH:mm:ss centos7.local bundle[13796]: Install missing gem executables with `bundle install`
```
どうやらpumaを見つけられなくて失敗した模様。
それもそのはずで、vender/bundleにgemをインストールしていればpumaも以下の場所に入るので、おそらくこれを使っていると思われる。

```
$ ls -al /var/local/rails/Sample/vendor/bundle/ruby/2.4.0/bin/
合計 44
drwxr-xr-x 2 root root 148  MM DD HH:mm .
drwxr-xr-x 9 root root 104  MM DD HH:mm ..
-rwxr-xr-x 1 root root 599  MM DD HH:mm byebug
-rwxr-xr-x 1 root root 599  MM DD HH:mm listen
-rwxr-xr-x 1 root root 611  MM DD HH:mm nokogiri
-rwxr-xr-x 1 root root 587  MM DD HH:mm puma
-rwxr-xr-x 1 root root 593  MM DD HH:mm pumactl
-rwxr-xr-x 1 root root 591  MM DD HH:mm rackup
-rwxr-xr-x 1 root root 605  MM DD HH:mm rails
-rwxr-xr-x 1 root root 587  MM DD HH:mm rake
-rwxr-xr-x 1 root root 599  MM DD HH:mm spring
-rwxr-xr-x 1 root root 617  MM DD HH:mm sprockets
-rwxr-xr-x 1 root root 587  MM DD HH:mm thor
```

しかしvender/bundleにインストールしていない場合はpumaは存在しないからエラーになる。
## 4. 対応策

原因はわかったけどじゃあどうすれば良いのかと言うと、起動スクリプトでフルパスで使っているbundleと同じディレクトリにpumaがいるはずなのでそれをフルパスで指定するとうまくいく。

```/etc/systemd/system/puma.service
[Unit]
Description=Puma Application Server
After=network.target

[Service]
Type=simple
User=root
WorkingDirectory=/var/local/rails/Sample
Environment="RAILS_ENV=development"
Environment="PORT=3000"
ExecStart=/usr/local/rbenv/versions/2.4.2/bin/bundle exec /usr/local/rbenv/versions/2.4.2/bin/puma -C /var/local/rails/Sample/config/puma.rb
TimeoutSec=300
Restart=always

[Install]
WantedBy=multi-user.target
```

pumaをフルパスで指定するように修正して再挑戦。

```
$ systemctl daemon-reload
$ systemctl start puma
$ systemctl status puma
● puma.service - Puma Application Server
   Loaded: loaded (/etc/systemd/system/puma.service; disabled; vendor preset: disabled)
   Active: active (running) since 火 YYYY-MM-DD HH:mm:ss JST; 3s ago
 Main PID: 13840 (bundle)
   CGroup: /system.slice/puma.service
           └─13840 puma 3.11.0 (tcp://0.0.0.0:3000) [Sample]

 MM DD HH:mm:ss centos7.local systemd[1]: Started Puma Application Server.
 MM DD HH:mm:ss centos7.local systemd[1]: Starting Puma Application Server...
 MM DD HH:mm:ss centos7.local bundle[13840]: Puma starting in single mode...
 MM DD HH:mm:ss centos7.local bundle[13840]: * Version 3.11.0 (ruby 2.4.2-p198), codename: Love Song
 MM DD HH:mm:ss centos7.local bundle[13840]: * Min threads: 5, max threads: 5
 MM DD HH:mm:ss centos7.local bundle[13840]: * Environment: development

```

うまくいった模様。
今回のトラブルに関しては、VMplayerを入れているPCの都合上、グローバル(システム側)に入れてあるものを利用するようにしているrailsアプリを、vender/bundleにgemがインストールされていることが前提と思われるスクリプトがそのままでも動くでしょと思ったのが良くなかった。