今回は、RHEL系Linuxにおけるsystemdサービスの作成から管理方法まで、基礎から実践的な内容まで網羅的に解説します。
自作のスクリプトをサービス化したい、システム起動時に自動実行させたい、といった要望を持つシステム管理者の方に向けて、実務で役立つ知識を丁寧に説明していきます。
systemdとは
systemdは、現代のLinuxシステムで広く採用されているシステム・サービスマネージャーです。RHEL 7以降、CentOS 7以降、そしてAlmaLinux、Rocky Linuxなどの主要なRHEL系ディストリビューションで標準的に使用されています。
従来のinitシステムに代わって登場したsystemdは、サービスの起動・停止・管理を統一的に行うことができ、並列起動による高速化やサービス間の依存関係管理など、多くの利点を持っています。
なぜサービス化が必要なのか
実務では、以下のような場面でスクリプトやアプリケーションをサービス化する必要が出てきます。
- システム起動時に自動的にプログラムを実行したい
- バックグラウンドで常時動作するプロセスを管理したい
- 定期的にバックアップスクリプトを実行したい
- サービスの起動・停止を標準化された方法で管理したい
- プロセスが異常終了した際に自動再起動させたい
これらの要件を満たすために、systemdサービスの作成方法を理解することが重要です。
systemdサービスの基礎知識
ユニットファイルとは
systemdでは、サービスを「ユニット」という単位で管理します。ユニットファイルは、サービスの設定を記述したテキストファイルで、.serviceという拡張子を持ちます。
例えば、myapp.serviceというファイル名でサービスを定義します。
ユニットファイルの配置場所
ユニットファイルを配置する場所は主に2つあります。
- システムにインストールされたパッケージが提供するサービスの配置場所
- yumやdnfでインストールしたソフトウェアのサービスファイルがここに配置される
- 基本的にパッケージ管理システムが管理する領域
- システム管理者が独自に作成・カスタマイズするサービスの配置場所
- /usr/lib/systemd/system/のファイルよりも優先度が高い
- 自作のスクリプトをサービス化する場合はこちらを使用
/etc/systemd/system/に配置してください。この場所に配置することで、システムアップデート時に上書きされることを防ぎ、管理者が作成したサービスであることを明確にできます。サービスユニットファイルの基本構造
ユニットファイルは、INI形式で記述され、主に3つのセクションから構成されます。
[Unit]
Description=サービスの説明
After=network.target
[Service]
Type=simple
ExecStart=/path/to/script
Restart=on-failure
[Install]
WantedBy=multi-user.target
[Unit]セクション
サービスの基本情報と依存関係を定義します。
- Description: サービスの説明文
- After: このサービスが起動する前に起動すべきサービス
- Before: このサービスの後に起動すべきサービス
- Requires: 必須の依存サービス
- Wants: 推奨される依存サービス
[Service]セクション
サービスの実行方法を定義します。
- Type: サービスのタイプ(simple, forking, oneshot など)
- ExecStart: サービス起動時に実行するコマンド
- ExecStop: サービス停止時に実行するコマンド
- Restart: サービス異常終了時の再起動設定
- User: サービスを実行するユーザー
- Group: サービスを実行するグループ
User=とGroup=を指定しない場合、サービスはroot権限で実行されます。セキュリティの観点から、root権限が不要な処理は専用のユーザーで実行することを推奨します。一方、システムの設定変更や特権ポートの使用など、root権限が必要な処理の場合は、これらの指定を省略してroot権限で実行します。[Install]セクション
サービスの有効化に関する設定を定義します。
- WantedBy: どのターゲットで有効にするか(通常は
multi-user.target) - RequiredBy: 必須として有効にするターゲット
実践例1: シンプルなシェルスクリプトのサービス化
最も基本的な例として、簡単なシェルスクリプトをサービス化してみましょう。
ステップ1: スクリプトの作成
まず、サービス化するスクリプトを作成します。
sudo vi /usr/local/bin/hello-service.sh
#!/bin/bash
while true; do
echo "$(date): Hello from systemd service" >> /var/log/hello-service.log
sleep 60
done
実行権限を付与します。
sudo chmod +x /usr/local/bin/hello-service.sh
ステップ2: サービスユニットファイルの作成
sudo vi /etc/systemd/system/hello-service.service
[Unit]
Description=Hello Service Example
After=network.target
[Service]
Type=simple
ExecStart=/usr/local/bin/hello-service.sh
Restart=always
RestartSec=10
[Install]
WantedBy=multi-user.target
ステップ3: サービスの有効化と起動
# systemdにユニットファイルを再読み込みさせる
sudo systemctl daemon-reload
# サービスを起動
sudo systemctl start hello-service
# サービスの状態確認
sudo systemctl status hello-service
# システム起動時の自動起動を有効化
sudo systemctl enable hello-service
実践例2: バックアップスクリプトの定期実行
バックアップスクリプトを定期的に実行する方法を2通り紹介します。
バックアップスクリプトの作成
sudo vi /usr/local/bin/backup.sh
#!/bin/bash
BACKUP_DIR="/backup"
DATE=$(date +%Y%m%d_%H%M%S)
SOURCE_DIR="/home"
mkdir -p ${BACKUP_DIR}
tar -czf ${BACKUP_DIR}/home_backup_${DATE}.tar.gz ${SOURCE_DIR}
# 30日以上前のバックアップを削除
find ${BACKUP_DIR} -name "home_backup_*.tar.gz" -mtime +30 -delete
echo "$(date): Backup completed" >> /var/log/backup.log
sudo chmod +x /usr/local/bin/backup.sh
方法1: 通常のサービスとして実行
この方法では、スクリプト内でsleepを使って定期実行を実現します。
sudo vi /etc/systemd/system/backup.service
[Unit]
Description=Backup Service
After=network.target
[Service]
Type=oneshot
ExecStart=/usr/local/bin/backup.sh
User=root
[Install]
WantedBy=multi-user.target
ここではType=oneshotを使用しています。これは一度実行して終了するタイプのサービスに適しています。
方法2: systemdタイマーを使用
より柔軟で推奨される方法として、systemdタイマーを使用します。
サービスファイルの作成
sudo vi /etc/systemd/system/backup.service
[Unit]
Description=Backup Service
Wants=backup.timer
[Service]
Type=oneshot
ExecStart=/usr/local/bin/backup.sh
User=root
[Install]
WantedBy=multi-user.target
タイマーファイルの作成
sudo vi /etc/systemd/system/backup.timer
[Unit]
Description=Backup Timer
Requires=backup.service
[Timer]
OnCalendar=daily
OnCalendar=02:00
Persistent=true
[Install]
WantedBy=timers.target
タイマーの有効化と起動
sudo systemctl daemon-reload
sudo systemctl enable backup.timer
sudo systemctl start backup.timer
# タイマーの状態確認
sudo systemctl list-timers backup.timer
cronとsystemdタイマーの違い
- systemdのログ管理機能(journalctl)と統合されている
- サービスの依存関係を管理できる
- サービスが実行されなかった場合の補完実行(Persistent=true)
- より正確なスケジューリング
- シンプルで習得が容易
- 長年使われてきた実績
- 多くの環境で標準的に使用されている
実務では、systemdが標準のシステムではsystemdタイマーを使用することが推奨されますが、互換性や既存システムとの統一性を考慮してcronを選択する場合もあります。
実践例3: 監視スクリプトのデーモン化
システムの状態を常時監視するスクリプトをデーモンとして動作させる例です。
監視スクリプトの作成
sudo vi /usr/local/bin/monitor.sh
#!/bin/bash
LOG_FILE="/var/log/monitor.log"
CHECK_INTERVAL=300 # 5分ごとにチェック
while true; do
# ディスク使用率をチェック
DISK_USAGE=$(df -h / | awk 'NR==2 {print $5}' | sed 's/%//')
if [ ${DISK_USAGE} -gt 80 ]; then
echo "$(date): WARNING - Disk usage is ${DISK_USAGE}%" >> ${LOG_FILE}
fi
# メモリ使用率をチェック
MEM_USAGE=$(free | awk 'NR==2 {printf "%.0f", $3/$2*100}')
if [ ${MEM_USAGE} -gt 90 ]; then
echo "$(date): WARNING - Memory usage is ${MEM_USAGE}%" >> ${LOG_FILE}
fi
sleep ${CHECK_INTERVAL}
done
sudo chmod +x /usr/local/bin/monitor.sh
サービスファイルの作成
sudo vi /etc/systemd/system/monitor.service
[Unit]
Description=System Monitor Service
After=network.target
[Service]
Type=simple
ExecStart=/usr/local/bin/monitor.sh
Restart=always
RestartSec=10
StandardOutput=journal
StandardError=journal
[Install]
WantedBy=multi-user.target
この例では、Restart=alwaysを設定しているため、スクリプトが異常終了しても自動的に再起動されます。RestartSec=10により、再起動前に10秒間待機します。
sudo systemctl daemon-reload
sudo systemctl enable monitor.service
sudo systemctl start monitor.service
サービスの基本操作コマンド
systemdサービスを管理する基本的なコマンドを紹介します。
サービスの起動
sudo systemctl start サービス名.service
サービスを即座に起動します。.service拡張子は省略可能です。
サービスの停止
sudo systemctl stop サービス名.service
実行中のサービスを停止します。
サービスの再起動
sudo systemctl restart サービス名.service
サービスを一度停止してから再度起動します。設定ファイルを変更した場合などに使用します。
サービスの状態確認
sudo systemctl status サービス名.service
サービスの現在の状態、最近のログ、プロセスIDなどを表示します。
自動起動の有効化
sudo systemctl enable サービス名.service
システム起動時にサービスが自動的に起動するように設定します。
自動起動の無効化
sudo systemctl disable サービス名.service
システム起動時の自動起動を無効にします。
有効化と同時に起動
sudo systemctl enable --now サービス名.service
サービスを有効化すると同時に即座に起動します。
全サービスの一覧表示
systemctl list-units --type=service
現在読み込まれているすべてのサービスの状態を表示します。
ログの確認方法(journalctl)
systemdのサービスは、journalctlを使ってログを確認できます。
特定のサービスのログを表示
sudo journalctl -u サービス名.service
指定したサービスのログのみを表示します。
最新のログをリアルタイムで表示
sudo journalctl -u サービス名.service -f
tail -fのように、ログをリアルタイムで監視します。
指定した行数のログを表示
sudo journalctl -u サービス名.service -n 50
最新50行のログを表示します。
日時を指定してログを表示
sudo journalctl -u サービス名.service --since "2024-01-01" --until "2024-01-02"
指定した期間のログを表示します。
優先度を指定してログを表示
sudo journalctl -u サービス名.service -p err
エラーレベル以上のログのみを表示します。優先度はemerg、alert、crit、err、warning、notice、info、debugが指定できます。
トラブルシューティング
サービスが正常に動作しない場合の確認方法を紹介します。
ステップ1: 状態の確認
sudo systemctl status サービス名.service
まずはサービスの状態を確認します。Active: failedと表示されている場合は、サービスの起動に失敗しています。
ステップ2: ログの確認
sudo journalctl -u サービス名.service -n 100
直近のログを確認し、エラーメッセージを探します。
ステップ3: 設定ファイルの検証
sudo systemd-analyze verify /etc/systemd/system/サービス名.service
ユニットファイルの構文エラーをチェックします。
ステップ4: 実行権限の確認
ls -l /path/to/script
スクリプトに実行権限が付与されているか確認します。
ステップ5: スクリプトの単体テスト
sudo /path/to/script
スクリプトを直接実行して、エラーが発生しないか確認します。
よくあるエラーと対処法
- スクリプトに実行権限がない:
chmod +xで権限を付与 - User指定が不適切: root権限が必要な処理は
User=を削除
- ExecStartのパスが間違っている: 絶対パスを使用
- スクリプトのshebang行が間違っている:
#!/bin/bashを確認
- Type指定が不適切: 継続的に動作させる場合は
Type=simpleを使用 - スクリプトが即座に終了している: ループ処理を追加
まとめ
今回は、RHEL系Linuxにおけるsystemdサービスの作成から管理方法まで、基礎から実践的な内容を解説しました。
- 自作サービスは
/etc/systemd/system/に配置する - ユニットファイルは[Unit]、[Service]、[Install]の3セクションで構成
- 定期実行にはsystemdタイマーの使用が推奨される
- 基本コマンド(start、stop、restart、status、enable、disable)を使いこなす
- ログ確認にはjournalctlを活用する
- root権限が不要な処理は専用ユーザーで実行する
systemdサービスを適切に作成・管理することで、システムの安定運用と自動化を実現できます。実務でスクリプトをサービス化する際は、この記事の内容を参考に、段階的に実装してみてください。
まずは簡単なスクリプトから始めて、徐々に複雑な処理をサービス化していくことで、systemdの理解が深まっていきます。ログの確認とトラブルシューティングの方法を身につけることで、問題が発生した際にも冷静に対処できるようになります。

コメント