Systemdは起動直後にサービスを強制終了します


14

OSSEC HIDSのsystemdユニットファイルを書いています。問題は、systemdがサービスを開始するとすぐに停止することです。

そのExecStartディレクティブを使用すると、すべて正常に動作します。

ExecStart=/var/ossec/bin/ossec-control start

しかし、私がOSSECログを少し改善すると、開始後にSIG 15を受け取ることになります。

ExecStart=/bin/sh -c '${DIRECTORY}/bin/ossec-control start'

別の小さな変更を行うと、サービスは20秒後にSIG 15を受け取ります。

ExecStart=/bin/sh -c '${DIRECTORY}/bin/ossec-control start && sleep 20'

そのため、systemdはサービスの開始後に/ bin / shプロセスを強制終了し、bin / shはOSSECを強制終了します。

この問題を解決するにはどうすればよいですか?


1
サービスの種類は何ですか?
ヴィーランド

@Wieland、私は単純な分岐を試みましたが、結果はまだ同じです。
ダニエルスヴェトロフ

回答:


36

準備プロトコルの不一致

Wielandが示唆したようTypeに、サービスの重要性は重要です。その設定は、systemdがサービスが話すことを期待する準備プロトコルを示します。simpleサービスはすぐに準備ができていると想定されます。forkingサービスは、その最初のプロセスが終了し、その後、子をフォークし、後に準備ができていると解釈されます。dbusサービスは、サーバー、デスクトップバスに表示されたときに準備ができて取られます。などなど。

サービスが行うことと一致するようにサービスユニットで宣言された準備プロトコルを取得できない場合、問題が発生します。準備プロトコルの不一致により、サービスが正しく開始されないか、(より一般的に)systemdによって失敗と(誤って)診断されます。サービスがsystemdの開始に失敗したと見なされる場合、サービスの非アクティブな状態に適切に戻すために、(その観点から)障害の一部として実行されたままになっているサービスのすべての孤立した追加プロセスが強制的に強制終了されます状態。

あなたはまさにこれをやっています。

まず第一に、単純なもの:またはにsh -c一致しません。 Type=simpleType=forking

simpleプロトコル、初期処理をするものと解釈されることサービスプロセス。しかし実際には、sh -cラッパーは実際のサービスプログラムを子プロセスとして実行します。だから、MAINPIDうまくいかないとExecReloadスターターのため、作業を停止します。をType=simple使用するsh -c 'exec …'場合、最初に使用する か使用しないかのいずれかを行う必要がありますsh -c。後者は、多くの場合、一部の人が考えるよりも正しいコースです。

sh -cType=forkingどちらとも一致しません。forkingサービスの準備プロトコルは非常に具体的です。最初のプロセスでは、子をフォークしてから終了する必要があります。systemdは、このプロトコルにタイムアウトを適用します。初期プロセスが割り当てられた時間内に分岐しない場合、準備ができていないことになります。割り当てられた時間内に初期プロセスが終了しない場合、それも失敗です。

ある不必要な恐怖 ossec-control

それは複雑なもの、つまりそのossec-controlスクリプトに私たちを連れて行きます。

それは判明、それはシステム5だとrcスクリプトその4〜10のプロセスオフフォーク、あまりにも自分の順番フォークして終了でいる自分。ループ、競合状態、それらを回避するための任意のs、ハーフスタート状態でシステムを停止させる可能性のある障害モードを使用して、rcサーバープロセスのセット全体を1つのスクリプトで管理しようとするSystem 5 スクリプトの1つです。 20年前にAIX System Resource Controllerやdaemontoolsのようなものを発明させた他のすべての恐怖。そして、letのは、特有の実装に、それはその場で書き換えたことをバイナリディレクトリに隠されたシェルスクリプトを忘れていないし、動詞を。forsleepenabledisable

だからあなた/bin/sh -c '/var/ossec/bin/ossec-control start'が起こることはそれです:

  1. systemdは、サービスプロセスとなるものをフォークします。
  2. それがforkするシェルossec-controlです。
  3. その結果、4〜10人の孫が分岐します。
  4. 孫はすべて分岐し、順番に終了します。
  5. ひ孫はすべて分岐し、並行して終了します。
  6. ossec-control 終了します。
  7. 最初のシェルが終了します。
  8. サービスプロセスであった偉大な-great-孫が、作業の試合のためにこのようもないforking にもsimple即応プロトコル、systemdに全体が失敗したと、それがシャットダウンしてバックアップなどのサービスを検討します。

systemdでは、この恐怖は実際には必要ありません。どれでもない。

systemdテンプレートサービスユニット

代わりに、非常に単純なテンプレートユニットを記述します。

[単位]
Description = OSSEC HIDS%iサーバー
After = network.target 

[サービス]
Type = simple
ExecStartPre = / usr / bin / env / var / ossec / bin /%p-%i -t
ExecStart = / usr / bin / env / var / ossec / bin /%p-%i -f

[インストール]
WantedBy = multi-user.target

これをとして保存します/etc/systemd/system/ossec@.service

さまざまな実際のサービスは、このテンプレートのインスタンス化であり、次の名前が付けられています。

  • ossec@dbd.service
  • ossec@agentlessd.service
  • ossec@csyslogd.service
  • ossec@execd.service
  • ossec@agentd.service
  • ossec@logcollector.service
  • ossec@syscheckd.service
  • ossec@maild.service
  • ossec@analysisd.service
  • ossec@remoted.service
  • ossec@monitord.service

その後、有効化および無効化機能はサービス管理システムから直接提供されRedHatバグ752774が修正されています)、非表示のシェルスクリプトは不要です。

 systemctl enable ossec @ dbd ossec @ agentlessd ossec @ csyslogd ossec @ maild ossec @ execd ossec @ analysisd ossec @ logcollector ossec @ remoted ossec @ syscheckd ossec @ monitord

さらに、systemdは実際の各サービスについて直接知り、追跡することができます。でログをフィルタリングできますjournalctl -u。個々のサービスがいつ失敗したかを知ることができます。どのサービスが有効化され実行されているかがわかっています。

ちなみに、他の多くの場合と同じようにType=simple-fオプションもここにあります。実際、ごく少数のサービスがのdintによって準備ができていることを通知しexitますが、これらはこのようなケースでもありません。しかし、それがforkingタイプの意味です。メインでの野生のサービスは、それがデーモンがすべきことであるという誤った受信知恵の概念のために分岐して終了します。実際、そうではありません。それは1990年代以来ではありません。追いつく時です。

参考文献


2
非常に詳細な答え!またRequires=、必要なすべてのインスタンスを含むossec.targetなどの「グループ化」ターゲットを作成し、PartOf=ossec.targetossec @ .serviceに設定することもお勧めします。これにより、ossec.targetを開始および停止することにより、ossecを開始および停止できます。
intelfx

@JdeBP、すごい!このような詳細な回答をありがとうございます。この単元を作成し、結果についてここに書いてほしい。しかし、私はもっと楽になると思っていました。しかし、あなたは正しいです、ossec-controllは初期の地獄です。
ダニエルスヴェトロフ

1
/ usr / bin / envをラッパーとして使用する理由は何ですか?
マリウスゲドミナス

1

開始サービス/アプリがpidを維持している場合、Type = forkingを維持し、pidファイルの場所を指定します。

[Unit]
Description = "起動時にアプリを実行"
After = network.target syslog.target auditd.service

[Service]
Type = forking
PIDFile = / var / run / apache2 / apache2.pid
ExecStart = / etc / init.d / apache2 start
ExecStop = / etc / init.d / apache2 stop
StandardOutput = syslog
StandardError = syslog
Restart = on-failure
SyslogIdentifier = webappslog

[インストール]
WantedBy = multi-user.target
Alias = webapps


0

ある程度関連して、systemdが30秒後に「殺す」ように見えるsystemdサービスがありました。

systemctl status service-namemain process exited, code=exited, status=1/FAILURE30秒が経過すると表示されます。

「独立して」正常に実行されます(同じ環境の端末で手動で実行する場合など)。

それが判明した

Type=forking
...
Environment=ABC="TRUE"
ExecStart=/path/to/my_script_to_spawn_process.sh

その中my_script_to_spawn_process.shでやっていた

/bin/something > /dev/null 2>&1 &

これは機能しますが、出力ログ情報を破棄していました(通常はファイルに送信されますが、そうでない場合は、おそらくjournalctl)。

次のような他の場所にログを記録するように変更します /bin/something > /tmp/my_file

その後/tmp/my_file、実際の原因を明らかにします。これは、(接線上)Environment=ABC="true"bashのように構文を使用できないことでした。引用符や引用符内のすべてのキー値である必要はありませんEnvironment="ABC=true"


-4

systemdのデーモンモデルは単純であり、複数のフォーク、実行、setuidを実行する多くの既存のデーモンと互換性がないことに注意してください。最も一般的なのは、物事を設定するためにルートとして起動し、ルーチン操作のために権限の低いUIDに切り替えるデーモンです。たとえば、Pidファイルの初期化は、特権の問題のためにsystemdで失敗することの1つです。回避策はありますが(修正ではありません)、文書化が不十分です。

JdeBPの説明は歓迎されますが、不完全であり、それがすべてossec-controlのせいであるという彼の主張は単に真実ではありません。問題をデバッグするために切り捨てられていないログ行を取得したり、プロセスを強制終了したときにsystemd自体から意味のあるエラーメッセージを取得するなど、非常に些細なことでも問題があります。


1
とにかく、PIDファイルはどのような用途に使用されますか?特定のサービスに1つ存在する場合、そのPIDを持つ実際のプロセスが存在する場合と存在しない場合があり、正しいPIDを持つプロセスが存在する場合、実際に期待されるサービスである場合とそうでない場合があります。
JoostM
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.