Systemdサービスファイルの習得:包括的なガイド
Systemdは、ほとんどのモダンなLinuxディストリビューションにおいて、サービスやシステムプロセスを管理するための事実上の標準となっています。systemdサービスユニットファイルの作成と管理方法を理解することは、アプリケーションを確実にデプロイし、保守しようとするシステム管理者や開発者にとって不可欠です。このガイドでは、systemdサービスファイルの基本構文から高度な設定まで、その要点を解説し、Linuxサービスを効果的に管理できるようになることを目指します。
この記事では、systemdサービスユニットファイルをゼロから作成し、設定することに焦点を当てています。基本的な構文を網羅し、一般的で不可欠なディレクティブを探求し、堅牢なサービス管理のためのベストプラクティスについて議論します。このガイドを読み終える頃には、独自のsystemdサービスファイルを作成し、アプリケーションがスムーズかつ確実に動作するようにするための知識を身につけているでしょう。
Systemdユニットファイルを理解する
Systemdは、サービス、ソケット、デバイス、マウントポイントなど、さまざまなシステムリソースを記述するためにユニットファイルを使用します。.service 拡張子で終わるサービスユニットファイルは、systemdが特定のデーモンやアプリケーションをどのように管理すべきかを定義します。
これらのファイルはセクションに整理されており、各セクションには設定ディレクティブを表すキーと値のペアが含まれています。主要なセクションは、[Unit]、[Service]、および[Install]です。
Systemdサービスファイルの構造
典型的なsystemdサービスファイルは、次の構造を持っています:
[Unit]
Description=A brief description of the service.
After=network.target
[Service]
Type=simple
ExecStart=/usr/local/bin/my_application --config /etc/my_app.conf
Restart=on-failure
User=myuser
Group=mygroup
[Install]
WantedBy=multi-user.target
各セクションと、その一般的なディレクティブについて詳しく見ていきましょう:
[Unit]セクション
このセクションは、ユニットに関するメタデータを提供し、他のユニットとの関係を定義します。依存関係と順序付けに使用されます。
Description=: サービスの人間にわかりやすい名前。これはsystemctl statusの出力に表示されます。Documentation=: サービスのドキュメントへのURLまたはパス。Requires=: 強い依存関係を定義します。ここにリストされているユニットの起動に失敗した場合、このユニットも起動に失敗します。Wants=: 弱い依存関係を定義します。ここにリストされているユニットの起動に失敗した場合でも、このユニットは起動を試みます。Before=: このユニットがリストされているユニットより前に起動することを保証します。After=: このユニットがリストされているユニットより後に起動することを保証します。これは非常によく使われ、例えばAfter=network.targetは、サービスが起動する前にネットワークが利用可能であることを保証します。Conflicts=: ここにリストされているユニットが起動された場合、このユニットは停止され、逆もまた同様です。
[Service]セクション
このセクションは、サービス自体の動作を設定します。プロセスの起動、停止、管理方法をここで定義します。
-
Type=: プロセスの起動タイプを指定します。一般的な値は次のとおりです:simple(デフォルト):ExecStart=で指定されたものがメインプロセスです。systemdは、ExecStart=プロセスがフォークされた直後にサービスが開始されたと見なします。forking:ExecStart=プロセスが子をフォークし、親が終了します。systemdは、親が終了したときにサービスが開始されたと見なします。このタイプでは、PIDFile=を指定する必要があることがよくあります。oneshot:simpleに似ていますが、プロセスは作業完了後に終了することが期待されます。セットアップスクリプトに役立ちます。notify: デーモンは、正常に起動したときにsystemdに通知メッセージを送信します。これは、それをサポートするモダンなデーモンにとって推奨されるタイプです。dbus: サービスがD-Bus名を取得します。
-
ExecStart=: サービスを開始するために実行するコマンド。これは最も重要なディレクティブです。複数のExecStart=行を持つことができ、それらは順次実行されます。 ExecStop=: サービスを停止するために実行するコマンド。ExecReload=: サービスの設定を再起動せずにリロードするために実行するコマンド。-
Restart=: サービスが自動的に再起動されるべきタイミングを定義します。一般的な値は次のとおりです:no(デフォルト): 再起動しません。on-success: サービスが正常に終了した場合のみ再起動します (終了コード0)。on-failure: サービスがゼロ以外の終了コードで終了した場合、シグナルによって終了された場合、またはタイムアウトした場合に再起動します。on-abnormal: シグナルによって終了された場合、またはタイムアウトした場合に再起動します。on-abort: シグナルによって不適切に終了した場合のみ再起動します。always: 終了ステータスに関係なく、常に再起動します。
-
RestartSec=: サービスを再起動する前に待機する時間 (デフォルトは100ms)。 User=: サービスを実行するユーザー。Group=: サービスを実行するグループ。WorkingDirectory=: コマンドを実行する前に変更するディレクトリ。Environment=: サービスの環境変数を設定します。EnvironmentFile=: ファイルから環境変数を読み込みます。PIDFile=: PIDファイルへのパス (しばしばType=forkingと併用されます)。StandardOutput=/StandardError=: 標準出力/標準エラーの出力先を制御します (例:journal、syslog、null、inherit)。journalがデフォルトであり、ロギングに強く推奨されます。
[Install]セクション
このセクションは、ユニットがどのように有効化または無効化されるべきかを定義します。通常、シンボリックリンクを作成することによって行われます。
WantedBy=: このサービスが有効化されたときに、このサービスを「必要とする」ターゲットを指定します。一般的な値は次のとおりです:multi-user.target: システムがマルチユーザーコマンドラインの状態に達したときに起動すべきサービス用。graphical.target: システムがグラフィカルログインの状態に達したときに起動すべきサービス用。
初めてのSystemdサービスファイルの作成
/opt/my_app/my_app.py に配置された架空のPythonスクリプト my_app.py のためのシンプルなサービスファイルを作成しましょう。
1. サービスファイルの作成:
カスタムアプリケーション用のサービスファイルは通常、/etc/systemd/system/ に配置されます。ここでは、ファイル名を my_app.service としましょう。
# Create the directory if it doesn't exist
sudo mkdir -p /etc/systemd/system/
# Create the service file using a text editor
sudo nano /etc/systemd/system/my_app.service
2. my_app.service に以下の内容を追加します:
[Unit]
Description=My Custom Python Application
After=network.target
[Service]
Type=simple
User=appuser
Group=appgroup
WorkingDirectory=/opt/my_app/
ExecStart=/usr/bin/python3 /opt/my_app/my_app.py
Restart=on-failure
[Install]
WantedBy=multi-user.target
例の説明:
Description: アプリケーションを明確に識別します。After=network.target: 起動前にネットワークが利用可能であることを保証します。Type=simple:my_app.pyがメインプロセスであり、フォークしないことを前提とします。User=appuser,Group=appgroup: アプリケーションを実行するユーザーとグループを指定します。これらのユーザーとグループがシステム上に存在し、適切な権限を持っていることを確認してください。 必要に応じて作成する必要があります:
bash sudo groupadd appgroup sudo useradd -r -g appgroup appuser sudo chown -R appuser:appgroup /opt/my_app/WorkingDirectory: スクリプトの実行コンテキストを設定します。ExecStart: Pythonスクリプトを実行するコマンド。/usr/bin/python3が正しいPythonインタプリタへのパスであり、スクリプトが実行可能であることを確認してください。Restart=on-failure: スクリプトがクラッシュした場合、systemdは再起動を試みます。WantedBy=multi-user.target: このサービスは、システムがマルチユーザー環境で起動したときに自動的に開始されます。
3. systemdマネージャー設定のリロード:
サービスファイルを作成または変更した後、systemdに設定をリロードするよう指示する必要があります。
sudo systemctl daemon-reload
4. サービスの有効化と起動:
- 有効化 (Enable): これにより、サービスは起動時に自動的に開始されるようになります。
bash sudo systemctl enable my_app.service - 起動 (Start): これにより、サービスはすぐに開始されます。
bash sudo systemctl start my_app.service
5. サービスステータスの確認:
サービスが実行中であるか、および潜在的なエラーを確認するには:
sudo systemctl status my_app.service
問題がある場合、status コマンドは journald からのエラーメッセージやログを表示することがよくあります。
6. ログの表示:
Systemdはログ記録のためにjournaldと統合されています。サービスのログは以下を使用して表示できます:
sudo journalctl -u my_app.service
リアルタイムでログを追跡することもできます:
sudo journalctl -f -u my_app.service
その他の便利なコマンド:
- サービスの停止:
sudo systemctl stop my_app.service - サービスの再起動:
sudo systemctl restart my_app.service - 設定のリロード (アプリがサポートしている場合):
sudo systemctl reload my_app.service - 起動時の自動開始を無効化:
sudo systemctl disable my_app.service
高度な設定とベストプラクティス
セキュリティに関する考慮事項:
- サービスを非rootユーザーとして実行する:絶対に必要な場合を除き、常に
User=とGroup=を指定してください。これは最小権限の原則に従います。 - サービスを分離する:セキュリティを強化するために、
PrivateTmp=true、ProtectSystem=true、NoNewPrivileges=trueなどのサンドボックス機能の使用を検討してください。PrivateTmp=true: サービスに独自のプライベートな/tmpおよび/var/tmpディレクトリを提供します。ProtectSystem=true:/usr、/boot、/etcを読み取り専用にします。NoNewPrivileges=true: サービスが新たな権限を獲得するのを防ぎます。
複雑な起動の処理:
Type=forkingとPIDFile=の併用:フォークする古いアプリケーションの場合、PIDFile=が正しいファイルを指していることを確認してください。Type=notify: アプリケーションがサポートしている場合、これはsystemdがサービスが本当に準備できたことを知るための最も堅牢な方法です。ExecStartPre=とExecStartPost=:ExecStart=の前後で実行されるコマンド。セットアップやクリーンアップのタスクに役立ちます。
リソース制御:
Systemdでは、リソース使用量を制限できます:
CPUShares=: 相対的なCPU時間割り当て。MemoryLimit=: サービスが使用できる最大メモリ。IOWeight=: 相対的なI/O帯域幅。
例:
[Service]
# ... other directives ...
MemoryLimit=512M
CPUShares=512 # Roughly 50% of CPU time compared to default 1024
タイマーとCronの比較
Systemdタイマーは、従来のcronジョブに代わるモダンな代替手段を提供します。これらはより柔軟で、systemdのロギングおよび依存関係管理とよりよく統合されます。
- Cron:
crontabファイルで定義されるスケジュールされたタスク。 - Systemdタイマー (
.timerユニット): これらのユニットは.serviceユニットをスケジュールします。対応する.serviceファイルがいつ実行されるべきかを指定する.timerファイルを定義します。
例:
毎日午前3時にスクリプトを実行するには:
-
my_script.service: 実行するサービス。
```ini
[Unit]
Description=My daily script[Service]
Type=oneshot
ExecStart=/opt/my_scripts/run_daily.sh
User=scriptuser
``` -
my_script.timer: サービスをスケジュールするタイマー。
```ini
[Unit]
Description=Run my daily script once a day[Timer]
Run at 03:00 every day
OnCalendar=--* 03:00:00
Persistent=true # Run immediately if missed due to downtime[Install]
WantedBy=timers.target
```
使用方法:
- 両方のファイルを
/etc/systemd/system/に配置します。 sudo systemctl daemon-reloadを実行します。- タイマーを有効化して起動します:
sudo systemctl enable my_script.timerとsudo systemctl start my_script.timer。
タイマーには、Persistent=true (システム起動時に未実行のジョブを実行)、カレンダーイベント (例: hourly、daily、weekly)、およびjournalctlとのより良い統合などの利点があります。
一般的な問題のトラブルシューティング
- サービスが起動しない場合:
systemctl status <service_name>とjournalctl -u <service_name>を確認してください。タイプミス、パスの誤り、依存関係の不足、権限エラーなどを探してください。 Type=の誤り:サービスがすぐに失敗するかハングアップする場合、Type=が間違っている可能性があります。simpleまたはforkingを試して、forkingを使用している場合はPIDFileが正しいことを確認してください。- アクセス拒否:指定された
User=とGroup=が必要なファイルとディレクトリへの読み取り/書き込みアクセス権を持っていることを確認してください。 - 環境変数:アプリケーションが特定の環境変数に依存している場合、
Environment=またはEnvironmentFile=を使用して正しく設定されていることを確認してください。 - 依存関係:
After=とRequires=ディレクティブが正しく設定されており、サービスが起動する前に前提条件が満たされていることを確認してください。
まとめ
Systemdサービスファイルは、Linux上でアプリケーションを管理するための強力なツールです。ユニットファイルの構造、主要なディレクティブの目的、および設定のベストプラクティスを理解することで、サービスの信頼性、セキュリティ、および管理性を大幅に向上させることができます。シンプルなスクリプトを展開する場合でも、複雑なアプリケーションを展開する場合でも、systemdサービスファイルを習得することは、現代のLinuxシステム管理にとって不可欠なスキルです。
サービスファイルは常に徹底的にテストし、デバッグにはsystemctl statusとjournalctlを使用し、systemdが提供するセキュリティ機能を活用することを忘れないでください。