📌

Sidekiq を Capistrano でデプロイ

2022/04/29に公開

Sidekiq を EC2 で作った本番環境上で稼働させるために systemd で管理する必要があったので、
その方法と、Capistrano で再起動させる方法を忘備録としてまとめました🙌

前提

  • ruby 2.6.3
  • Rails 6.0.3
  • Sidekiq 6.4.1
  • Capistrano 3.14.1

目次

  1. systemd で Sidekiq を管理する
  2. sudo をパスワードなしで実行出来るようにする
  3. Capistrano をインストール
  4. 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 のプロセス起動タイプを設定する。 notifyexec (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 内にシンボリックリンクが作成される

以上が SidekiqUnit の解説となりますが、さらに Systemd について学びたい方には こちら の記事が参考になるかと思うのでご一読ください。

sudo をパスワードなしで実行出来るようにする

capistrano-sidekiq を使って Sidekiq を再起動する際、 systemctlsudo で実行します
が、パスワードを要求されてしまうので、パスワードなしで実行出来るように修正して行きます。

$ 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 を指定して sudosystemctl を実行出来るように 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

参考資料

https://github.com/mperham/sidekiq/blob/main/examples/systemd/sidekiq.service
https://qiita.com/JhonnyBravo/items/a28074c20fa9adf02be3#execreload
https://enakai00.hatenablog.com/entry/20130914/1379146157

GitHubで編集を提案

Discussion