Common Systemd Configuration Errors and How to Fix Them

Systemd の設定エラーは、重要なサービスを停止させる可能性があります。このガイドでは、ユニットファイルでよく遭遇する落とし穴に対する実践的な解決策を提供します。実行パスのエラーを修正する方法、`After=` および `Wants=` を使用して重要な依存関係の順序を管理する方法、および `forking` や `simple` のようなサービスタイプを適切に設定する方法を学びます。パーミッションや欠落している変数などの環境問題の修正方法、および Linux サービスが確実に信頼性高く実行されるようにするための `systemctl daemon-reload` を使用した不可欠なデバッグワークフローと包括的な `journalctl` コマンドについて詳述します。

37 ビュー

Systemd の一般的な設定エラーとその修正方法

Systemd は、システムの初期化、サービス、依存関係、リソースの管理を担当する、現代の Linux ディストリビューションの基盤です。強力ですが、ユニットファイルにおける軽微な設定ミスが、重大なサービス障害、煩わしい起動遅延、複雑なトラブルシューティングセッションにつながる可能性があります。

この記事は、最も一般的な systemd 設定の落とし穴を特定し、解決するための実践的なガイドとして役立ちます。構文エラー、パスの問題、重要な依存関係の順序付けミス、環境コンテキストの問題をカバーし、サービスが常に確実に起動するように、明確で実行可能な手順を提供します。


1. ユニットファイルにおける構文エラーとパスエラー

サービス障害の最も頻繁な原因の 1 つは、ユニットファイル内での単純なタイプミスや不正確に定義されたパスです。

Exec コマンドにおける不正確または非絶対パス

Systemd はコマンド実行に厳格です。Path= ディレクティブが明示的に定義されていない限り、systemd は標準のシェルセッションから期待される可能性のある環境変数(PATH など)を継承しないことがよくあります。すべての実行可能コマンドは絶対パスを使用する必要があります。

エラー:

コマンドの場所を指定せずにコマンド名を使用する。

[Service]
ExecStart=my-app-server --config /etc/config.yaml

my-app-server/usr/local/bin にある場合、systemd はそれを見つけられない可能性が高いです。

修正方法:

常に実行可能ファイルへの完全な絶対パスを使用します。

[Service]
ExecStart=/usr/local/bin/my-app-server --config /etc/config.yaml

ヒント: ExecStart を設定する前に、シェルで which [command_name] を使用してパスを確認してください。

タイプミスと大文字小文字の区別

Systemd の設定ディレクティブは大文字と小文字を区別し、正しいセクション([Unit][Service][Install])に配置する必要があります。スペルミスや大文字小文字の誤りは、サービスがロードに失敗したり、予期しない動作を示したりする原因となります。

エラー例:

[Service]
ExecStart=/usr/bin/python3 app.py
RestartAlways=true  ; Restart=always にすべき

修正方法:

すべてのディレクティブが systemd のドキュメント形式に厳密に従っていることを確認してください。デーモンをリロードする前に、基本的な構文チェックを実行するために systemd-analyze verify <unit_file> コマンドを使用してください。

$ systemd-analyze verify /etc/systemd/system/my-service.service

2. サービス依存関係と順序の誤管理

依存関係は、サービスが を必要とするかを定義し、順序は いつ それらのリソースが利用可能でなければならないかを定義します。

RequiresWants の混同

これらのディレクティブは依存関係を定義するために使用されますが、失敗の処理方法が異なります。

  • Wants=: 弱い依存関係。要求されたユニットが失敗するか、開始しない場合、現在のユニットは引き続き開始を試みます。これはクリティカルではない依存関係に使用します。
  • Requires=: 強い依存関係。要求されたユニットが失敗した場合、現在のユニットは開始しません(そして、要求されたユニットが後で失敗した場合、既に実行中であれば停止されます)。

Requires に依存し、適切な順序付けを行わない

依存関係(例: Requires=network.target)を定義するだけでは、依存関係が開始されることが保証されるだけです。サービスが開始を試みる前に、依存関係が 完全に初期化されている ことが保証されるわけではありません。

エラー:

Web サーバーは起動しますが、ネットワークスタックがまだ初期化中のため、データベース接続が失敗します。

修正方法: After=Before= の使用

順序を強制するには、After=(または Before=)を使用する必要があります。一般的な要件は、続行する前にネットワークが完全にアップし、構成されていることを確認することです。

[Unit]
Description=My Web Application Service
Wants=network-online.target
After=network-online.target  ; これにより順序が保証される

[Service]
...

ベストプラクティス: ストレージやネットワークなどのシステムリソースに依存するほとんどのアプリケーションサービスでは、常に Wants= または Requires= ディレクティブと対応する After= ディレクティブを組み合わせて使用してください。

サービスタイプの誤管理

Systemd サービスには、Type= ディレクティブによって管理されるいくつかの実行タイプがあります。これを誤って設定すると、サービスが一時的に起動してからすぐに失敗する一般的な原因となります。

エラー: Type=forking の誤用

アプリケーションがフォアグラウンドで実行され、単一のメインプロセスを維持するように設計されている場合(ほとんどの最新アプリケーションはこのモデルを使用します)、Type=forking を設定すると、最初の親プロセスが終了するとすぐに、systemd はサービスが正常に開始して終了したと見なします。その後、systemd は 実際の バックグラウンド子プロセスを終了させます。

修正方法:

  1. 最新のアプリケーションの場合: Type=simple を使用します。これはデフォルトであり、ExecStart プロセスがメインプロセスであることを期待します。
  2. デーモン化(フォーク)するレガシーアプリケーションの場合: Type=forking を設定し、そして極めて重要なこととして PIDFile= ディレクティブを定義して、systemd がフォークを生き残った子プロセスを追跡できるようにします。
[Service]
Type=forking
PIDFile=/var/run/legacy-app.pid
ExecStart=/usr/sbin/legacy-app

3. 環境とユーザーコンテキストの問題

サービス障害は、サービスがアプリケーションが期待するものとは異なるコンテキストで実行されることから生じることがよくあります。これは通常、権限または環境変数に関連しています。

権限拒否またはファイルの欠落

アプリケーションを手動でテストする場合、通常は適切な権限を持つユーザーアカウントで実行されます。systemd によって実行される場合、多くの場合、デフォルトでルートユーザーまたはユニットファイルで指定されたユーザーになります。

エラー:

アプリケーションがログを書き込めない、設定ファイルにアクセスできない、またはローポートにバインドできない。

修正方法:

  1. 非ルートユーザーの定義: 常に専用の低権限ユーザーとグループをサービスに指定します。

    ini [Service] User=www-data Group=www-data ...

  2. 所有権の確認: サービスの作業ディレクトリ、ログファイル、設定ファイルが指定された User=Group= によって所有されていることを確認します。

    bash sudo chown -R www-data:www-data /var/www/my-app

環境変数の欠落

Systemd サービスは最小限の環境で実行されます。重要な環境変数(API キー、データベース接続文字列、カスタムライブラリパスなど)はすべて明示的に渡す必要があります。

修正方法: Environment= または EnvironmentFile= の使用

単純な変数の場合は Environment= を使用します。

[Service]
Environment="APP_PORT=8080"
Environment="API_KEY=ABCDEFG"

複雑または多数の変数の場合は、標準の .env ファイルを指す EnvironmentFile= を使用します。

[Service]
EnvironmentFile=/etc/default/my-app.conf

4. 重要なデバッグワークフロー

最も一般的な設定エラーは、ユニットファイルを編集してからサービスを再起動しようとする間の重要なステップを忘れることです。

デーモンのリロードを忘れる

Systemd はユニットファイルの変更を自動的に監視しません。/etc/systemd/system/ 内のファイルを変更した後は、systemd マネージャーに設定キャッシュをリロードするように指示する必要があります。

エラー:

ファイルを編集し、systemctl restart my-service を実行しても、古い設定が引き続き使用される。

修正方法: daemon-reload を実行する

ユニットファイルの変更を保存した直後に、必ずこのコマンドを実行してください。

sudo systemctl daemon-reload
sudo systemctl restart my-service

ログツールの効果的な使用

サービスが失敗した場合は、正確な診断のために公式ツールに頼ってください。

  1. サービスステータスの確認: これにより、即時の状態、終了コード、および最後の数行のログが表示されます。

    bash systemctl status my-service.service

  2. ジャーナルの検査: ジャーナルには、サービスの包括的な出力(stdout/stderr)が含まれています。「Permission denied」や「No such file or directory」などの手がかりを探してください。

    ```bash

    ユニット固有の最近のログを表示

    journalctl -u my-service.service --since '1 hour ago'

    ログを表示し、リアルタイムで出力を追跡

    journalctl -f -u my-service.service
    ```

まとめと次のステップ

Systemd 設定エラーの解決は、構文の遵守、絶対パス、および規律あるデバッグワークフローにかかっています。常に After= を使用して正確なサービス順序を定義し、適切なセキュリティコンテキスト(User=/Group=)を指定し、サービスタイプを正しく管理することを忘れないでください。

問題が解決しない場合は、既知の良好なテンプレートと比較してユニットファイルを確認し、トラブルシューティングを開始する際は常に sudo systemctl daemon-reload を実行し、その後 systemctl status および journalctl によって提供される出力を注意深くレビューしてください。