Systemd beendet den Dienst sofort nach dem Start

14

Ich schreibe eine System-Unit-Datei für OSSEC HIDS. Das Problem ist, dass systemd beim Starten des Dienstes diese sofort stoppt.

Wenn ich diese ExecStart-Direktive verwende, funktioniert alles einwandfrei.

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

Aber wenn ich kleine Verbesserungen mache, geht es mir gut in OSSEC-Protokollen, dass es SIG 15 nach dem Start erhält.

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

Wenn ich einen anderen kleinen Geldwechselservice mache, wird nach 20 Sekunden SIG 15 empfangen.

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

Ich vermute also, dass systemd / bin / sh nach dem Start des Dienstes beendet und bin / sh dann OSSEC beendet.

Wie kann ich dieses Problem lösen?

Daniil Svetlov
quelle
1
Was ist die Art der Dienstleistung?
Wieland,
@Wieland, ich habe es einfach versucht und gegabelt, aber das Ergebnis ist immer noch das gleiche.
Daniil Svetlov

Antworten:

36

Nichtübereinstimmung des Bereitschaftsprotokolls

Wie Wieland angedeutet hat, ist Typeder Service wichtig. Diese Einstellung gibt an, von welchem Bereitschaftsprotokoll systemd erwartet, dass der Dienst spricht. Es simplewird davon ausgegangen, dass ein Service sofort bereit ist. Ein forkingDienst wird als bereit angesehen, nachdem sein anfänglicher Prozess ein Kind gegabelt und dann beendet hat. Ein dbusDienst gilt als bereit, wenn ein Server auf dem Desktop Bus angezeigt wird. Und so weiter.

Wenn Sie nicht das Bereitschaftsprotokoll erhalten, das in der Serviceeinheit für die Serviceleistungen deklariert ist, läuft die Sache schief. Nicht übereinstimmende Bereitschaftsprotokolle führen dazu, dass Dienste nicht ordnungsgemäß gestartet werden oder (in der Regel) von systemd als fehlerhaft (falsch) diagnostiziert werden. Wenn festgestellt wird, dass ein Dienst nicht gestartet werden kann, stellt systemd sicher, dass alle verwaisten zusätzlichen Prozesse des Dienstes , die möglicherweise als Teil des Fehlers ausgeführt wurden (aus seiner Sicht), abgebrochen werden, um den Dienst ordnungsgemäß wieder inaktiv zu machen Zustand.

Du machst genau das.

Zuallererst das einfache Zeug: sh -cpasst nicht Type=simpleoder Type=forking.

In dem simpleProtokoll wird der anfängliche Prozess als der Dienstprozess angenommen. Tatsächlich führt ein sh -cWrapper das eigentliche Dienstprogramm als untergeordneten Prozess aus . Also MAINPIDgeht schief und ExecReloadhört auf zu arbeiten, für den Anfang. Bei der Verwendung Type=simplemuss man entweder verwenden sh -c 'exec …'oder nicht verwenden sh -c . Letzteres ist häufiger der richtige Kurs als manche denken.

sh -cpasst auch nicht Type=forking. Das Bereitschaftsprotokoll für einen forkingDienst ist sehr spezifisch. Der anfängliche Prozess muss ein Kind verzweigen und dann beenden. systemd wendet eine Zeitüberschreitung auf dieses Protokoll an. Wenn der anfängliche Prozess nicht innerhalb der zugewiesenen Zeit stattfindet, ist es ein Fehler, bereit zu werden. Wenn der anfängliche Prozess nicht innerhalb der vorgegebenen Zeit beendet wird, ist dies ebenfalls ein Fehler.

das unnötige Grauen das ist ossec-control

Das bringt uns zu den komplexen Dingen: dem ossec-controlDrehbuch.

Es stellt sich heraus, dass es sich um ein System 5- rcSkript handelt, das zwischen 4 und 10 Prozesse abspaltet, die sich selbst in ihrer Abzweigung befinden und auch beendet werden. Es ist eines dieser System 5- rcSkripte, die versuchen, eine ganze Reihe von Serverprozessen in einem einzigen Skript zu verwalten, mit forSchleifen, Race-Bedingungen, willkürlichen sleeps, um sie zu vermeiden. und all die anderen Schrecken, die Menschen dazu gebracht haben, Dinge wie den AIX System Resource Controller und daemontools vor zwei Jahrzehnten zu erfinden. Und vergessen wir nicht das versteckte Shell-Skript in einem Binärverzeichnis, das es im laufenden Betrieb neu schreibt, um Eigenheiten enableund disableVerben zu implementieren .

Also, wenn Sie, /bin/sh -c '/var/ossec/bin/ossec-control start'was passiert, ist das:

  1. systemd gibt an, was der Serviceprozess sein soll.
  2. Das ist die Schale, die sich gabelt ossec-control.
  3. Das wiederum gabelt zwischen 4 und 10 Enkelkindern.
  4. Die Enkelkinder gabelten sich alle und gingen der Reihe nach ab.
  5. Die Urenkel gabelten sich alle und gingen parallel ab.
  6. ossec-control Ausgänge.
  7. Die erste Shell wird beendet.
  8. Die Serviceprozesse waren die Ur-Ur- Enkelkinder, aber da diese Arbeitsweise weder mit dem Bereitschaftsprotokoll forking noch mit dem simpleBereitschaftsprotokoll übereinstimmt , betrachtet systemd den gesamten Service als fehlgeschlagen und fährt ihn herunter.

Keiner dieser Horror ist unter systemd überhaupt notwendig. Nichts davon.

eine systemd template service unit

Stattdessen schreibt man eine sehr einfache Vorlageneinheit :

[Einheit]
Description = Der OSSEC HIDS% i Server
After = network.target 

[Bedienung]
Typ = einfach
ExecStartPre = / usr / bin / env / var / ossec / bin /% p-% i -t
ExecStart = / usr / bin / env / var / ossec / bin /% p-% i -f

[Installieren]
WantedBy = multi-user.target

Speichern Sie dies als /etc/systemd/system/[email protected].

Die verschiedenen tatsächlichen Dienste sind Instanziierungen dieser Vorlage mit dem Namen:

Die Funktion zum Aktivieren und Deaktivieren wird direkt vom Service-Management-System (mit behobenem RedHat-Fehler 752774 ) bereitgestellt , ohne dass versteckte Shell-Skripte erforderlich sind.

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

Darüber hinaus lernt systemd jeden tatsächlichen Service direkt kennen und nachverfolgen. Es kann ihre Protokolle mit filtern journalctl -u. Es kann wissen, wann ein einzelner Dienst ausgefallen ist. Es weiß, welche Dienste aktiviert und ausgeführt werden sollen.

Übrigens: Type=simpleund die -fOption ist hier genauso richtig wie in vielen anderen Fällen. Nur sehr wenige Dienste im wilden Signal tatsächlich ihre Bereitschaft , vermöge der exit, und diese sind hier nicht solche Fälle nicht. Aber genau das forkingbedeutet der Typ. Dienstleistungen in freier Wildbahn in der Hauptsache nur Gabelung und Ausfahrt wegen einer irrtümlich empfangenen Weisheitsvorstellung, dass dies das ist, was Dæmons tun sollen. In der Tat ist es nicht. Es ist nicht seit den 1990er Jahren gewesen. Es ist Zeit aufzuholen.

Weitere Lektüre

JdeBP
quelle
2
Sehr ausführliche Antwort! Ich würde auch vorschlagen, ein "Gruppierungs" -Ziel zu erstellen, z. B. ossec.target, das Requires=alle erforderlichen Instanzen enthält, und dann PartOf=ossec.targetin ossec @ .service festzulegen. Dies ermöglicht das Starten und Stoppen von ossec durch Starten und Stoppen von ossec.target.
intelfx
@ JdeBP, wow! Vielen Dank für solch eine detaillierte Antwort. Ich hoffe, ich mache dieses Gerät und schreibe hier über die Ergebnisse. Ich war aber, dass ich leichter sein werde. Aber Sie haben Recht, Ossec-Controll ist eine Hölle.
Daniil Svetlov
1
Was ist der Grund für die Verwendung von / usr / bin / env als Wrapper?
Marius Gedminas
1

Behalten Sie Type = forking bei und geben Sie einen Speicherort für die PID-Datei an, wenn start service / app eine PID beibehält.

[Unit]
Description = "App beim Booten
ausführen " 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 stoppen
StandardOutput = syslog
Standarderror = syslog
Restart = on-Ausfall
SyslogIdentifier = webappslog

[Install]
WantedBy = multi-user.target
Alias = webapps

Raushan
quelle
0

Etwas verwandt, ich hatte einen systemd-Dienst, der den Anschein hatte, als würde systemd ihn nach 30s "töten".

systemctl status service-namewürde main process exited, code=exited, status=1/FAILUREnach Ablauf der 30er Jahre zeigen .

Es würde gut "isoliert" laufen (wie manuell im Terminal mit der gleichen Umgebung ).

Es stellte sich heraus, dass es war

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

innerhalb my_script_to_spawn_process.shes tat

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

Das funktioniert, verwirft aber die Ausgabeprotokollinformationen (normalerweise wird es in eine Datei verschoben, oder, wenn nicht, möglicherweise journalctl).

Ändern Sie es, um sich an einen anderen Ort zu loggen /bin/something > /tmp/my_file

dann verrät das /tmp/my_filetailing die eigentliche ursache. Was (tangential) war, dass Sie die Syntax nicht Environment=ABC="true"wie in Bash verwenden können, müssen keine Anführungszeichen oder der Schlüsselwert in Anführungszeichen sein, Environment="ABC=true"was dazu führte, dass mein Prozess nach ungefähr 30 Sekunden "in seiner Einrichtungsphase" beendet wurde.

Rogerdpack
quelle
-4

Beachten Sie, dass das Daemon-Modell von systemd simpel ist und nicht mit vielen vorhandenen Daemons kompatibel ist, die mehrfach forken, ausführen und einstellen. Am häufigsten sind die Daemons, die als root beginnen, um die Dinge einzurichten, und dann für den Routinebetrieb auf eine weniger privilegierte UID wechseln. zB Pid-Datei-Initialisierung ist eine Sache, die unter Systemd aufgrund von Berechtigungsproblemen fehlschlägt. Es gibt Workarounds (keine Fixes), aber es ist schlecht dokumentiert.

Die Erklärung von JdeBP ist willkommen, aber unvollständig, und seine Behauptung, es sei alles die Schuld von ossec-control, ist einfach nicht wahr. Sogar ganz einfache Dinge sind problematisch, z. B. nicht abgeschnittene Protokollzeilen zum Debuggen von Problemen oder sinnvolle Fehlermeldungen von systemd selbst, wenn Prozesse abgebrochen werden.

John
quelle
1
Was nützen PID-Dateien überhaupt? Wenn es für einen bestimmten Dienst einen gibt, kann es einen tatsächlichen Prozess mit dieser PID geben oder nicht, und wenn es einen Prozess mit der richtigen PID gibt, kann es sein, dass es tatsächlich den erwarteten Dienst gibt oder nicht.
JoostM