サードパーティのプロキシを開始するシステムサービスユニットがあります。これを「サービスc」といいます。サービスデバイスはうまく動作しています。少なくとも私の知る限りでは!パッチサイクルの後、systemdは(予想どおり)サービスユニットを開始しますが、その後、サービスユニットが再起動されます。止めるサービスデバイスは正常に起動してから約2秒後に起動します。私はこのサービスが初めて成功したと信じるすべての理由を持っています。再起動後にログインすると、この時点でサービスが実際に実行されていないことがわかります。サービスユニット(systemctl start service-c
)を手動で開始でき、期待どおりにサービスが開始されます。
systemdがサービスユニットを停止する必要があると思う理由を知りたいです。 systemdが「停止」操作を実行する理由を確認するために何を設定または有効にできますか?
わかりましたシステムログレベルオプションデフォルトの「情報」の代わりに「デバッグ」に設定しました。
同様のアイデアはサービス単位ファイルに設定することですが、Environment=SYSTEMD_LOG_LEVEL=debug
特に必要はありません。提供するデバッグ中ですが、独自にシステム化されています。
サービスユニットは次のように構成されます。
# /etc/systemd/system/service-c.service
[Unit]
Description=service c
After=network-online.target local-fs.target
[Service]
Type=forking
ExecStart=/local-path/start.service-c
ExecStop=/local-path/stop.service-c
Restart=on-failure
[Install]
WantedBy=multi-user.target
...証拠は次のとおりです。
$ systemctl status service-c
● service-c.service - service c
Loaded: loaded (/etc/systemd/system/service-c.service; enabled; vendor preset: disabled)
Active: inactive (dead) since Wed 2021-04-07 17:49:30 EDT; 14h ago
Process: 3162 ExecStop=/local-path/stop.service-c (code=exited, status=0/SUCCESS)
Process: 1319 ExecStart=/local-path/start.service-c (code=exited, status=0/SUCCESS)
Main PID: 1478 (code=exited, status=0/SUCCESS)
/local-path
システム上のローカルディレクトリの難読化されたバージョン。
これは継続的な問題であるため、最後の再起動後に「stop」ラッパースクリプトを使用してプロセスの親ツリーを記録しました。pstree -a -A -l -p -s $$)
ログファイルは次のとおりです。
04/07/2021 17:49:19 stop.service-c:
systemd,1 --switched-root --system --deserialize 22
`-stop.service-c,3162 /local-path/stop.service-c
`-pstree,3178 -a -A -l -p -s 3162
...ここで、PID 3162はsystemdのストップスクリプト呼び出しに対応しています。 systemdがサービスのExecStopを呼び出すようです。
systemdは起動が完了してから約2秒後にサービスを停止します。エージェントのログファイルには、次のタイムスタンプがあります。
04/07/2021 17:49:12 start.service-c: Starting agent
04/07/2021 17:49:17 start.service-c: startup success
04/07/2021 17:49:19 stop.service-c: Executing from /agent/home as user
...で終わる...
04/07/2021 17:49:30 stop.service-c: Finished with RC=0
...systemdの「死」タイムスタンプ17:49:30に対応します。
"Restart = on-failure"ディレクティブはサービスを再起動しますが、systemdはサービスが正常に開始されたことを知らせます。
Apr 07 17:49:10 hostname systemd[1]: Starting service c...
Apr 07 17:49:17 hostname systemd[1]: Started service c.
サービスがきちんと起動し、systemdが試していないので再起動サービスでは、再起動パラメータが機能していないようです。
興味深いことに、Journalctlには対応する「Stop service ...」ログはありませんが(サービスを手動で停止したとき)、systemdがExecStopを呼び出すという証拠があります。
私は現在systemd 219を実行しています。
答え1
systemdがサービスユニットを停止する必要があると思う理由を知りたいです。 systemdが「停止」操作を実行する理由を確認するために何を設定または有効にできますか?
サービスのリアルタイム状態を表示するには、次のようにします。
- 次のコマンドを使用してください
systemd-cgls -l <service-cgroup-path>
。その時点のすべてのサービスプロセスが表示されます。サービスのcgroupパスはコマンドを使用して取得できますsystemctl show -p ControlGroup <service-name>
。最新バージョン(v219以外)では、サービスのcgroupパスの代わりに便利なオプションをsystemd
使用することもできます。-u <service-name>
systemd-cgls
- 詳細な洞察を得るには、非常に冗長な
systemctl show <service-name>
コマンドを使用できます。これは既知のサービス状態に関する多くの情報を提供し、そのsystemd
情報から何が起こっているかをより詳細に推論することができます。
ExecStop
「疑わしい停止」状況を調査するには、これらのコマンドをコマンドとして追加することが正しいです。あなたはそれらを追加するだけです最初に独自のstop.service-c
スクリプト(実際にスクリプトの場合)
ExecStop
または、独自の追加コマンドとして追加することもできます。今後あなたのstop.service-c
コマンドは次のとおりです。
[Service]
Type=forking
ExecStart=/local-path/start.service-c
ExecStop=-/bin/sh -c 'systemd-cgls -l -u %n && systemctl show %n'
ExecStop=/local-path/stop.service-c
Restart=on-failure
%n
指定子は、引用符付き文字列内に表示されても正しく処理されます。systemd
または、次のことができます。
[Service]
Type=forking
ExecStart=/local-path/start.service-c
ExecStop=-/usr/bin/systemd-cgls -l -u %n
ExecStop=-/bin/systemctl show %n
ExecStop=/local-path/stop.service-c
Restart=on-failure
また、-
理解できない理由で失敗した場合は、コマンドの終了ステータスが無視されるように、コマンドの前にプレフィックスを付けます。
もちろん、ExecStartPost
サービスが「成功的に開始された」と見なされるとすぐに、アクティブ状態を考慮するコマンドとして使用することもできますsystemd
。 (再びシャットダウン状態が無視またはsystemd
失敗した場合、サービス全体が中断されます。)
systemd-cgls
次に、実行コマンドの出力を表示するには、その時点でプロセスがまだ表示されていることをExecStop
確認する必要がありますMainPID
。表示されている場合、これはExecStop
実際にsystemd
提案されているように自律的に実行されたという証拠です。そうでない場合(MainPID
プロセスいいえsystemd-cgls
「停止した」時間に出力に表示されます。これはExecStop
実行されたことを意味します。だからプロセスMainPID
自体が終了します。(より多くの推論は以下を参照)。サービスプロセスのPID番号とExecStart
(現在は存在しない)コマンドのPID番号を記録して、サービスが開始されてから何が起こったかを推測することもできますfork(2)
。これは、サービスに関して非常に重要なためです。type=forking
うまく行動しているかどうかを評価します。(より多くの推論は以下を参照)。
systemctl show
コマンドで実行した結果に関してExecStop
注意すべき最も関連性の高い属性は次のとおりです。あなたの特定のケース例:
MainPID
:サービスのメインプロセスがそれ自体で終了したかどうかを読み取り0
、それ以外の場合はサービスのメインプロセスのPIDを読み込みます(まだ存在して実際に停止している場合)。systemd
ExecMainExitTimestamp
:サービスのメインプロセスが自分で終了した場合は、終了時刻を形式で読み込みますdate
。そうでなければ、プロセスがまだ生きていればまったく読み取られないので、実際に停止します。systemd
ExecMainExitTimestampMonotonic
:上記と同じですが、Linuxの単調時計を読み、0
プロセスがまだ生きているかどうかを読みます。ExecMainCode
:これは英語の単語に変換されるのではなく、シンボルの10進値を報告することを除いて、1の文字列に対応します。code=
このフィールドは、現在のシンボル値に基づいてプロセスがまだ生きているため、実際に停止するかどうかを読み取ります。 Linuxでは(最初から)、そうでなければ読み取りプロセスはそれ自体で行われます。 -edされた場合(明らかにこのユースケースでは)systemctl status
CLD_*
CLD_*
enum
1
ExecMainCode
0
systemd
1
_exit(2)
2
kill(2)
いいえsystemd
)などで
ノートしかし、上記のフィールドはいいえそのサービスに対応する現在のsystemd
サービスの開始時にサービスの基本プロセスを検出できないかどうかを示します。(下記の説明を参照)。最新の実行に対応することをお勧めします。systemd
以前はテストを完全に完了できます。
追加の洞察
あなたの推論でさらに説明が必要な2つの重要な点を見ることができます。
type=forking
提供する
type=forking
サービスは特に使いにくいですsystemd
。特に使用する場合GuessMainPID=yes
(デフォルトであるため、現在プロキシで使用されているもの)、これらのサービスタイプの場合、コマンド自体ExecStart
は次のようになります。fork(2)
一度その後、終了すると、フォークされたプロセスはMainPID
長い間サービスとして維持され、繁栄すると予想されます。その他:
- これらのフォークされたプロセスが再度フォークされてからシャットダウンされ、実際のサービスの役割を独自の「2番目の」フォークされたプロセスに委任する場合、これは単にパスを失い、サービスが
GuessMainPID
定期systemd
的に自発的に完了したと仮定して、次の任務を完了します。すべてを掃除します(例:実行中ExecStop
など)いいえStopping service...
メッセージはsystemd
意図的なサービス終了にのみ反応するため、記録されます。 - に変更された場合
ExecStart
オリジナル終了する前にfork(2)
2回(またはそれ以上)処理してからGuessMainPID
降伏し、systemd
終了時にすべてを破壊しないでください。ExecStart
オリジナルプロセスは最終的に終了します。サービスの実際のプロセスがまだ存在するため、これはより良い状況ですが、systemd
イベントも完全に追跡されず、少なくとも一貫性がないか不完全なログが発生するため、理想的ではありません。
ExecStop
実装する
コマンドがExecStop
実行されました返品MainPID
メインプロセスも終了した場合、プロセス自体が正常に終了した場合スタート成功(これが現在の状況です)。これは直観に反しているように見えますが、これは通常の動作ですsystemd
。サービスExecStop
コマンドがそのサービスの後にクリーンアップするのに好ましい方法であると考え、SIGTERMを最初に送信し(デフォルトで参照systemd.kill(5)
)、SIGKILLを送信することもできます。
マンページのどこにもこれについて明示的には記載されていませんが、systemd.service(5)
一部の文書、特にコマンドで使用できる環境変数に関連する文書から推論できますExec*
。より$SERVICE_RESULT
と$EXIT_CODE
$EXIT_STATUS
変数が取ることができる値は何であるか、変数が持つ意味論的意味は何であり、変数が正確にコマンドに使用可能であるというExecStop
事実ExecStopPost
。
明示的でない(または個人的に解釈された)文書に加えて、この動作を実行するソースを見てみましょう。 v219からのものです。ここで言うservice_sigchld_event()
のはservice_enter_running()
「実行中」の状態であることが知られている子供に関連するイベントで後者の関数呼び出しservice_enter_stop()
サービスの主なプロセスが検出RemainAfterExit=yes
されない限り、すべての場合にタスクを「停止」します。type=dbus
(type=forking
上記の説明を参照)または、コントロールグループは健康ではありません。
についてはなぜ人々はsystemd
そうすることにしました。私は開発者ではないのでよくわかりません。ただし、サービスがまだ存在しているが「不明」なプロセスがあるときにsystemd
通知を受ける機会を提供するためにこの動作が役に立つことがわかります。systemd
完全制御グループの終了 最後の手段として厳しい SIGTERM と SIGKILL を受け取る前に、可能な限り最善の方法で終了してください。このアクションはサービスに特に便利です。段落で述べたように、正しく追跡するのはtype=forking
最も困難であり、シャットダウン後にクリーンアップを試みる前に正常にシャットダウンされていない従来の/怠惰な/誤って実装されたサービスが原因です。systemd
type=
systemd.service(5)
systemd
ファタイ
1.code=
プロセスの「終了理由」を示す単語が続きます。つまりexited
、存在したか偶数であるkilled
かは、文字通りさまざまな有効な値を翻訳する単語を意味します。trapped
dumped
CLD_*
siginfo_t.si_code
で説明されているフィールドsigaction(2)