systemdのユーザモードを使う

Botプログラムが通信環境が不安定になると落ちるのでcronとかshellのループを使ってプログラムを監視していたのですが、VPSサーバのロード負荷が高い場合、複数Botが立ち上がったりなどあまり連携がよくありませんでした。そんななかsystemdでユーザモードが使えることがわかったので使ってみることにしました。とくにsystemdで有効なのはデーモンの再起動機能です。ユーザモードでBotを立ち上げると、何らかの原因でプログラムが終了してもすぐに再起動してくれます。

systemdのsystemモードとuserモードの関係はArchlinux WIKIから引用ですが、わかりやすい説明があります。

systemd –user は systemd –system プロセスとは別のプロセスを起動します。ユーザーユニットからシステムユニットに依存したり参照することはできません。

つまり、systemモードとuserモードは排他的関係でお互いに独立したシステムというとになります。またuserモードで使えるディレクトリにも制約があります。ArchLinux WIKIで見てみるとつぎのようになっています。

  • /usr/lib/systemd/user/: インストールしたパッケージに含まれているサービス
  • ~/.local/share/systemd/user/: ホームディレクトリにインストールしたパッケージのユニット
  • /etc/systemd/user/: システムの管理者が配置する、全てのユーザーが使えるユーザーサービス
  • ~/.config/systemd/user/: ユーザーが配置する、そのユーザーのサービス

このことを念頭において作業に取り掛かります。まずテストにオレオレサービスを作ってみます。

~/.config/systemd/user/oreoreservice.service

[Unit]
Description=test unit

[Service]
ExecStart=/home/user1/bin/oreoreservice
ExecStopPost=/home/user/bin/systemd-email %n

Restart=always
StandardOutput=journal
StandardError=journal
Type=simple

[Install]
WantedBy=default.target

前回、設定したコマンドラインメールからEmailレポートします。

~/bin/systemd-email

#!/bin/bash

SUBJECT="Error report: $1"
RECIPIENT='krishna@mydomain.tld'

/usr/bin/mutt -s "$SUBJECT" "$RECIPIENT" <<ERRMAIL
$(systemctl --user status --full $1)
ERRMAIL

起動します。

systemctl --user enable oreoreservice.service
systemctl --user daemon-reload
systemctl --user start oreoreservice.service

Emailが送られるかどうかチェックしてみます。

systemctl –user kill oreoreservice.service -s SIGKILL

最後にsystemdのユーザインスタンスの自動起動をONにします。これでブート時に自動実行できるようになります。

loginctl enable-linger username

以上です。

追記1:

systemdの資料を読んでいたらもっと良い方法を思いつきました。

oreoreservice.service

[Unit]
Description=test unit
OnFailure=status-report@%n.service

[Service]
ExecStart=/home/user/bin/oreoreservice
Restart=always
StandardOutput=journal
StandardError=journal
Type=simple

[Install]
WantedBy=default.target

status-report@.service

[Unit]
Description=status email for %i to user

[Service]
Type=oneshot
ExecStart=/home/shiva/bin/systemd-email %i

[Install]
WantedBy=default.target

サービスを登録します。

systemctl –user enable status-report@oreoreservice.service.service

テスト:

まず起動します。

systemctl --user start oreoreservice.service
systemctl --user status oreoreservice.service

ステータスで起動していることを確認します。

chmod 644 /home/user/bin/oreoreservice
systemctl --user kill oreoreservice.service -s SIGKILL

つぎにパーミッションを変更して実行できないようにします。killシグナルを送り再起動させます。当然ながらプログラムが実行できないのでエラーになりUnitのOnFailureオプションが発動されます。

追記2:

journalctlでログを取る場合、次のエラーが出ます。

journalctl --user -u bitmex_database.service
Hint: You are currently not seeing messages from the system.
      Users in the 'systemd-journal' group can see all messages. Pass -q to
      turn off this notice.

このエラーを修正するには次のようにします。

/etc/systemd/journald.conf

[Journal]
Storage=persistent
...

コンテナを再起動します。

journalctl --user -u bitmex_database.service
-- Logs begin at Sun 2019-09-15 00:26:22 UTC, end at Sun 2019-09-15 00:26:23 UTC. --
Sep 15 00:26:23 srv0102mex systemd[322]: Started Bitmex database service.
Sep 15 00:26:23 srv0102mex systemd[336]: Failed to attach 336 to compat systemd cgroup /user.slice/user-1000.slice/user@1000.service/bitmex_database

このように正常にログを出力できるようになります。

note:’Failed to attach 336 to compat systemd cgroup’はlxc側の問題なので現在のところプログラムの実行には影響ありません。

参考: