AR ホームベーカリー

オイラのアウトプット用ホームベーカリー!

AmazonLinux2 (RHEL7/CentOS7) で capistrano-sidekiq を systemd 指定する方法

実際に systemd で動くのは sidekiq だけですが。

追記:2021/08/18

対応されてなさそうなら fork して修正作るかー、と思って確認したのですが、それっぽい修正が取り込まれていました。

github.com

ログ記述の append: あたりとか、いまだに RHEL7(CentOS7) だと動作しない気がするのですが、少なくとも完全に RHEL 系を考慮していない以前のようなユニットファイルではなくなったようです。

capistrano-sidekiq

こんなエラーが出ることがあります。

Systemctl stderr: Failed to get D-Bus connection: No such file or directory

これは RHEL7 派生ディストリで、 systemctl から --user オプションを削除されているからのようです。

D-Bus

情報のやり取りを担当するなんか、みたいなふわっとした理解で良いです。 以下にだいぶ頑張ってわかりやすく記述されていますが、読んでも結局よくわかりません。

www.silex.jp

nonylene.hatenablog.jp

busctluser system を可視化できるの初めて知った。

nonylene.hatenablog.jp

sidekiq_service_unit_user

:sidekiq_service_unit_user の値によって systemd の起動オプションに --user が付与されるか否か変わります。

具体的な例は以下。

github.com

この --user オプションが曲者で、 RHEL7 派生、つまり CentOS7 やおそらく同じ派生の AmazonLinux2 では動きません。

bugs.centos.org

Basically we don't know if systemd --user will stay in systemd as is right now. So we have decided to disable it completely so we will not hit regression in future versions of centos.

「systemd の --user が現在のままであるかどうかわからんので、完全に無効化するわ」とのことです。

参考

forums.centos.org

どうする

こうする。

  1. sidekiq を動作させるユーザにパスワードなしで sudo できるように sudoers を編集する
  2. config/deploy.rb に capistrano-sidekiq systemd 向け設定を追加する
  3. bundle exec cap ${RAILS_ENV} sidekiq:install する
  4. /etc/systemd/system/sidekiq.service を手動で修正
  5. daemon-reload で修正を反映する
  6. sidekiq:start sidekiq:quiet sidekiq:stop が動くか確認する

AmazonLinux2 を想定して以下記載します。

0. sidekiq を動作させるユーザにパスワードなしで sudo できるように sudoers を編集する

AmazonLinux2 は ec2-user なら最初から sudoers に居るので飛ばしていい。 RHEL 系なら user ALL=NOPASSWD: ALL のように visudo で記述する。

1. config/deploy.rb に capistrano-sidekiq systemd 向け設定を追加する

config/deploy.rb なり RAILS_ENV ごとの設定ファイルに、下記項目に対応する値を設定する。

# sidekiq systemd options
set :sidekiq_service_unit_user, :system # これで --user オプションを使わなくなる
set :sidekiq_user, -> { "ec2-user" } # sidekiq を動作させるユーザ名、 Rails 動かすユーザと基本は同一

2. bundle exec cap ${RAILS_ENV} sidekiq:install する

production 対象ならローカルから実行するコマンドはこう。

bundle exec cap production sidekiq:install

これでリモートサーバに /etc/systemd/system/sidekiq.service が生成されつつ daemon-reload も実行されるので、 systemctl ${ACTION} sidekiq が使えるようになる。

3. /etc/systemd/system/sidekiq.service を手動で修正

おおよそ以下ような記述のファイルだと思われるので、これらから ExecStart の行を変更する。

[Unit]
Description=sidekiq for example (staging)
After=syslog.target network.target

[Service]
Type=simple
WorkingDirectory=/var/www/example/current
ExecStart=/usr/bin/env /usr/local/bin/bundle exec sidekiq -e staging
ExecReload=/bin/kill -TSTP $MAINPID
ExecStop=/bin/kill -TERM $MAINPID
StandardOutput=append:/var/www/example/shared/log/sidekiq.log
StandardError=append:/var/www/example/shared/log/sidekiq.error.log
User=ec2-user

RestartSec=1
Restart=on-failure

SyslogIdentifier=sidekiq

[Install]
WantedBy=default.target

変更前

ExecStart=/usr/bin/env /usr/local/bin/bundle exec sidekiq -e staging

変更後

ExecStart=/bin/bash -lc "cd /var/www/example/current && /usr/bin/env /usr/local/bin/bundle exec sidekiq -e staging"

4. daemon-reload で修正を反映する

systemd のお約束。

sudo systemctl daemon-reload
  1. sidekiq:start sidekiq:quiet sidekiq:stop が動くか確認する

ローカルからリモートの systemd を操作してそれぞれ動くか確認する。

# 起動
bundle exec cap production sidekiq:start                     # Start sidekiq
# 新しい接続を拒否して今のジョブキューが終了するまで待つ
bundle exec cap production sidekiq:quiet                     # Quiet sidekiq (stop fetching new tasks from Redis)
# 終了(処理中のキューは全部 Redis に戻すので、次回起動すると最初から重複実行されると思われる)
bundle exec cap production sidekiq:stop                      # Stop sidekiq (graceful shutdown within timeout, put unfinished tasks back to Redis)

ちなみに何があるかはご存知 -T を利用した bundle exec cap -T sidekiq で確認できる。

という感じです

一番のメインが ExecStart の編集箇所で、 Capistrano 経由では起動しないけど ssh ログインして RAILS_ROOT 以下に移動して直接 sidekiq を起動すると動いた。

という感じだったので、おそらく Capistrano 特有のユーザシェルを持たない、という動作が邪悪ねんな? というトコから、 bash -lc で明示的にユーザシェルを所持してその中で sidekiq を実行、としたところ動作しました。 なんか筋が悪い感じでもやっとするけど rbenv 利用している環境もこんな感じなのでしゃーなし。

おまけ

StandardOutput StandardErrorappend: がついてるけど、これ AmazonLinux2 の systemd だと verison 219 で動きません。 ので、 syslog を指定した上で、 syslogidentifer だったかな? で sidekiq など指定して、 syslog 経由してログを rotate なり分解なりするようにしましょう。

記述を削除するとそのまま journald に格納されるのですが、個人的に journald にデーモンの動作以外のログが入るのはよくないと思っています。 (実装によっては sidekiq ジョブキューの中身がログに出ると思われるので)

急にヒュッと飛んできて丸一日使ってしまった。