systemdを使ってプログラムの自動起動が上手く行かなかった時の備忘録

# はじめに
Ubuntu22.04(LTS)のサーバーに時雨堂さんのWebRTC SFU Soraを導入して、自動起動させる時に詰まったので備忘録。デーモンを起動する必要のある他のソフトにも応用できると思う。結論から言うと、起動用のshell scriptがあるのならserviceファイルは```Type=oneshot```と```RemainAfterExit=yes```を記載すると良い。

https://sora.shiguredo.jp/

特に以下の記事で上手く行かなかった人の助けになればと思う。

https://qiita.com/tkato/items/6a227e7c2c2bde19521c

# 一瞬で終了するプログラムをデーモンとして起動したかった
マシンが起動したときに以下のコマンドが自動で走ることを目指す
```shell
/home/$USER/Documents/sora-2022.XX.XX/bin/sora daemon
```

## やり方
今回はsystemdを使って自動起動を実現させるため、まずは以下の２つのファイルを作成していく
| ファイル名                       | 概要                                                                      | 
| -------------------------------- | ------------------------------------------------------------------------- | 
| /etc/systemd/system/XXXX.service | systemdからプログラムを起動するための設定ファイル                         | 
| /opt/パッケージ名/bin/YYYY.sh    | systemdからプログラムを起動するプログラムを呼び出すためのシェルスクリプト。パッケージ名は何でも良い | 

今回は例として、名前は以下のようにする
```XXXX.service=>boot_sora.service```
```パッケージ名=>boot_sora```
```YYYY.sh=>boot_sora.sh```

## boot_sora.serviceの作成
```shell
cd #ホームディレクトリから作業を開始
sudo vi /etc/systemd/system/boot_sora.service
```
boot_sora.serviceの中身
```shell
[Unit]
Description=Auto Start Up Service for Sora #説明文なので好きにに入れる
After=local-fs.target
ConditionPathExists=/opt/boot_sora/bin #自動起動するプログラムが配置されているディレクトリ

[Service]
Type=oneshot #スクリプトでデーモンを一回起動すればそれで済むのでoneshot
RemainAfterExit=yes #本命はExecStartを経て動き出すから
ExecStart=/opt/boot_sora/bin/boot_sora.sh #自動起動するプログラムへのフルパス
Restart=on-failure #プログラムが異常終了した時のみ再起動

[Install]
WantedBy=multi-user.target
```
ファイル作成後は所有者と権限を設定

```shell
sudo chown root:root /etc/systemd/system/boot_sora.service
sudo chmod 644 /etc/systemd/system/boot_sora.service
```

## boot_sora.shの作成
```shell
sudo vi /opt/boot_sora/bin/boot_sora.sh
```
boot_sora.shの中身
```shell
#!/bin/sh
/home/$USER/Documents/sora-2022.XX.XX/bin/sora daemon
```
今回筆者はDocuments配下にソフトを配置していたが、ここのパスは適宜変えること(フルパスが良い)
```shell
sudo chmod 755 /opt/boot_sora/bin
sudo chown root:root /opt/boot_sora/bin/boot_sora.sh
sudo chmod 755 /opt/boot_sora/bin/boot_sora.sh
```

## systemctlの更新
作成したserviceファイルを```enable```するためには、ファイル更新したことをsystemctl側に知らせる必要がある
```shell
sudo systemctl daemon-reload #systemctlの更新
sudo systemctl enable boot_sora.service #boot_sora.serviceの自動起動有効化
sudo systemctl status boot_sora.service
# 以下のように .../boot_sora.service; enabled;となっていれば正常に有効化されている
● boot_sora.service - Auto Start Up Service for Sora
   Loaded: loaded (/etc/systemd/system/boot_sora.service; enabled; vendor preset: disabled)
   Active: inactive (dead)
```
正常にserviceファイルが```enable```されたら、試運転をする
```shell
sudo systemctl start boot_sora.service #手動でserviceを起動
sudo systemctl status RasPiAuto.service #起動後の動作を確認
# こんな出力になるはず
● RasPiAuto.service - A sample automatic execution
   Loaded: loaded (/etc/systemd/system/boot_sora.service; enabled; vendor preset: disabled)
   Active: active (exited) since Tue 2023-05-14 20:47:24 JST; 3min 21s ago
```
```Active: inactive (dead)```が```Active: active (exited)```に変わっていれば、作成したスクリプトは正常に動作している。手動でserviceを止める場合は```sudo systemctl stop boot_sora.service```

# 補足説明

筆者の場合、```Type=simple```にしていた時、デーモンが起動せずに苦慮していた。
そもそも、```Type=simple```は、ExecStart で指定したプロセスがメインのサービスプロセスとなり、このプロセスが終了するとサービスも終了するという意味を持つ。そのため```/opt/boot_sora/bin/boot_sora.sh``` が ```sora daemon``` をバックグラウンドで起動してすぐに終了すると、systemdはサービスが終了したと判断される。

一方で、```Type=oneshot``` と ```RemainAfterExit=yes``` を設定した場合、サービスは ExecStart で指定したプロセスが終了した後も子プロセスを参照して "active" 状態として保持される。したがって、```boot_sora.sh``` スクリプトが ```sora daemon``` をバックグラウンドで起動してすぐに終了しても、systemd上ではサービスがまだ稼働しているとみなされる。今回のケースでサービスの稼働状況を監視する場合、後者の方が好ましいだろう。

### 結局何がいけなかったの？
推測するに、```boot_sora.sh``` スクリプトが```sora daemon``` をバックグラウンドで起動してすぐに終了し、```Type=simple``` の設定下ではその終了がサービスの終了と解釈されたため、サーバーが正常に起動せず、一方で、```Type=oneshot``` の設定下ではスクリプトの終了後もサービスが "active" と見なされ、```sora daemon``` が正常に起動する時間を得られたのかもしれない。