新人エンジニアは「デーモン」がわからない　~ 2. systemd？なにそれ怖い~

# はじめに

前回、デーモンの基本について勉強する過程において今まで「systemd」とか言うやつを見て見ぬ振りをしていたことに気づきました。
なので今回でこの systemd にある程度の決着をつけようと思います。
そもそもの出発点はデーモンについて理解することなので、systemd で自作スクリプトを Linux のサービス（＝デーモン）として登録・起動・管理することを目標にしてみようと思います。
…ハァ〜、だっる。


### ゴール

自作スクリプトを systemd 経由でサービス（本物のデーモン）として起動・停止できるようにする。

### 全体の流れ

1. 動かしたいスクリプトを用意
1. systemdのサービス定義ファイルを作る
1. systemdに登録する
1. 起動＆停止できるか確認


## ① サンプルスクリプトの作成

/home/youruser/logtime.sh　（※自分のホームに置いてOK）

```bash
#!/bin/bash
# 毎分、現在時刻をログに記録する簡単なスクリプト
LOGFILE="/tmp/logtime_daemon.log"

while true; do
  echo "$(date): Hello from systemd daemon!" >> "$LOGFILE"
  sleep 60
done
```

実行権限を付けておく
`chmod +x /home/youruser/logtime.sh`


## ② systemd ユニットファイルの作成

/etc/systemd/system/logtime.service を作成します（管理者権限が必要）

`sudo nano /etc/systemd/system/logtime.service`

```ini
# サービスの説明や起動順序の定義
[Unit]
Description=Logtime Daemon
After=network.target

# サービスそのものの設定（どう実行するか）
[Service]
ExecStart=/home/youruser/logtime.sh
Restart=always
User=youruser
Group=youruser

# サービスの自動起動設定
[Install]
WantedBy=multi-user.target
# sudo systemctl enable logtime.service とすると以下のようなシンボリックリンクが作成される
# /etc/systemd/system/multi-user.target.wants/logtime.service → /etc/systemd/system/logtime.service
```

- [Unit]
    - Description：サービスの説明
    - After：このサービスはネットワークが有効になった後で起動される
- [Service]
    - ExecStart：実行するスクリプトを指定する
    - Restart=always：スクリプトが落ちても自動再起動する設定
    - User：サービスをこのユーザ権限で実行する
    - Group：実行時に使用するグループ
- [Install]
    - WantedBy：`systemctl enable`で自動起動設定をONにした時に有効化される起動ターゲット
        - multi-user.target：CUI によるログインで、複数のユーザが同時にログイン可能な、サーバ向けの標準的な起動状態のこと
        - graphical.target：GUI によるログインで、複数のユーザがログイン可能な、デスクトップ向けの標準的な起動状態のこと
        - rescue.target：レスキューモード（最小限の回復） 


### 【ちょっと脱線】 .service ってなに？これも拡張子なの？

そうらしいです。他にも色々あります。

#### 主なユニットファイルの種類と拡張子

|拡張子	|意味	|役割の例|
|-------|-------|-------|
|.service	|サービス|常駐プロセス（デーモン）を定義する|
|.timer	|タイマー|特定の時刻や間隔で .service を起動する
|.socket	|ソケット|ポート待ち受けで .service を起動する
|.mount	|マウント|ファイルシステムのマウント設定
|.target	|ターゲット|複数サービスの起動順序を制御する
|.path	|ファイル監視|特定ファイルやディレクトリの変化を検知して .service を起動する

!! [todo] device, swap もある

#### なぜ拡張子が必要なの？

systemctl は、拡張子がないとどの種類のユニットを扱うのか分からないからです。

たとえば、以下の systemctl startコマンドは今すぐ logtime.service を探して実行しますが、.service を省略して実行することもできます。

`sudo systemctl start logtime`

でもタイマー(timer)の場合は、以下のように .timer 付きで指定する必要があります。
（enableは、起動時に自動起動をONにするコマンド（すぐには実行されない））

`sudo systemctl enable logtime.timer`

#### 各ユニットファイルの超簡単な例

ここでは先ほどの logtime.service を中心に、以下の systemd ユニットタイプ（.timer, .socket, .mount, .target, .path）を実用に近い超簡単な例で紹介します。
（このサービスは /home/youruser/logtime.sh を実行して、1分ごとにログを出すだけの簡単なデーモンです。）

##### .timer ： 定期実行したいとき

```ini
# /etc/systemd/system/logtime.timer
[Unit]
Description=Run logtime every minute

[Timer]
OnBootSec=30sec         # 起動から30秒後に最初の実行
OnUnitActiveSec=1min    # その後、毎分実行
Unit=logtime.service    # 起動対象のサービス

[Install]
# 有効化(enable)した時に「timers.target.wants/」にシンボリックリンクで紐づける指定
# (/etc/systemd/system/timers.target.wants/logtime.timer => /etc/systemd/system/logtime.timer)
WantedBy=timers.target 
```

`sudo systemctl enable --now logtime.timer`

📝 .timer は .service をスケジュールで起動するためのトリガーです。logtime.service を毎分起動→終了させる場合に使えます。

処理の流れ：

logtime.timer
    ↓（時間になったら）
logtime.service
    ↓（サービスとして起動）
logtime.sh

##### .socket ： 接続されたら起動する

これは少し特殊ですが、たとえば「ポート12345に接続があったら logtime.service を起動する」ように設定できます。

```ini
# /etc/systemd/system/logtime.socket
[Unit]
Description=Socket for logtime

[Socket]
ListenStream=12345
Accept=no

[Install]
# /etc/systemd/system/sockets.target.wants/logtime.socket => /etc/systemd/system/logtime.socket
WantedBy=sockets.target
```

そして .service 側も対応が必要

```ini
# logtime.service（例）
[Service]
ExecStart=/home/youruser/logtime.sh
StandardInput=socket
```

`sudo systemctl enable --now logtime.socket`

📝 .socket は ソケット接続をトリガーに .service を起動します。


##### .mount ： マウントポイントを定義する

たとえば /mnt/myusb に /dev/sdb1 を自動マウントしたい場合

```ini
# /etc/systemd/system/mnt-myusb.mount
[Unit]
Description=Mount USB drive

[Mount]
What=/dev/sdb1
Where=/mnt/myusb
Type=auto

[Install]
WantedBy=multi-user.target
```

`sudo mkdir -p /mnt/myusb`
`sudo systemctl enable --now mnt-myusb.mount`

※ ファイル名のルール：/mnt/myusb → mnt-myusb.mount

##### .target ： 一括起動やグループ化

自作の .target に複数の .service を紐づけることができます。

```ini
# /etc/systemd/system/logbundle.target
[Unit]
Description=My custom log target
```

そして、logtime.service に以下を追加

```ini
[Install]
# /etc/systemd/system/logbundle.target.wants/logtime.service => /etc/systemd/system/logtime.service
WantedBy=logbundle.target
```

`sudo systemctl enable logtime.service`
`sudo systemctl start logbundle.target`

.target は複数ユニットの**グループ（起動単位）**として使えます。

##### .path ： ファイルやディレクトリの変化を監視

たとえば /tmp/start_trigger.txt が作られたら logtime.service を起動したい。

```ini
# /etc/systemd/system/logtime.path
[Unit]
Description=Watch trigger file

[Path]
PathExists=/tmp/start_trigger.txt
Unit=logtime.service

[Install]
WantedBy=multi-user.target
```

`sudo systemctl enable --now logtime.path`

.path は ファイル・ディレクトリの監視 → 自動実行 を可能にします。

#### まとめ

|ユニットタイプ|用途|logtimeとの例|
|------------|---|------------|
.service|実行本体|logtime.sh を動かす
.timer|定期実行|毎分自動で実行
.socket|ポート接続で起動|接続時に実行（非同期）
.mount|デバイスマウント|USBやHDD自動マウント
.target|グループ起動|複数サービスまとめ起動
.path|ファイル監視|特定ファイル作成で起動












## ③ systemd にサービスを読み込ませる

`sudo systemctl daemon-reexec`
`sudo systemctl daemon-reload`


## ④ サービスを起動・自動起動に登録！

`sudo systemctl start logtime.service`
`sudo systemctl enable logtime.service`  # 再起動後も自動起動させる場合


### 動作確認

`sudo systemctl status logtime.service`

動いていれば、/tmp/logtime_daemon.log に1分ごとにログが追加されていきます。


### 停止する場合

`sudo systemctl stop logtime.service`


### 削除したい場合

`sudo systemctl disable logtime.service`
`sudo rm /etc/systemd/system/logtime.service`
`sudo systemctl daemon-reload`


### まとめ

|操作|コマンド例|
|----|--------|
起動|sudo systemctl start logtime.service
停止|sudo systemctl stop logtime.service
自動起動ON|sudo systemctl enable logtime.service
ステータス確認|sudo systemctl status logtime.service


### 補足
- 実際の業務では Python で書かれたデーモンを systemd で管理することが多いです
- ログ出力は journald 経由にすることも可能です（StandardOutput=journal など）


## 終わりに
まあわかった部分もあれば、むしろわからない部分が増えた感じですねー。
とりあえず、次に python でデーモンを作ることができたらとりあえずは良しとします。
そのうちまた必要になったら勉強しますね。今はもう、さっさとこの勉強を終わらせたいっす。