7839

雑草魂エンジニアブログ

【Linux】Supervisor でプロセス制御(Rails + Puma / Capistrano)

EC2(AmazonLinux2)に構築した Railsアプリケーションのプロセス管理、具体的にはデーモンプロセスとして動かすために、今回 Supervisor を使ったので、備忘録として残しておく。

  • Supervisor v4.2.1

Supervisor とは

supervisord.org

Supervisor とは、Python 製のプロセスの制御・管理ツールで、常時起動させたいスクリプトなどを簡単にデーモン化することができる。(デーモン化することで、クラッシュした際の再起動などを自動で行ってくれる。)

今回は、Rails + Puma で運用しているWEBアプリケーションの Puma のプロセスの制御を Supervisor で行えるようにした。

しかしながら、Linux であれば、「systemd」に service として登録することで同様の設定を簡単に行うことができる。ただし、systemd の場合、基本は root ユーザーで起動停止管理を行う必要がある。

今回、systemd ではなく、Supervisor を採用した大きな理由としては、Capistrano を使って、一般ユーザーでも起動・停止のプロセス管理を実施したかったことが挙げられる。

Supervisor のコマンド

Supervisor に関して、使用するコマンドは2種類ある。(プロセス管理を行うには、supervisord が事前に立ち上がっておく必要がある。)

  1. supervisord:supervisordの起動に使う
  2. supervisorctl:プロセスの管理・制御で使う

supervisord

supervisord の起動を行う

$ /usr/bin/supervisord -n -c /etc/supervisord.conf

オプションは複数あるが、上記ができていればいいのではないかと思う。

  • -c:設定ファイルを指定する
  • -n:フォアグラウンドで起動する(こちらを設定することで、ctrl + cで停止が可能となる。試験時には、便利。)

詳細は、公式ドキュメント 参照。

supervisorctl

プロセス全体の管理・制御を行う場合

# プロセス全体の起動
$ supervisorctl start all
# プロセス全体のステータス確認
$ supervisorctl status
# プロセス全体の停止
$ supervisorctl stop all
# プロセス全体の再起動
$ supervisorctl restart all
# 構成ファイルを再読み込みする(設定変更時、実施要)
$ supervisorctl reread
# supervisordを再起動する
$ supervisorctl reload

1つのプロセスの管理・制御を行う場合

# 起動
$ supervisorctl start <name>
# ステータス確認
$ supervisorctl status <name>
# 停止
$ supervisorctl stop <name>
# 再起動
$ supervisorctl restart <name>

詳細は、公式ドキュメント 参照。

EC2への設定方法

今回は、Puma のプロセスの制御を行う。

1. Supervisorインストール

python パッケージマネージャ(pip/easy_install)を使う。

$ sudo easy_install supervisor
$ supervisord -v
4.2.1

2. デフォルトの設定ファイルを生成する

$ echo_supervisord_conf > /etc/supervisord.conf

3. アプリケーション用の設定ファイルを追加する

$ sudo mkdir /etc/supervisord.d
$ sudo vim /etc/supervisord.d/sample-app.conf
# プロセス名を設定する。今回は、sample-appとする。
[program:sample-app]
# 実行ディレクトリを指定する(RailsのCapistranoのcurrentディレクトリ)
directory=/app/sample-app/current
# 実行コマンド(puma起動)
command=/bin/bash -c "/opt/rbenv/shims/bundle exec pumactl start"
environment=RAILS_ENV="production"
autostart=true
autorestart=true
startsecs=3
user=ec2-user
redirect_stderr=false
# logファイルの保存先を指定する
stderr_logfile=/app/sample-app/shared/log/sample-app.stderr.log
stdout_logfile=/app/sample-app/shared/log/sample-app.stdout.log

詳細な設定項目は、公式ドキュメント を参照。

4. 設定ファイルを変更する

変更内容は、以下。

  • アプリケーション用の設定ファイルを include できるようにする
  • web の管理コンソールを開放し、同時に root 以外のユーザーからのクライアントコマンド許可を行う(Capistranoで実行できるようにしておく)
$ sudo vim /etc/supervisord.conf
# 以下の3箇所のコメントアウトを外してあげる
[inet_http_server]
port=127.0.0.1:9001

[supervisorctl]
serverurl=http://127.0.0.1:9001

[include]
files = /etc/supervisord.d/*.conf
# rootユーザー以外の場合に、以下のエラーが出た
error: <class 'socket.error'>, [Errno 13] Permission denied: file: /usr/lib64/python2.7/socket.py line: 228

5. supervisord をデーモン化するために、systemd にサービスを登録する

Supervisor を使用するには supervisord をあらかじめ起動しておく必要があるため、systemd にサービスを登録して、supervisord をデーモン化しておく。

$ sudo vim /lib/systemd/system/supervisord.service
[Unit]
Description=Supervisor process control system for UNIX
Documentation=http://supervisord.org
After=network.target

[Service]
ExecStart=/usr/bin/supervisord -n -c /etc/supervisord.conf
ExecStop=/usr/bin/supervisorctl $OPTIONS shutdown
ExecReload=/usr/bin/supervisorctl $OPTIONS reload
KillMode=process
Restart=on-failure
RestartSec=50s

[Install]
WantedBy=multi-user.target

以下のコマンドを実行して、「supervisord.service」が systemd に登録されているか確認する。

systemctl list-unit-files --type=service

無事に、サービス登録がされていたら、以下のコマンドを実行して起動・ステータス確認・停止ができるかを確認する。

# 起動
sudo systemctl start supervisord
# 状態確認
sudo systemctl status supervisord
● supervisord.service - Supervisor process control system for UNIX
   Loaded: loaded (/etc/systemd/system/supervisord.service; disabled; vendor preset: disabled)
   Active: active (running) since 2020-12-01 00:00:00 UTC
# 停止
sudo systemctl stop supervisord

6. 起動している状態で、サービスの自動起動の設定を行う

$ sudo systemctl enable supervisord.service
Created symlink from /etc/systemd/system/multi-user.target.wants/supervisord.service to /usr/lib/systemd/system/supervisord.service.

7. 最後に、supervisor でプロセス制御を行う

# 起動
$ supervisorctl start sample-app
# ステータス確認
$ supervisorctl status sample-app
# 停止
$ supervisorctl stop sample-app
# 再起動
$ supervisorctl restart sample-app
# 設定ファイルを変更した場合には、再読み込みが必要
$ supervisorctl reload

無事に、Supervisor で Puma のプロセスを制御できれば設定は完了である。

きちんとデーモン化されているかを確認するために、以下を実行してみる。

# プロセスIDを確認する
$ supervisorctl status sample-app
sample-app        RUNNING   pid 26041, uptime 1:00:00
# プロセスを kill する
$ kill -9 26041
# 再度、プロセスIDを確認する
# プロセスIDが変わっていれば、デーモン化されていることが確認できたことになる
$ supervisorctl status sample-app
sample-app        RUNNING   pid 26045, uptime 1:00:00

これで、設定は完了である。

Capistrano の設定

Capistrano から supervisor を操作する場合は、以下の設定をしておく。

namespace :console do
  desc 'status service'
  task :status do
    on roles(:app) do |_host|
      execute 'supervisorctl status sample-app'
    end
  end
  desc 'start service'
  task :start do
    on roles(:app) do |_host|
      execute 'supervisorctl start sample-app'
    end
  end
  desc 'restart service'
  task :restart do
    on roles(:app) do |_host|
      execute 'supervisorctl restart sample-app'
    end
  end
  desc 'stop service'
  task :stop do
    on roles(:app) do |_host|
      execute 'supervisorctl stop sample-app'
    end
  end
end

namespace を console としているので、以下のようにして実行することができる。

# サービス再起動
bundle exec cap {stage} console:restart
# サービス確認
bundle exec cap {stage} console:status

まとめ

無事に Supervisor でプロセス制御を行うことができた。簡単に設定できて、非常に使いやすいのではないかと思えた。

それでは、ステキな開発ライフを。

関連記事

serip39.hatenablog.com

serip39.hatenablog.com

serip39.hatenablog.com

serip39.hatenablog.com