web-dev-qa-db-ja.com

systemdがサービスの開始中に戻りコードを無視する

1つの単純なデーモンのユニットファイルを作成しているときに、この問題に遭遇しました。起動時にデーモンが「1」を返すと、systemdはそれを無視し、実際に死んでいる間にデーモンが正常に起動したように見えます。

たとえば、非常に単純なシェルスクリプトがあります。

#!/bin/bash
exit 1

したがって、unit-fileは次のようになります。

[Unit]
Description=test service
After=syslog.target

[Service]
User=testuser
Group=testuser
ExecStart=/usr/local/bin/return1

[Install]
WantedBy=multi-user.target

開始しようとすると、問題ないようです:

# service testservice start
# echo $?
0

しかし、実際には死んでいます:

# service testservice status
● testservice.service - test service
   Loaded: loaded (/etc/systemd/system/testservice.service; enabled)
   Active: failed (Result: exit-code) since Fri 2016-01-22 14:51:45 MSK; 1min 13s ago
  Process: 16416 ExecStart=/usr/local/bin/return1 (code=exited, status=1/FAILURE)
 Main PID: 16416 (code=exited, status=1/FAILURE)

Jan 22 14:51:45 servername systemd[1]: Started test service.
Jan 22 14:51:45 servername systemd[1]: testservice.service: main process exited, code=exited, status=1/FAILURE
Jan 22 14:51:45 servername systemd[1]: Unit testservice.service entered failed state.

Systemdはデーモンが正常に起動されたが、後でクラッシュしたと考えているようです。

サービスのタイプを「フォーク」などに変更してこの問題を解決しようとしました-これはゼロ以外のコードの場合は正常に機能しますが、サービスは実際には「シンプル」です。端末がビジーです。

この種のサービスをどのように管理しますか?または、デーモンコードで何かを修正する必要があるかもしれませんか?

OS debian 8 x64、systemd 215

6
Paul K.

systemdがプロセスが正常に開始されたかどうかを検出するには、Type=forkingを使用して、ヘルパースクリプトでプロセスをフォークし、そのスクリプトがプロセスが正常に開始されたかどうかをチェックインする必要があります。フォークすると、systemdExecStartコマンドが完了するまで待機し、終了ステータスをチェックします。

ユニットファイルは次のように変更する必要があります。

[Unit]
Description=test service
After=syslog.target

[Service]
Type=forking
User=testuser
Group=testuser
ExecStart=/usr/local/bin/fork_service

[Install]
WantedBy=multi-user.target

/usr/local/bin/fork_serviceでは、次のようになります。

#!/bin/bash

# Run your process in background
/path/to/your_service &

# Check if the services started successfully 
if ! kill -0 $! 2>/dev/null; then
    # Return 1 so that systemd knows the service failed to start
    exit 1
fi

ここでは、バックグラウンドプロセスのPIDがまだアクティブかどうかを確認していますが、必要に応じて任意のチェックを行うことができます。唯一重要なことは、このスクリプトは、プロセスが正常に開始された場合は0、失敗した場合はゼロ以外の正の値で終了することです。

また、プロセスをフォークするためにBashを使用する必要はありません。任意の言語を使用できます。

4

したがって、systemdがプロセスが初期化されるまで待ってから戻る必要があります。これは合理的な機能です。ただし、プロセスの初期化には任意の時間がかかる可能性があるため、開始されているプロセスの支援なしにsystemdが待機する適切な時間を知る方法はありません。

これを行う従来の方法は、フォークデーモンを使用することです。デーモンは1つのプロセスで初期化を行い、正常に初期化できることが確立されると、実際のプロセスをフォークします。フォークが発生し、元のプロセスが正常に終了すると、デーモンが初期化されたことをsystemdに通知します。 systemdのドキュメントから

Forkに設定されている場合、ExecStart =で構成されたプロセスは、起動の一部としてfork()を呼び出すことが期待されます。親プロセスは、起動が完了し、すべての通信チャネルが設定されると終了することが期待されています。子はメインサービスプロセスとして引き続き実行され、サービスマネージャーは親プロセスが終了したときにユニットが開始されたと見なします。これは、従来のUNIXサービスの動作です。この設定を使用する場合、systemdがサービスのメインプロセスを確実に識別できるように、PIDFile =オプションも使用することをお勧めします。 systemdは、親プロセスが終了するとすぐに、フォローアップユニットの開始に進みます。

Systemdは、私にとってよりエレガントに見える別のソリューションを提供します。 forkする理由はありません。初期化時にデーモンに直接systemdに通知させるだけです。これはType = notifyです。 (上記でリンクしたドキュメントを参照)

したがって、特定の例を修正するには、サービスファイルを変更して、Type=notifyおよび/usr/local/bin/return1となるようにします。

#!/bin/bash                                                                     
exit 1
systemd-notify READY=1

初期化が失敗した場合、明らかに出口1は通常条件付きで発生します。そして、初期化が完了すると、READY = 1が送信されます。

これを開始しようとすると、コマンドラインに予期されるエラーメッセージが表示されます。 systemdが実際にREADY = 1を待機していることを確認するには、次の/usr/local/bin/return1を使用して試してください。

#!/bin/bash                                                                     
sleep 3
systemd-notify READY=1
sleep 1000000
1
user3049102