GNU/Linux >> LINUX-Kenntnisse >  >> Linux

Verwalten Sie den Start mit systemd

Als ich kürzlich ein Linux-System eingerichtet habe, wollte ich wissen, wie ich sicherstellen kann, dass Abhängigkeiten für Dienste und andere Units eingerichtet sind und ausgeführt werden, bevor diese abhängigen Dienste und Units gestartet werden. Insbesondere brauchte ich mehr Wissen darüber, wie systemd die Startsequenz verwaltet, insbesondere bei der Bestimmung, dass die Auftragsdienste in einem im Wesentlichen parallelen System gestartet werden.

Sie wissen vielleicht, dass SystemV (der Vorgänger von systemd, wie ich im ersten Artikel dieser Serie erklärt habe) die Startreihenfolge ordnet, indem es die Startskripte mit einem SXX-Präfix benennt, wobei XX eine Zahl von 00 bis 99 ist. SystemV verwendet dann die Sortierreihenfolge nach Namen und führt jedes Startskript der Reihe nach für den gewünschten Runlevel aus.

Systemd verwendet jedoch Unit-Dateien, die von einem Systemadministrator erstellt oder geändert werden können, um Unterroutinen nicht nur für die Initialisierung, sondern auch für den regulären Betrieb zu definieren. Im dritten Artikel dieser Serie habe ich erklärt, wie man eine Mount-Unit-Datei erstellt. In diesem fünften Artikel zeige ich, wie Sie eine andere Art von Unit-Datei erstellen – eine Service-Unit-Datei, die beim Start ein Programm ausführt. Sie können auch bestimmte Konfigurationseinstellungen in der Unit-Datei ändern und das systemd-Journal verwenden, um den Speicherort Ihrer Änderungen in der Startsequenz anzuzeigen.

Vorbereitung

Stellen Sie sicher, dass Sie rhgb entfernt haben und quiet aus dem GRUB_CMDLINE_LINUX= Zeile in /etc/default/grub Datei, wie ich im zweiten Artikel dieser Serie gezeigt habe. Dadurch können Sie den Linux-Startnachrichtenstrom beobachten, den Sie für einige der Experimente in diesem Artikel benötigen.

Das Programm

In diesem Tutorial erstellen Sie ein einfaches Programm, das es Ihnen ermöglicht, eine Meldung während des Starts auf der Konsole und später im systemd-Journal zu beobachten.

Erstellen Sie das Shell-Programm /usr/local/bin/hello.sh und fügen Sie den folgenden Inhalt hinzu. Sie möchten sicherstellen, dass das Ergebnis während des Starts sichtbar ist und Sie es beim Durchsuchen des systemd-Journals leicht finden können. Sie verwenden eine Version des „Hello world“-Programms mit einigen Balken drumherum, damit es auffällt. Stellen Sie sicher, dass die Datei ausführbar ist und Benutzer- und Gruppeneigentum von root mit 700-Berechtigungen für die Sicherheit hat:

#!/usr/bin/bash
# Simple program to use for testing startup configurations
# with systemd.
# By David Both
# Licensed under GPL V2
#
echo "###############################"
echo "######### Hello World! ########"
echo "###############################"

Führen Sie dieses Programm über die Befehlszeile aus, um zu überprüfen, ob es ordnungsgemäß funktioniert:

[root@testvm1 ~]# hello.sh 
###############################
######### Hello World! ########
###############################
[root@testvm1 ~]#

Dieses Programm kann in jeder Skriptsprache oder kompilierten Sprache erstellt werden. Die hello.sh Das Programm könnte sich auch an anderen Orten befinden, basierend auf der hierarchischen Struktur des Linux-Dateisystems (FHS). Ich platziere es in /usr/local/bin Verzeichnis, damit es einfach von der Befehlszeile aus ausgeführt werden kann, ohne dass ein Pfad vorangestellt werden muss, wenn ich den Befehl eingebe. Ich finde, dass viele der von mir erstellten Shell-Programme von der Befehlszeile und von anderen Tools wie systemd ausgeführt werden müssen.

Die Service-Unit-Datei

Erstellen Sie die Service-Unit-Datei /etc/systemd/system/hello.service mit folgendem Inhalt. Diese Datei muss nicht ausführbar sein, aber aus Sicherheitsgründen benötigt sie Benutzer- und Gruppenbesitz durch root und 644- oder 640-Berechtigungen:

# Simple service unit file to use for testing 
# startup configurations with systemd.
# By David Both
# Licensed under GPL V2
#

[Unit]
Description=My hello shell script

[Service]
Type=oneshot
ExecStart=/usr/local/bin/hello.sh

[Install]
WantedBy=multi-user.target

Überprüfen Sie, ob die Service-Unit-Datei wie erwartet funktioniert, indem Sie den Servicestatus anzeigen. Alle syntaktischen Fehler werden hier angezeigt:

[root@testvm1 ~]# systemctl status hello.service 
● hello.service - My hello shell script
     Loaded: loaded (/etc/systemd/system/hello.service; disabled; vendor preset: disabled)
     Active: inactive (dead)
[root@testvm1 ~]#

Sie können diesen "Oneshot"-Diensttyp problemlos mehrmals ausführen. Der Oneshot-Typ ist für Dienste gedacht, bei denen das von der Service-Unit-Datei gestartete Programm der Hauptprozess ist und abgeschlossen werden muss, bevor systemd einen abhängigen Prozess startet.

Es gibt sieben Service-Typen, und Sie finden eine Erklärung für jeden (zusammen mit den anderen Teilen einer Service-Unit-Datei) in der Manpage systemd.service(5). (Weitere Informationen finden Sie auch in den Ressourcen am Ende dieses Artikels.)

So neugierig ich auch bin, ich wollte sehen, wie ein Fehler aussehen könnte. Also habe ich das "o" aus Type=oneshot gelöscht Zeile, also sah es aus wie Type=neshot , und führte den Befehl erneut aus:

[root@testvm1 ~]# systemctl status hello.service
● hello.service - My hello shell script
     Loaded: loaded (/etc/systemd/system/hello.service; disabled; vendor preset: disabled)
     Active: inactive (dead)

May 06 08:50:09 testvm1.both.org systemd[1]: /etc/systemd/system/hello.service:12: Failed to parse service type, ignoring: neshot
[root@testvm1 ~]#

Diese Ergebnisse sagten mir genau, wo der Fehler lag, und machten es sehr einfach, das Problem zu lösen.

Weitere Linux-Ressourcen

  • Spickzettel für Linux-Befehle
  • Spickzettel für fortgeschrittene Linux-Befehle
  • Kostenloser Online-Kurs:RHEL Technical Overview
  • Spickzettel für Linux-Netzwerke
  • SELinux-Spickzettel
  • Spickzettel für allgemeine Linux-Befehle
  • Was sind Linux-Container?
  • Unsere neuesten Linux-Artikel

Beachten Sie dies auch nach der Wiederherstellung von hello.service Datei in ihre ursprüngliche Form zurück, bleibt der Fehler bestehen. Obwohl ein Neustart den Fehler löscht, sollten Sie das nicht tun müssen, also habe ich nach einer Methode gesucht, um anhaltende Fehler wie diese zu löschen. Ich bin auf Dienstfehler gestoßen, die den Befehl systemctl daemon-reload erfordern um einen Fehlerzustand zurückzusetzen, aber das hat in diesem Fall nicht funktioniert. Die Fehlermeldungen, die mit diesem Befehl behoben werden können, scheinen immer eine entsprechende Anweisung zu enthalten, sodass Sie wissen, dass Sie ihn ausführen müssen.

Es wird jedoch empfohlen, systemctl daemon-reload auszuführen nach dem Ändern einer Unit-Datei oder dem Erstellen einer neuen. Dadurch wird systemd benachrichtigt, dass die Änderungen vorgenommen wurden, und es kann bestimmte Arten von Problemen bei der Verwaltung geänderter Dienste oder Einheiten verhindern. Fahren Sie fort und führen Sie diesen Befehl aus.

Nach Korrektur des Rechtschreibfehlers in der Service-Unit-Datei genügt ein einfacher systemctl restart hello.service den Fehler gelöscht. Experimentieren Sie ein wenig, indem Sie einige andere Fehler in hello.service einfügen Datei, um zu sehen, welche Art von Ergebnissen Sie erhalten.

Dienst starten

Jetzt können Sie den neuen Dienst starten und den Status überprüfen, um das Ergebnis anzuzeigen. Obwohl Sie im vorherigen Abschnitt wahrscheinlich einen Neustart durchgeführt haben, können Sie einen Oneshot-Dienst beliebig oft starten oder neu starten, da er einmal ausgeführt und dann beendet wird.

Fahren Sie fort und starten Sie den Dienst (wie unten gezeigt) und überprüfen Sie dann den Status. Je nachdem, wie viel Sie mit Fehlern experimentiert haben, können Ihre Ergebnisse von meinen abweichen:

[root@testvm1 ~]# systemctl start hello.service 
[root@testvm1 ~]# systemctl status hello.service
● hello.service - My hello shell script
     Loaded: loaded (/etc/systemd/system/hello.service; disabled; vendor preset: disabled)
     Active: inactive (dead)

May 10 10:37:49 testvm1.both.org hello.sh[842]: ######### Hello World! ########
May 10 10:37:49 testvm1.both.org hello.sh[842]: ###############################
May 10 10:37:49 testvm1.both.org systemd[1]: hello.service: Succeeded.
May 10 10:37:49 testvm1.both.org systemd[1]: Finished My hello shell script.
May 10 10:54:45 testvm1.both.org systemd[1]: Starting My hello shell script...
May 10 10:54:45 testvm1.both.org hello.sh[1380]: ###############################
May 10 10:54:45 testvm1.both.org hello.sh[1380]: ######### Hello World! ########
May 10 10:54:45 testvm1.both.org hello.sh[1380]: ###############################
May 10 10:54:45 testvm1.both.org systemd[1]: hello.service: Succeeded.
May 10 10:54:45 testvm1.both.org systemd[1]: Finished My hello shell script.
[root@testvm1 ~]#

Beachten Sie in der Ausgabe des Statusbefehls, dass die systemd-Meldungen angeben, dass hello.sh Skript gestartet und der Dienst abgeschlossen. Sie können auch die Ausgabe des Skripts sehen. Diese Anzeige wird aus den Journaleinträgen der letzten Aufrufe des Dienstes generiert. Versuchen Sie mehrmals, den Dienst zu starten, und führen Sie dann den Statusbefehl erneut aus, um zu sehen, was ich meine.

Sie sollten sich auch direkt die Zeitschrifteninhalte ansehen; Es gibt mehrere Möglichkeiten, dies zu tun. Eine Möglichkeit besteht darin, die Datensatztypkennung anzugeben, in diesem Fall den Namen des Shell-Skripts. Dies zeigt die Journaleinträge für frühere Neustarts sowie die aktuelle Sitzung. Wie Sie sehen, recherchiere und teste ich seit einiger Zeit für diesen Artikel:

[root@testvm1 ~]# journalctl -t hello.sh
<snip>
-- Reboot --
May 08 15:55:47 testvm1.both.org hello.sh[840]: ###############################
May 08 15:55:47 testvm1.both.org hello.sh[840]: ######### Hello World! ########
May 08 15:55:47 testvm1.both.org hello.sh[840]: ###############################
-- Reboot --
May 08 16:01:51 testvm1.both.org hello.sh[840]: ###############################
May 08 16:01:51 testvm1.both.org hello.sh[840]: ######### Hello World! ########
May 08 16:01:51 testvm1.both.org hello.sh[840]: ###############################
-- Reboot --
May 10 10:37:49 testvm1.both.org hello.sh[842]: ###############################
May 10 10:37:49 testvm1.both.org hello.sh[842]: ######### Hello World! ########
May 10 10:37:49 testvm1.both.org hello.sh[842]: ###############################
May 10 10:54:45 testvm1.both.org hello.sh[1380]: ###############################
May 10 10:54:45 testvm1.both.org hello.sh[1380]: ######### Hello World! ########
May 10 10:54:45 testvm1.both.org hello.sh[1380]: ###############################
[root@testvm1 ~]#

So finden Sie die systemd-Einträge für hello.service Einheit können Sie auf systemd suchen. Sie können G+Enter verwenden , um zum Ende der Journaleinträge zu blättern und dann zurück zu scrollen, um diejenigen zu finden, die Sie interessieren. Verwenden Sie den -b Option, um nur die Einträge für den letzten Start anzuzeigen:

[root@testvm1 ~]# journalctl -b -t systemd
<snip>
May 10 10:37:49 testvm1.both.org systemd[1]: Starting SYSV: Late init script for live image....
May 10 10:37:49 testvm1.both.org systemd[1]: Started SYSV: Late init script for live image..
May 10 10:37:49 testvm1.both.org systemd[1]: hello.service: Succeeded.
May 10 10:37:49 testvm1.both.org systemd[1]: Finished My hello shell script.
May 10 10:37:50 testvm1.both.org systemd[1]: Starting D-Bus System Message Bus...
May 10 10:37:50 testvm1.both.org systemd[1]: Started D-Bus System Message Bus.

Ich habe ein paar andere Tagebucheinträge kopiert, um Ihnen eine Vorstellung davon zu geben, was Sie finden könnten. Dieser Befehl spuckt alle Journalzeilen aus, die sich auf systemd beziehen – 109.183 Zeilen, als ich dies schrieb. Das sind eine Menge Daten, die es zu sortieren gilt. Sie können die Suchfunktion des Pagers verwenden, die normalerweise less ist , oder Sie können das eingebaute grep verwenden Merkmal. Das -g (oder --grep= ) verwendet Perl-kompatible reguläre Ausdrücke:

[root@testvm1 ~]# journalctl -b -t systemd -g "hello"
[root@testvm1 ~]# journalctl -b -t systemd -g "hello"
-- Logs begin at Tue 2020-05-05 18:11:49 EDT, end at Sun 2020-05-10 11:01:01 EDT. --
May 10 10:37:49 testvm1.both.org systemd[1]: Starting My hello shell script...
May 10 10:37:49 testvm1.both.org systemd[1]: hello.service: Succeeded.
May 10 10:37:49 testvm1.both.org systemd[1]: Finished My hello shell script.
May 10 10:54:45 testvm1.both.org systemd[1]: Starting My hello shell script...
May 10 10:54:45 testvm1.both.org systemd[1]: hello.service: Succeeded.
May 10 10:54:45 testvm1.both.org systemd[1]: Finished My hello shell script.
[root@testvm1 ~]#

Sie könnten das Standard-GNU grep verwenden Befehl, aber das würde die Log-Metadaten nicht in der ersten Zeile anzeigen.

Wenn Sie nicht nur die Journaleinträge zu Ihrem hello sehen möchten Service können Sie die Dinge ein wenig eingrenzen, indem Sie einen Zeitraum angeben. Zum Beispiel beginne ich mit der Anfangszeit von 10:54:00 auf meiner Test-VM, die Anfang der Minute war, stammen die obigen Einträge. Beachten Sie, dass --since= Option in Anführungszeichen gesetzt werden muss und dass diese Option auch als -S "<time specification>" ausgedrückt werden kann .

Das Datum und die Uhrzeit werden auf Ihrem Host anders sein, verwenden Sie also unbedingt die Zeitstempel, die mit den Zeiten in Ihren Journalen übereinstimmen:

[root@testvm1 ~]# journalctl --since="2020-05-10 10:54:00"
May 10 10:54:35 testvm1.both.org audit: BPF prog-id=54 op=LOAD
May 10 10:54:35 testvm1.both.org audit: BPF prog-id=55 op=LOAD
May 10 10:54:45 testvm1.both.org systemd[1]: Starting My hello shell script...
May 10 10:54:45 testvm1.both.org hello.sh[1380]: ###############################
May 10 10:54:45 testvm1.both.org hello.sh[1380]: ######### Hello World! ########
May 10 10:54:45 testvm1.both.org hello.sh[1380]: ###############################
May 10 10:54:45 testvm1.both.org systemd[1]: hello.service: Succeeded.
May 10 10:54:45 testvm1.both.org systemd[1]: Finished My hello shell script.
May 10 10:54:45 testvm1.both.org audit[1]: SERVICE_START pid=1 uid=0 auid=4294967295 ses=4294967295 msg='unit=hello comm="systemd" exe="/usr/lib/systemd"'
May 10 10:54:45 testvm1.both.org audit[1]: SERVICE_STOP pid=1 uid=0 auid=4294967295 ses=4294967295 msg='unit=hello comm="systemd" exe="/usr/lib/systemd/"'
May 10 10:56:00 testvm1.both.org NetworkManager[840]: <error> [1589122560.0633] dhcp4 (enp0s3): error -113 dispatching events
May 10 10:56:00 testvm1.both.org NetworkManager[840]: <info>  [1589122560.0634] dhcp4 (enp0s3): state changed bound -> fail
<snip>

Der since Die Spezifikation überspringt alle Einträge vor diesem Zeitpunkt, aber es gibt nach diesem Zeitpunkt immer noch viele Einträge, die Sie nicht benötigen. Sie können auch until verwenden Option zum Abschneiden der Einträge, die etwas nach der Zeit kommen, die Sie interessiert. Ich möchte die gesamte Minute, in der das Ereignis aufgetreten ist, und nichts weiter:

[root@testvm1 ~]# journalctl --since="2020-05-10 10:54:35" --until="2020-05-10 10:55:00"
-- Logs begin at Tue 2020-05-05 18:11:49 EDT, end at Sun 2020-05-10 11:04:59 EDT. --
May 10 10:54:35 testvm1.both.org systemd[1]: Reloading.
May 10 10:54:35 testvm1.both.org audit: BPF prog-id=27 op=UNLOAD
May 10 10:54:35 testvm1.both.org audit: BPF prog-id=26 op=UNLOAD
<snip>
ay 10 10:54:35 testvm1.both.org audit: BPF prog-id=55 op=LOAD
May 10 10:54:45 testvm1.both.org systemd[1]: Starting My hello shell script...
May 10 10:54:45 testvm1.both.org hello.sh[1380]: ###############################
May 10 10:54:45 testvm1.both.org hello.sh[1380]: ######### Hello World! ########
May 10 10:54:45 testvm1.both.org hello.sh[1380]: ###############################
May 10 10:54:45 testvm1.both.org systemd[1]: hello.service: Succeeded.
May 10 10:54:45 testvm1.both.org systemd[1]: Finished My hello shell script.
May 10 10:54:45 testvm1.both.org audit[1]: SERVICE_START pid=1 uid=0 auid=4294967295 ses=4294967295 msg='unit=hello comm="systemd" exe="/usr/lib/systemd>
May 10 10:54:45 testvm1.both.org audit[1]: SERVICE_STOP pid=1 uid=0 auid=4294967295 ses=4294967295 msg='unit=hello comm="systemd" exe="/usr/lib/systemd/>
lines 1-46/46 (END)

Wenn in diesem Zeitraum viele Aktivitäten stattgefunden haben, können Sie den resultierenden Datenstrom mit einer Kombination dieser Optionen weiter eingrenzen:

[root@testvm1 ~]# journalctl --since="2020-05-10 10:54:35" --until="2020-05-10 10:55:00" -t "hello.sh"
-- Logs begin at Tue 2020-05-05 18:11:49 EDT, end at Sun 2020-05-10 11:10:41 EDT. --
May 10 10:54:45 testvm1.both.org hello.sh[1380]: ###############################
May 10 10:54:45 testvm1.both.org hello.sh[1380]: ######### Hello World! ########
May 10 10:54:45 testvm1.both.org hello.sh[1380]: ###############################
[root@testvm1 ~]#

Ihre Ergebnisse sollten meinen ähneln. Sie können dieser Reihe von Experimenten entnehmen, dass der Dienst ordnungsgemäß ausgeführt wurde.

Neustart – endlich

Bisher haben Sie den Host, auf dem Sie Ihren Dienst installiert haben, noch nicht neu gestartet. Tun Sie das also jetzt, denn schließlich geht es in dieser Anleitung darum, ein Programm beim Start auszuführen. Zuerst müssen Sie den Dienst so aktivieren, dass er während der Startsequenz gestartet wird:

[root@testvm1 ~]# systemctl enable hello.service 
Created symlink /etc/systemd/system/multi-user.target.wants/hello.service → /etc/systemd/system/hello.service.
[root@testvm1 ~]#

Beachten Sie, dass der Link in /etc/systemd/system/multi-user.target.wants erstellt wurde Verzeichnis. Dies liegt daran, dass die Diensteinheitendatei angibt, dass der Dienst von multi-user.target "gewünscht" wird .

Starten Sie neu und achten Sie darauf, den Datenstrom während der Startsequenz zu beobachten, um die Nachricht „Hello World“ zu sehen. Warte … du hast es nicht gesehen? Nun, ich auch nicht. Obwohl es sehr schnell verging, sah ich die Meldung von systemd, dass es den hello.service startete .

Sehen Sie sich das Journal seit dem letzten Systemstart an. Sie können den less verwenden Suchtool des Pagers, um „Hallo“ oder „Hallo“ zu finden. Ich habe viele Datenzeilen gekürzt, aber einige der umgebenden Tagebucheinträge belassen, damit Sie ein Gefühl dafür bekommen, wie die Einträge zu Ihrem Dienst vor Ort aussehen:

[root@testvm1 ~]# journalctl -b
<snip>
May 10 10:37:49 testvm1.both.org systemd[1]: Listening on SSSD Kerberos Cache Manager responder socket.
May 10 10:37:49 testvm1.both.org systemd[1]: Reached target Sockets.
May 10 10:37:49 testvm1.both.org systemd[1]: Reached target Basic System.
May 10 10:37:49 testvm1.both.org systemd[1]: Starting Modem Manager...
May 10 10:37:49 testvm1.both.org systemd[1]: Starting Network Manager...
May 10 10:37:49 testvm1.both.org systemd[1]: Starting Avahi mDNS/DNS-SD Stack...
May 10 10:37:49 testvm1.both.org systemd[1]: Condition check resulted in Secure Boot DBX (blacklist) updater being skipped.
May 10 10:37:49 testvm1.both.org systemd[1]: Starting My hello shell script...
May 10 10:37:49 testvm1.both.org systemd[1]: Starting IPv4 firewall with iptables...
May 10 10:37:49 testvm1.both.org systemd[1]: Started irqbalance daemon.
May 10 10:37:49 testvm1.both.org audit[1]: SERVICE_START pid=1 uid=0 auid=4294967295 ses=4294967295 msg='unit=irqbalance comm="systemd" exe="/usr/lib/sy>"'
May 10 10:37:49 testvm1.both.org systemd[1]: Starting LSB: Init script for live image....
May 10 10:37:49 testvm1.both.org systemd[1]: Starting Hardware Monitoring Sensors...
<snip>
May 10 10:37:49 testvm1.both.org systemd[1]: Starting NTP client/server...
May 10 10:37:49 testvm1.both.org systemd[1]: Starting SYSV: Late init script for live image....
May 10 10:37:49 testvm1.both.org systemd[1]: Started SYSV: Late init script for live image..
May 10 10:37:49 testvm1.both.org audit[1]: SERVICE_START pid=1 uid=0 auid=4294967295 ses=4294967295 msg='unit=livesys-late comm="systemd" exe="/usr/lib/>"'
May 10 10:37:49 testvm1.both.org hello.sh[842]: ###############################
May 10 10:37:49 testvm1.both.org hello.sh[842]: ######### Hello World! ########
May 10 10:37:49 testvm1.both.org hello.sh[842]: ###############################
May 10 10:37:49 testvm1.both.org systemd[1]: hello.service: Succeeded.
May 10 10:37:49 testvm1.both.org systemd[1]: Finished My hello shell script.
May 10 10:37:49 testvm1.both.org audit[1]: SERVICE_START pid=1 uid=0 auid=4294967295 ses=4294967295 msg='unit=hello comm="systemd" exe="/usr/lib/systemd>"'
May 10 10:37:49 testvm1.both.org audit[1]: SERVICE_STOP pid=1 uid=0 auid=4294967295 ses=4294967295 msg='unit=hello comm="systemd" exe="/usr/lib/systemd/>
May 10 10:37:50 testvm1.both.org audit: BPF prog-id=28 op=LOAD
<snip>

Sie können sehen, dass systemd den hello.service gestartet hat Einheit, die hello.sh ausführte Shell-Skript mit der im Journal aufgezeichneten Ausgabe. Wenn Sie es während des Bootens abfangen könnten, hätten Sie auch die systemd-Meldung gesehen, die angibt, dass das Skript gestartet wurde, und eine weitere Meldung, die angibt, dass der Dienst erfolgreich war. Wenn Sie sich die erste systemd-Nachricht im obigen Datenstrom ansehen, können Sie sehen, dass systemd Ihren Dienst sehr bald nach Erreichen des grundlegenden Systemziels gestartet hat.

Ich möchte aber auch, dass die Meldung beim Start angezeigt wird. Es gibt eine Möglichkeit, dies zu erreichen:Fügen Sie die folgende Zeile zu [Service] hinzu Abschnitt von hello.service Datei:

StandardOutput=journal+console

Der hello.service Datei sieht jetzt so aus:

# Simple service unit file to use for testing 
# startup configurations with systemd.
# By David Both
# Licensed under GPL V2
#

[Unit]
Description=My hello shell script

[Service]
Type=oneshot
ExecStart=/usr/local/bin/hello.sh
StandardOutput=journal+console

[Install]
WantedBy=multi-user.target

Nachdem Sie diese Zeile hinzugefügt haben, starten Sie das System neu und beobachten Sie den Datenstrom, während er während des Startvorgangs auf dem Display nach oben scrollt. Sie sollten die Nachricht in ihrem kleinen Kästchen sehen. Nachdem die Startsequenz abgeschlossen ist, können Sie das Journal für den letzten Start anzeigen und die Einträge für Ihren neuen Dienst suchen.

Reihenfolge ändern

Jetzt, da Ihr Dienst funktioniert, können Sie sich ansehen, wo er in der Startsequenz beginnt, und mit Änderungen experimentieren. Es ist wichtig, sich daran zu erinnern, dass es die Absicht von systemd ist, so viele Dienste und andere Unit-Typen parallel innerhalb jedes der Hauptziele zu starten:basic.target , multi-user.target und graphical.target . Sie sollten gerade die Journaleinträge für den letzten Start gesehen haben, die meinem Journal in der obigen Ausgabe ähneln sollten.

Beachten Sie, dass systemd Ihren Testdienst gestartet hat, kurz nachdem er das Zielbasissystem erreicht hat. Dies haben Sie in der Service-Unit-Datei im WantedBy angegeben Linie, also ist es richtig. Bevor Sie etwas ändern, listen Sie den Inhalt von /etc/systemd/system/multi-user.target.wants auf Verzeichnis, und Sie sehen einen symbolischen (weichen) Link zur Service Unit-Datei. Die [Install] Abschnitt der Service-Unit-Datei gibt an, welches Ziel den Dienst startet und systemctl enable hello.service ausführt Der Befehl erstellt den Link im entsprechenden "Ziel will"-Verzeichnis:

hello.service -> /etc/systemd/system/hello.service

Bestimmte Dienste müssen während basic.target gestartet werden , und andere müssen nicht gestartet werden, es sei denn, das System startet graphical.target . Der Dienst in diesem Experiment startet nicht in basic.target – Angenommen, Sie brauchen es nicht, um bis zum graphical.target zu starten . Ändern Sie also den WantedBy Zeile:

WantedBy=graphical.target

Achten Sie darauf, den hello.service zu deaktivieren und aktivieren Sie es erneut, um den alten Link zu löschen und den neuen in graphical.targets.wants hinzuzufügen Verzeichnis. Mir ist aufgefallen, dass ich systemctl disable ausführen kann, wenn ich vergesse, den Dienst zu deaktivieren, bevor ich das gewünschte Ziel ändere Befehl, und die Links werden aus beiden "Target Wants"-Verzeichnissen entfernt. Dann muss ich nur den Dienst wieder aktivieren und neu starten.

Ein Problem beim Starten von Diensten in graphical.target ist das, wenn der Host zu multi-user.target bootet , wird dieser Dienst nicht automatisch gestartet. Das ist möglicherweise das, was Sie möchten, wenn der Dienst eine GUI-Desktop-Oberfläche erfordert, aber es ist möglicherweise auch nicht das, was Sie möchten.

Sehen Sie sich die Journaleinträge für graphical.target an und das multi-user.target mit dem -o short-monotonic Option, die Sekunden nach dem Kernel-Start mit Mikrosekunden-Präzision anzeigt:

[root@testvm1 ~]# journalctl -b -o short-monotonic

Einige Ergebnisse für multi-user.target :

[   17.264730] testvm1.both.org systemd[1]: Starting My hello shell script...
[   17.265561] testvm1.both.org systemd[1]: Starting IPv4 firewall with iptables...
<SNIP>
[   19.478468] testvm1.both.org systemd[1]: Starting LSB: Init script for live image....
[   19.507359] testvm1.both.org iptables.init[844]: iptables: Applying firewall rules: [  OK  ]
[   19.507835] testvm1.both.org hello.sh[843]: ###############################
[   19.507835] testvm1.both.org hello.sh[843]: ######### Hello World! ########
[   19.507835] testvm1.both.org hello.sh[843]: ###############################
<SNIP>
[   21.482481] testvm1.both.org systemd[1]: hello.service: Succeeded.
[   21.482550] testvm1.both.org smartd[856]: Opened configuration file /etc/smartmontools/smartd.conf
[   21.482605] testvm1.both.org systemd[1]: Finished My hello shell script.

Und einige Ergebnisse für graphical.target :

[   19.436815] testvm1.both.org systemd[1]: Starting My hello shell script...
[   19.437070] testvm1.both.org systemd[1]: Starting IPv4 firewall with iptables...
<SNIP>
[   19.612614] testvm1.both.org hello.sh[841]: ###############################
[   19.612614] testvm1.both.org hello.sh[841]: ######### Hello World! ########
[   19.612614] testvm1.both.org hello.sh[841]: ###############################
[   19.629455] testvm1.both.org audit[1]: SERVICE_START pid=1 uid=0 auid=4294967295 ses=4294967295 msg='unit=hello comm="systemd" exe="/usr/lib/systemd/systemd" hostname=? addr=? terminal=? res=success'
[   19.629569] testvm1.both.org audit[1]: SERVICE_STOP pid=1 uid=0 auid=4294967295 ses=4294967295 msg='unit=hello comm="systemd" exe="/usr/lib/systemd/systemd" hostname=? addr=? terminal=? res=success'
[   19.629682] testvm1.both.org systemd[1]: hello.service: Succeeded.
[   19.629782] testvm1.both.org systemd[1]: Finished My hello shell script.

Trotz graphical.target "want" in der Unit-Datei, dem hello.service Gerät läuft etwa 19,5 oder 19,6 Sekunden nach dem Start. Aber hello.service beginnt bei etwa 17,24 Sekunden im multi-user.target und 19,43 Sekunden im grafischen Ziel.

Was bedeutet das? Sehen Sie sich /etc/systemd/system/default.target an Verknüpfung. Der Inhalt dieser Datei zeigt, dass systemd zuerst das Standardziel graphical.target startet , die dann multi-user.target einzieht :

[root@testvm1 system]# cat default.target
#  SPDX-License-Identifier: LGPL-2.1+
#
#  This file is part of systemd.
#
#  systemd is free software; you can redistribute it and/or modify it
#  under the terms of the GNU Lesser General Public License as published by
#  the Free Software Foundation; either version 2.1 of the License, or
#  (at your option) any later version.

[Unit]
Description=Graphical Interface
Documentation=man:systemd.special(7)
Requires=multi-user.target
Wants=display-manager.service
Conflicts=rescue.service rescue.target
After=multi-user.target rescue.service rescue.target display-manager.service
AllowIsolate=yes
[root@testvm1 system]#

Ob es den Dienst mit dem graphical.target startet oder das multi-user.target , der hello.service Gerät läuft bei etwa 19,5 oder 19,6 Sekunden in den Start. Basierend auf diesen und den Journalergebnissen (insbesondere denen, die die monotone Ausgabe verwenden), wissen Sie, dass diese beiden Ziele parallel starten. Sehen Sie sich noch etwas aus der Journalausgabe an:

[   28.397330] testvm1.both.org systemd[1]: Reached target Multi-User System.
[   28.397431] testvm1.both.org systemd[1]: Reached target Graphical Interface.

Beide Ziele enden fast gleichzeitig. Dies ist konsistent, weil graphical.target zieht das multi-user.target ein und kann nicht vor dem multi.user target beendet werden erreicht, also fertig ist. Aber  hello.service endet viel früher.

All dies bedeutet, dass diese beiden Ziele ziemlich parallel starten. Wenn Sie die Tagebucheinträge durchsuchen, werden Sie verschiedene Ziele und Dienste von jedem dieser primären Ziele sehen, die meistens parallel beginnen. Es ist klar, dass das multi-user.target muss nicht vor graphical.target abgeschlossen werden beginnt. Daher funktioniert es nicht sehr gut, diese primären Ziele einfach zu verwenden, um den Start zu sequenzieren, obwohl es nützlich sein kann, sicherzustellen, dass Units nur dann gestartet werden, wenn sie für graphical.target benötigt werden .

Bevor Sie fortfahren, setzen Sie den hello.service zurück Unit-Datei nach WantedBy=multi-user.target (falls noch nicht geschehen.)

Stellen Sie sicher, dass ein Dienst gestartet wird, nachdem das Netzwerk ausgeführt wird

Ein häufiges Problem bei der Startsequenz besteht darin, sicherzustellen, dass eine Einheit startet, nachdem das Netzwerk hochgefahren ist und läuft. Der Freedesktop.org-Artikel Running services after the network is up sagt, dass es keinen wirklichen Konsens darüber gibt, wann ein Netzwerk als „in Betrieb“ gilt. Der Artikel bietet jedoch drei Optionen, und diejenige, die die Anforderungen eines voll funktionsfähigen Netzwerks erfüllt, ist network-online.target . Beachten Sie nur, dass network.target wird beim Herunterfahren und nicht beim Hochfahren verwendet, daher nützt es Ihnen nichts, wenn Sie versuchen, den Start zu sequenzieren.

Bevor Sie weitere Änderungen vornehmen, überprüfen Sie unbedingt das Journal und vergewissern Sie sich, dass hello.service Einheit beginnt weit vor dem Netzwerk. Sie können nach network-online.target suchen im Tagebuch zur Überprüfung.

Ihr Dienst benötigt den Netzwerkdienst nicht wirklich, aber Sie können ihn als Avatar für einen verwenden, der dies tut.

Weil WantedBy=graphical.target gesetzt wird nicht sicherstellt, dass der Dienst gestartet wird, nachdem das Netzwerk betriebsbereit ist, müssen Sie eine andere Möglichkeit sicherstellen, dass dies der Fall ist. Glücklicherweise gibt es eine einfache Möglichkeit, dies zu tun. Add the following two lines to the [Unit] section of the hello.service unit file:

After=network-online.target                                                                             
Wants=network-online.target

Both of these entries are required to make this work. Reboot the host and look for the location of entries for your service in the journals:

[   26.083121] testvm1.both.org NetworkManager[842]: <info>  [1589227764.0293] device (enp0s3): Activation: successful, device activated.
[   26.083349] testvm1.both.org NetworkManager[842]: <info>  [1589227764.0301] manager: NetworkManager state is now CONNECTED_GLOBAL
[   26.085818] testvm1.both.org NetworkManager[842]: <info>  [1589227764.0331] manager: startup complete
[   26.089911] testvm1.both.org systemd[1]: Finished Network Manager Wait Online.
[   26.090254] testvm1.both.org systemd[1]: Reached target Network is Online.
[   26.090399] testvm1.both.org audit[1]: SERVICE_START pid=1 uid=0 auid=4294967295 ses=4294967295 msg='unit=NetworkManager-wait-online comm="systemd" exe="/usr/lib/systemd/systemd" hostname=? addr=? termina>"'
[   26.091991] testvm1.both.org systemd[1]: Starting My hello shell script...
[   26.095864] testvm1.both.org sssd[be[implicit_files]][1007]: Starting up
[   26.290539] testvm1.both.org systemd[1]: Condition check resulted in Login and scanning of iSCSI devices being skipped.
[   26.291075] testvm1.both.org systemd[1]: Reached target Remote File Systems (Pre).
[   26.291154] testvm1.both.org systemd[1]: Reached target Remote File Systems.
[   26.292671] testvm1.both.org systemd[1]: Starting Notify NFS peers of a restart...
[   26.294897] testvm1.both.org systemd[1]: iscsi.service: Unit cannot be reloaded because it is inactive.
[   26.304682] testvm1.both.org hello.sh[1010]: ###############################
[   26.304682] testvm1.both.org hello.sh[1010]: ######### Hello World! ########
[   26.304682] testvm1.both.org hello.sh[1010]: ###############################
[   26.306569] testvm1.both.org audit[1]: SERVICE_START pid=1 uid=0 auid=4294967295 ses=4294967295 msg='unit=hello comm="systemd" exe="/usr/lib/systemd/systemd" hostname=? addr=? terminal=? res=success'
[   26.306669] testvm1.both.org audit[1]: SERVICE_STOP pid=1 uid=0 auid=4294967295 ses=4294967295 msg='unit=hello comm="systemd" exe="/usr/lib/systemd/systemd" hostname=? addr=? terminal=? res=success'
[   26.306772] testvm1.both.org systemd[1]: hello.service: Succeeded.
[   26.306862] testvm1.both.org systemd[1]: Finished My hello shell script.
[   26.584966] testvm1.both.org sm-notify[1011]: Version 2.4.3 starting

This confirms that the hello.service unit started after the network-online.target . This is exactly what you want. You may also have seen the "Hello World" message as it passed by during startup. Notice also that the timestamp is about six seconds later in the startup than it was before.

The best way to define the start sequence

This article explored Linux startup with systemd and unit files and journals in greater detail and discovered what happens when errors are introduced into the service file. As a sysadmin, I find that this type of experimentation helps me understand the behaviors of a program or service when it breaks, and breaking things intentionally is a good way to learn in a safe environment.

As the experiments in this article proved, just adding a service unit to either the multi-user.target or the graphical.target does not define its place in the start sequence. It merely determines whether a unit starts as part of a graphical environment or not. The reality is that the startup targets multi-user.target and graphical.target —and all of their Wants and Requires—start up pretty much in parallel. The best way to ensure that a unit starts in a specific order is to determine the unit it is dependent on and configure the new unit to "Want" and "After" the unit upon which it is dependent.

Resources

There is a great deal of information about systemd available on the internet, but much is terse, obtuse, or even misleading. In addition to the resources mentioned in this article, the following webpages offer more detailed and reliable information about systemd startup.

  • The Fedora Project has a good, practical guide to systemd. It has pretty much everything you need to know in order to configure, manage, and maintain a Fedora computer using systemd.
  • The Fedora Project also has a good cheat sheet that cross-references the old SystemV commands to comparable systemd ones.
  • For detailed technical information about systemd and the reasons for creating it, check out Freedesktop.org's description of systemd.
  • Linux.com's "More systemd fun" offers more advanced systemd information and tips.

There is also a series of deeply technical articles for Linux sysadmins by Lennart Poettering, the designer and primary developer of systemd. These articles were written between April 2010 and September 2011, but they are just as relevant now as they were then. Much of everything else good that has been written about systemd and its ecosystem is based on these papers.

  • Rethinking PID 1
  • systemd for Administrators, Part I
  • systemd for Administrators, Part II
  • systemd for Administrators, Part III
  • systemd for Administrators, Part IV
  • systemd for Administrators, Part V
  • systemd for Administrators, Part VI
  • systemd for Administrators, Part VII
  • systemd for Administrators, Part VIII
  • systemd for Administrators, Part IX
  • systemd for Administrators, Part X
  • systemd for Administrators, Part XI

Linux
  1. So verwenden Sie den Systemctl-Befehl zum Verwalten von Systemd-Diensten

  2. Verwenden von Logrotate unter Linux zum Verwalten von Protokolldateien (mit Beispielen)

  3. Wie schreibe ich ein Startskript für Systemd?

  4. Wie verwendet man Systemd, um einen Dienst neu zu starten, wenn er ausgefallen ist?

  5. So leiten Sie die Ausgabe des systemd-Dienstes in eine Datei um

Systemctl-Befehle zum Verwalten des Systemd-Dienstes

Verwenden von systemd-Funktionen zum Sichern von Diensten

So richten Sie die automatische Ausführung eines Python-Skripts mit Systemd ein

Korrekte Verwendung von Ubuntu systemctl zur Steuerung von Systemd

Systemd:Sowohl After als auch Requires verwenden

systemd-Dienstprotokolle in eine Datei umleiten