はじめに
前回、デーモンの基本について勉強する過程において今まで「systemd」とか言うやつを見て見ぬ振りをしていたことに気づきました。
なので今回でこの systemd にある程度の決着をつけようと思います。
そもそもの出発点はデーモンについて理解することなので、systemd で自作スクリプトを Linux のサービス(=デーモン)として登録・起動・管理することを目標にしてみようと思います。
…ハァ〜、だっる。
ゴール
自作スクリプトを systemd 経由でサービス(本物のデーモン)として起動・停止できるようにする。
全体の流れ
- 動かしたいスクリプトを用意
- systemdのサービス定義ファイルを作る
- systemdに登録する
- 起動&停止できるか確認
① サンプルスクリプトの作成
/home/youruser/logtime.sh (※自分のホームに置いてOK)
#!/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
# サービスの説明や起動順序の定義
[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:レスキューモード(最小限の回復)
- WantedBy:
【ちょっと脱線】 .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 : 定期実行したいとき
# /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 を起動する」ように設定できます。
# /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 側も対応が必要
# logtime.service(例)
[Service]
ExecStart=/home/youruser/logtime.sh
StandardInput=socket
sudo systemctl enable --now logtime.socket
📝 .socket は ソケット接続をトリガーに .service を起動します。
.mount : マウントポイントを定義する
たとえば /mnt/myusb に /dev/sdb1 を自動マウントしたい場合
# /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 を紐づけることができます。
# /etc/systemd/system/logbundle.target
[Unit]
Description=My custom log target
そして、logtime.service に以下を追加
[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 を起動したい。
# /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 でデーモンを作ることができたらとりあえずは良しとします。
そのうちまた必要になったら勉強しますね。今はもう、さっさとこの勉強を終わらせたいっす。