Systemdサービス障害のトラブルシューティング:ステップバイステップガイド
Systemdは、ほとんどの最新のLinuxディストリビューションの事実上のシステムおよびサービス管理マネージャーとなり、サービス、デーモン、プロセスの管理において極めて重要な役割を果たしています。強力かつ効率的ですが、systemdによって管理されているサービスが起動に失敗することがあり、アプリケーションのダウンタイムやシステム不安定化を引き起こす可能性があります。これらの障害を診断するには、systemdの堅牢なロギング機能とイントロスペクション機能を利用した体系的なアプローチが必要です。
このガイドでは、一般的なsystemdサービス起動障害をトラブルシューティングするための包括的なステップバイステップの手順を紹介します。初期ステータスチェックからログの詳細な調査、ユニットファイルの確認、複雑な依存関係の問題の解決まで、すべてを網羅します。この記事の終わりまでに、ほとんどのsystemdサービス障害を効率的に診断し解決し、アプリケーションとサービスがスムーズに実行され続けるようにするための実践的な知識とツールを習得しているでしょう。
第一の防衛線: systemctl status
サービスが起動に失敗した場合、最初に実行すべきコマンドはsystemctl status <service_name>です。このコマンドは、サービスがアクティブか、ロードされているか、そして最も重要なこととして、最近のログのスニペットを含む、サービスの現在の状態のスナップショットを提供します。これは、問題を迅速に特定するのに十分な情報を提供することがよくあります。
Webアプリケーションサービスであるmywebapp.serviceが起動していないと仮定しましょう。
systemctl status mywebapp.service
出力例の解釈:
● mywebapp.service - My Web Application
Loaded: loaded (/etc/systemd/system/mywebapp.service; enabled; vendor preset: disabled)
Active: failed (Result: exit-code) since Mon 2023-10-26 10:30:05 UTC; 10s ago
Process: 12345 ExecStart=/usr/local/bin/mywebapp-start.sh (code=exited, status=1/FAILURE)
Main PID: 12345 (code=exited, status=1/FAILURE)
CPU: 10ms
Oct 26 10:30:05 hostname systemd[1]: Started My Web Application.
Oct 26 10:30:05 hostname mywebapp-start.sh[12345]: Error: Port 8080 already in use
Oct 26 10:30:05 hostname systemd[1]: mywebapp.service: Main process exited, code=exited, status=1/FAILURE
Oct 26 10:30:05 hostname systemd[1]: mywebapp.service: Failed with result 'exit-code'.
この出力から、すぐに次のことがわかります。
* サービスmywebapp.serviceはfailed(失敗)しています。
* Result: exit-codeで失敗しており、ExecStartコマンドがゼロ以外のステータスで終了したことを意味します。
* Process行は、mywebapp-start.shスクリプトがstatus=1/FAILUREで失敗したことを示しています。
* 最も重要なこととして、ログ行にはError: Port 8080 already in useと表示されています。これは問題の明確な指標です。
このコマンドは、原因を直接示唆するか、次に調査すべき場所を絞り込むための最初の診断ツールです。
journalctlによる詳細な調査
systemctl statusが簡単な概要を提供するのに対し、journalctlは詳細なロギングのための頼りになるコマンドです。これは、サービスを含むシステム全体からのログを収集するsystemdジャーナルを照会します。
基本的なログの確認
履歴エントリを含む特定のサービスに関するすべてのログを表示するには:
journalctl -u mywebapp.service
これにより、mywebapp.serviceに関連するすべてのログエントリが表示されます。サービスが繰り返し失敗している場合、失敗した試行ごとにエントリが表示されます。
フィルタリングと時間ベースのクエリ
特に最近の障害の後で結果を絞り込むには、--sinceや--priorityのようなフラグを使用できます。
- 特定時刻以降のログの表示:
bash journalctl -u mywebapp.service --since "10 minutes ago" journalctl -u mywebapp.service --since "2023-10-26 10:00:00" - エラーレベル以上のメッセージのみの表示:
bash journalctl -u mywebapp.service -p err - 拡張された説明と冗長な出力のために
-xeと組み合わせる:
bash journalctl -u mywebapp.service -xe --since "5 minutes ago"
これは、journalctl -xeが特定のログメッセージの説明や利用可能な場合はスタックトレースなどの追加のコンテキストを提供するため、非常に役立ちます。
ログメッセージの理解
Error、Failed、Warning、または何が問題だったかを示すアプリケーション固有のメッセージなどのキーワードを探します。障害に至るイベントの順序を理解するために、タイムスタンプに注意を払います。
ヒント: サービスのExecStartスクリプトが標準出力または標準エラーに出力する場合、それらのメッセージは通常journalctlによってキャプチャされます。スクリプトが説明的なエラーメッセージを記録するようにしてください。
ユニットファイルの検査:サービスの設計図
すべてのsystemdサービスはユニットファイル(例:mywebapp.service)によって定義されます。このファイル内の設定ミスは、起動障害の一般的な原因です。サービスが何を実行しようとしているのかを理解する必要があります。
ユニットファイルの取得
サービスのアクティブなユニットファイルを表示するには:
systemctl cat mywebapp.service
このコマンドは、systemdが使用している正確なユニットファイル(オーバーライドを含む)を表示します。
確認すべき主要なディレクティブ
実行関連の問題については[Service]セクションに、依存関係については[Unit]セクションに焦点を当てます。
ExecStart: systemdがサービス起動のために実行するコマンドです。パスが正しいか、またコマンド自体が実行可能であり、指定されたUserとして手動で呼び出された場合に正常に実行されるかを確認します。
ini ExecStart=/usr/local/bin/mywebapp-start.shType: プロセス起動のタイプを定義します。一般的なタイプには次のようなものがあります。simple(デフォルト):ExecStartがメインプロセスです。forking:ExecStartが子プロセスをフォークし、親プロセスは終了します。systemdは親プロセスが終了するのを待ちます。oneshot:ExecStartが実行され終了します。コマンドが実行されている限り、systemdはこのサービスがアクティブであると見なします。notify: サービスが準備完了になったときにsystemdに通知を送信します。- 不適切な
Type設定は、サービスが実際には起動しているのにsystemdが失敗したと見なしたり、その逆にさせたりする可能性があります。
User/Group: サービスが実行されるユーザーとグループです。サービスがこのユーザーの下でアクセス権限のないファイルやリソースにアクセスしようとすると、権限の問題が発生することがよくあります。
ini User=mywebappuser Group=mywebappgroupWorkingDirectory: サービスが実行されるディレクトリです。ExecStartやその他のコマンド内の相対パスはこれに依存します。Restart: サービスがいつ再起動されるかを定義します。on-failureまたはalwaysに設定されている場合、失敗したサービスが絶えず再起動し、最初の失敗を捉えるのが難しくなることがあります。TimeoutStartSec/TimeoutStopSec: systemdがサービスが起動または停止するのを待つ時間です。サービスが初期化にTimeoutStartSecよりも長くかかる場合、systemdはそのプロセスを強制終了し、失敗を報告します。
一般的なユニットファイルの問題
- 不正確なパス:
ExecStartやその他のファイルパスのスペルミス。 - 欠落している
Environment変数: サービスは、systemdのクリーンな環境では存在しない可能性のある特定の環境変数(例:PATH)を必要とすることがよくあります(下記参照)。 - 権限: 指定された
Userがスクリプトの実行権限、または必要なデータファイルへの読み書き権限を持っていない。 - 構文エラー: ユニットファイル自体の単純なタイプミス。
ExecStartを手動でテストするには:
サービスのユーザーに切り替えて、コマンドを直接実行してみてください。
sudo -u mywebappuser /usr/local/bin/mywebapp-start.sh
これは、journalctlで表示されたエラーをターミナルに直接再現することが多く、デバッグが容易になります。
依存関係の管理:サービスが単独で起動できない場合
サービスは、それ自体が起動する前に、他のサービスやシステムコンポーネントがアクティブであることをしばしば必要とします。Systemdは、これらの依存関係を管理するためにWants、Requires、After、およびBeforeディレクティブを使用します。
依存関係の特定
サービスが明示的に必要とするもの、または起動したいものを確認するには、systemctl list-dependencies <service_name>を使用します。
systemctl list-dependencies mywebapp.service
[Unit]セクションの一般的なディレクティブ:
After=: このサービスが指定されたユニットの後に起動する必要があることを指定します。指定されたユニットが失敗した場合でも、このサービスは起動を試みます(Requires=が使用されていない限り)。Requires=: このサービスが指定されたユニットを必要とすることを指定します。必要とされるユニットのいずれかが起動に失敗した場合、このサービスは起動しません。Wants=:Requires=の弱い形式です。必要とされているユニットが失敗した場合でも、このサービスは起動を試みます。
例:
[Unit]
Description=My Web Application
After=network.target mysql.service
Requires=mysql.service
ここでは、mywebapp.serviceはnetwork.targetとmysql.serviceが起動した後にのみ起動し、mysql.serviceの成功を必要とします。mysql.serviceが失敗した場合、mywebapp.serviceは起動しません。
依存関係の競合の解決
依存関係の問題でサービスが失敗した場合、journalctlは通常、どの依存関係が満たされなかったかを示します。たとえば、mysql.serviceの失敗に関する詳細に続いて、My Web Applicationの依存関係が失敗しましたといったメッセージが表示されることがあります。
解決手順:
1. 依存サービスを確認する: まずその失敗をトラブルシューティングするために、systemctl status <dependent_service>(例:systemctl status mysql.service)とjournalctl -u <dependent_service>を実行します。
2. After=とRequires=ディレクティブを確認する: これらが意図した起動順序と厳密さを正しく反映していることを確認します。場合によっては、サービスが単にサービスがアクティブであるだけでなく、特定のポートが開くのを待つ必要があります。複雑なケースでは、systemd-socket-activateやカスタムのExecStartPreスクリプトが役立つことがあります。
環境変数とパス:隠れた落とし穴
Systemdサービスは、非常にクリーンで最小限の環境で実行されます。これにより、ユーザーのシェルでは完璧に動作するコマンドが、重要な環境変数(例:PATH)が欠落しているためにsystemdによって実行されると失敗するという問題が頻繁に発生します。
Systemdのクリーンな環境
systemdがサービスを起動するとき、systemctl startを実行したユーザーの完全な環境を継承しません。例えば、PATH変数はしばしば削減され、pythonやnodeのようなコマンドが/usr/binや/binのような標準的な場所にあれば見つからない可能性があります。
症状: ExecStart=/usr/local/bin/myscript.shが「