Sidekiq を Capistrano でデプロイ
Sidekiq を EC2 で作った本番環境上で稼働させるために systemd で管理する必要があったので、
その方法と、Capistrano で再起動させる方法を忘備録としてまとめました🙌
前提
- ruby 2.6.3
- Rails 6.0.3
- Sidekiq 6.4.1
- Capistrano 3.14.1
目次
- systemd で Sidekiq を管理する
-
sudo
をパスワードなしで実行出来るようにする - Capistrano をインストール
- Capfile を修正
systemd で Sidekiq を管理する
Sidekiq6 以降デーモンとして動作するのではなく、 systemd
などで管理する方針に変更された
ので、まず systemd
で管理するため Unit を定義します。Unit の内容に関しては こちら を参考にしました。
# /etc/systemd/system/sidekiq.service
[Unit]
Description=sidekiq
After=syslog.target network.target
[Service]
Type=notify
WatchdogSec=10
WorkingDirectory=/var/www/example/staging/current
ExecStart=/bin/bash -lc 'exec /home/deploy/.rbenv/shims/bundle exec sidekiq -e staging -C config/sidekiq.yml'
ExecReload=/bin/kill -TSTP $MAINPID
Environment=MALLOC_ARENA_MAX=2
RestartSec=1
Restart=always
[Install]
WantedBy=multi-user.target
この記事ではこの Unit で使われているオプションのみ簡単に解説します。
Unit ファイルは「Unit」「Service」「Install」という3つのセクションで構成され、各セクションの役割は以下の様になります。
セクション | 説明 |
---|---|
Unit | Unit の説明・依存関係を記述する |
Service | 固有の設定項目 |
Install | systemctl enable/disable の動作に影響する設定項目 |
次に上記の Unit で使われているオプションについてまとめていきます。
Unit
オプション | 説明 |
---|---|
Description | Unit の説明 |
After | この Unit より先に起動する Unit |
Service
オプション | 説明 |
---|---|
Type | この Unit のプロセス起動タイプを設定する。 notify は exec (fork()→execve() で新しいプロセスが実行される)と似ていますが、 sd_notify(3) を介してメッセージが通されることが期待される |
WatchdogSec | WatchDog はサービスを定期的に監視して稼働状況をチェックしてくれる機能。 WatchdogSec で指定した時間内に応答がなければ WatchdogSignal で指定した Signal でサービスを停止する |
WorkingDirectory | コマンドを実行するディレクトリを指定 |
ExecStart | このサービスの start 時に実行されるコマンド |
ExecReload | このサービスの reload 時に実行されるコマンド |
Environment | 環境変数を定義する。今回はスレッドあたりのメモリアリーナを制限するため MALLOC_ARENA_MAX を指定しています。(こちら の記事 も合わせて読むのが良いと思います) |
RestartSec | サービスを再起動する前にスリープする時間を指定する |
Restart | プロセスが終了、強制終了、またはタイムアウトした時に再起動するかどうか指定する。no on-success on-failure on-abnormal on-watchdog on-abort always が指定出来る。no の時は再起動しない |
こちら には ExecReload
の指定はありませんが、 capistrano-sidekiq
でデプロイする際、このオプションの指定がないと
00:02 sidekiq:quiet
01 sudo systemctl reload sidekiq.service
01 Failed to reload sidekiq.service: Job type reload is not applicable for unit sidekiq.service.
01 See system logs and 'systemctl status sidekiq.service' for details.
✘ 01 hoge@i-05413653a3f710cc8 0.077s
というエラーが発生したため指定しました。プロセスの PID は環境変数 $MAINPID
で参照でき、
TSTP
シグナルは一時停止を意味します。
Install
オプション | 説明 |
---|---|
WantedBy |
enable 時にこのUnitの .wants ディレクトリにシンボリックリンクを作成する。今回の場合 multi-user.target.wants 内にシンボリックリンクが作成される |
以上が Sidekiq
の Unit
の解説となりますが、さらに Systemd
について学びたい方には こちら の記事が参考になるかと思うのでご一読ください。
sudo
をパスワードなしで実行出来るようにする
capistrano-sidekiq
を使って Sidekiq を再起動する際、 systemctl
を sudo
で実行します
が、パスワードを要求されてしまうので、パスワードなしで実行出来るように修正して行きます。
$ sudo visudo
で visudo
を開きます。
## Allow root to run any commands anywhere
root ALL=(ALL:ALL) ALL
こちらの部分の下の行に新しい権限を追加していきますが、その前にこの ALL
が何を意味しているのかまとめておきます。
# | 説明 |
---|---|
=の左側のALL | 許可するホスト |
()の中の左側のALL | ここにユーザー名を指定するとそのユーザーと同じ権限を得られる。ALLを指定するとどのユーザーにもなれる |
()の中の右側のALL | 上記のグループバージョン |
最後のALL | 実行出来るコマンド。コマンドを複数指定したい時は、, で区切る |
今回の場合、ユーザー名が hoge
だとして、「パスワードなしで /usr/bin/systemctl
のみ実行出来る」という権限にしたいので以下の様になります。
hoge ALL=(ALL) NOPASSWD: /usr/bin/systemctl
Capfile を修正
既に capistrano
はインストールされている前提として、 capistrano-sidekiq
をインストールします。
# Gemfile
group 'development' do
gem 'capistrano-sidekiq'
end
さらに Capfile
に以下を追加します。
# Capfile
require 'capistrano/sidekiq'
install_plugin Capistrano::Sidekiq
install_plugin Capistrano::Sidekiq::Systemd
capistrano-sidekiq
のコードを読んでみると、次のメソッドで systemctl
コマンドを組み立てているのですが、 sidekiq_service_unit_user
に :system
を設定しない場合、 systemctl --user
を実行してしまいます。
def systemctl_command(*args, process: nil)
execute_array =
if fetch(:sidekiq_service_unit_user) == :system
[:sudo, :systemctl]
else
[:systemctl, '--user']
end
if process
execute_array.push(
*args, sidekiq_service_unit_name(process: process)
).flatten
backend.execute(*execute_array, raise_on_non_zero_exit: false)
else
execute_array.push(*args, sidekiq_service_unit_name).flatten
backend.execute(*execute_array, raise_on_non_zero_exit: false)
end
end
今回はシステム全体でサービスを起動したいので、 sidekiq_service_unit_user
に :system
を指定して sudo
で systemctl
を実行出来るように config/deploy.rb
に以下を追加します。
# config/deploy.rb
set :sidekiq_service_unit_user, :system
ここまで完了すれば次のタスクが実行出来るはずです。
bundle exec cap production sidekiq:stop
bundle exec cap production sidekiq:start
bundle exec cap production sidekiq:quiet
bundle exec cap production sidekiq:restart
参考資料
Discussion