Systemd, interaktive Skripte und Timer


Einleitung


Bei der Entwicklung für Linux gibt es Aufgaben zum Erstellen interaktiver Skripte, die ausgeführt werden, wenn das System eingeschaltet oder heruntergefahren wird. In System V war dies einfach, aber mit systemd werden Anpassungen vorgenommen. Aber es kann seine Timer tun.


Warum brauchen wir Ziel


Es wird oft geschrieben, dass target als Analogon von runlevel in system V -init dient. Ich bin grundsätzlich anderer Meinung. Es gibt mehrere von ihnen, und Sie können Pakete in Gruppen unterteilen und beispielsweise eine Gruppe von Diensten mit einem Team ausführen und zusätzliche Aktionen ausführen. Darüber hinaus haben sie keine Hierarchie, nur Abhängigkeiten.


Zielbeispiel beim Start (Funktionsübersicht) mit dem Start eines interaktiven Skripts


Beschreibung des Ziels selbst:


cat installer.target [Unit] Description=My installer Requires=multi-user.target Conflicts=rescue.service rescue.target After=multi-user.target rescue.service rescue.target AllowIsolate=yes Wants=installer.service 

Dieses Ziel wird gestartet, wenn multi-user.target gestartet wird und installer.service aufgerufen wird. Darüber hinaus kann es mehrere solcher Dienste geben.


 cat installer.service [Unit] #  Description=installer interactive dialog [Service] #   ,     Type=idle #   -   ExecStart=/usr/bin/installer.sh #      tty3 StandardInput=tty TTYPath=/dev/tty3 TTYReset=yes TTYVHangup=yes [Install] WantedBy=installer.target 

Und zum Schluss ein Beispiel für ein ausführbares Skript:


 #!/bin/bash #   tty3 chvt 3 echo "Install, y/n ?" read user_answer 

Das Wichtigste ist, final.target - target zu wählen, zu dem das System beim Start kommen soll. Während des Startvorgangs durchläuft systemd die Abhängigkeiten und führt alles aus, was Sie benötigen.
Es gibt verschiedene Möglichkeiten, final.target auszuwählen. Ich habe dafür die Bootloader-Option verwendet.


Der endgültige Start sieht folgendermaßen aus:


  1. Der Bootloader startet
  2. Der Bootloader startet die Firmware, indem er den Parameter final.target übergibt
  3. Systemd startet den Systemstart. Es wird sequentiell zu installer.target oder work.target von basic.target über deren Abhängigkeiten (z. B. multi-user.target) weitergeleitet. Letzteres und führen das System dazu, im gewünschten Modus zu arbeiten

Vorbereiten der Firmware für den Start


Beim Erstellen der Firmware besteht immer die Aufgabe, den Status des Systems beim Start wiederherzustellen und beim Ausschalten zu speichern. Status bedeutet Konfigurationsdateien, Datenbank-Dumps, Schnittstelleneinstellungen usw.


Systemd startet den Prozess in einem Ziel parallel. Es gibt Abhängigkeiten, mit denen Sie die Reihenfolge der Skriptausführung bestimmen können.


Wie es in meinem Projekt funktioniert ( https://habr.com/de/post/477008/ https://github.com/skif-web/monitor )


  1. System startet
  2. Der Dienst settings_restore.service wird gestartet und überprüft die Datei settings.txt im Datenbereich. Ist dies nicht der Fall, wird die Referenzdatei an ihre Stelle verschoben und anschließend werden die Systemeinstellungen wiederhergestellt:
    • Administratorkennwort
    • Hostname
    • Zeitzone
    • Gebietsschema
    • Bestimmt, ob alle Medien verwendet werden. Standardmäßig ist die Bildgröße klein - zum bequemen Kopieren und Aufnehmen auf Medien. Beim Start wird geprüft, ob noch ungenutzter Speicherplatz vorhanden ist. Ist dies der Fall, wird die Festplatte neu partitioniert.
    • Generieren Sie die Computer-ID aus der MAC-Adresse. Dies ist wichtig, um die gleiche Adresse über DHCP zu erhalten.
    • Netzwerkeinstellungen
    • Die Protokollgröße ist begrenzt
    • Das externe Laufwerk ist für die Arbeit vorbereitet (wenn die entsprechende Option aktiviert ist und das Laufwerk neu ist)
  3. Führen Sie postgresq aus
  4. Der Wiederherstellungsdienst wird gestartet. Es wird benötigt, um zabbix selbst und seine Datenbank vorzubereiten:
    • Überprüft, ob bereits eine zabbix-Datenbank vorhanden ist. Wenn nicht, wird es durch Initialisieren von Dumps erstellt (sie werden mit zabbix geliefert).
    • Es wird eine Liste mit Zeitzonen erstellt (erforderlich, um sie auf der Weboberfläche anzuzeigen).
    • Die aktuelle IP wird gefunden, sie wird in Ausgabe angezeigt (Einladung zum Betreten der Konsole)
  5. Die Einladung ändert sich - der Satz Bereit zur Arbeit wird angezeigt
  6. Die Firmware ist betriebsbereit.

Servicedateien sind wichtig, sie sind es, die die Reihenfolge für ihren Start festlegen


 [Unit] Description=restore system settings Before=network.service prepare.service postgresql.service systemd-networkd.service systemd-resolved.service [Service] Type=oneshot ExecStart=/usr/bin/settings_restore.sh [Install] WantedBy=multi-user.target 

Wie Sie sehen, habe ich die Abhängigkeiten so festgelegt, dass ich zuerst mein Skript ausführen und erst dann das Netzwerk ansteigen und das DBMS starten würde.


Und der zweite Dienst (Zabbix vorbereiten)


 #!/bin/sh [Unit] Description=monitor prepare system After=postgresql.service settings_restore.service Before=zabbix-server.service zabbix-agent.service [Service] Type=oneshot ExecStart=/usr/bin/prepare.sh [Install] WantedBy=multi-user.target 

Dies ist etwas komplizierter. Der Start erfolgt ebenfalls in multi-user.target, jedoch NACH dem Ausführen des postgresql-DBMS und meiner setting_restore. Aber BEVOR Sie zabbix services ausführen.


Service mit einem Timer für Logrotate


Systemd kann CRON ersetzen. Ernsthaft. Darüber hinaus beträgt die Genauigkeit nicht bis zu einer Minute, sondern bis zu einer Sekunde (was ist, wenn dies erforderlich ist) .Und Sie können einen eintönigen Timer erstellen, der durch Timeout des Ereignisses aufgerufen wird.
Es war der monotone Timer, der die Zeit ab dem Start der von mir erstellten Maschine zählt.
Dies erfordert 2 Dateien
logrotateTimer.service - die tatsächliche Beschreibung des Dienstes:


 [Unit] Description=run logrotate [Service] ExecStart=logrotate /etc/logrotate.conf TimeoutSec=300 

Es ist einfach - eine Beschreibung des Startbefehls.
Die zweite logrotateTimer.timer-Datei setzt die Timer auf Arbeit:


 [Unit] Description=Run logrotate [Timer] OnBootSec=15min OnUnitActiveSec=15min [Install] WantedBy=timers.target 

Was ist da:


  • Timer Beschreibung
  • Zum ersten Mal ab dem Systemstart
  • Zeitraum weiterer Starts
  • Timer Service Dependency. Eigentlich ist dies die Zeile und macht den Timer

Interaktives Skript zum Herunterfahren und benutzerdefiniertes Herunterfahrziel


In einer anderen Entwicklung musste ich eine komplexere Version des Ausschaltens der Maschine erstellen - durch mein eigenes Ziel, um viele Aktionen auszuführen. Es wird normalerweise empfohlen, den Dienst "oneshot" mit der Option "RemainAfterExit" zu erstellen, dies verhindert jedoch die Erstellung eines interaktiven Skripts.


Fakt ist jedoch, dass die von der ExecOnStop-Option gestarteten Befehle außerhalb von TTY ausgeführt werden! Die Prüfung ist einfach - fügen Sie den Befehl tty ein und speichern Sie die Ausgabe.


Daher habe ich das Herunterfahren über mein Ziel implementiert. Ich gebe nicht vor, 100% korrekt zu sein, aber es funktioniert!
Wie es gemacht wurde (allgemein):
Erstellt das Ziel my_shutdown.target, das von niemandem abhängt:
my_shutdown.target


 [Unit] Description=my shutdown AllowIsolate=yes Wants=my_shutdown.service 

Beim Wechsel zu diesem Ziel (über systemctl isolate my_shutdwn.target) startete er den Dienst my_shutdown.service, dessen Aufgabe einfach ist: Ausführen des Skripts my_shutdown.sh:


 [Unit] Description=MY shutdown [Service] Type=oneshot ExecStart=/usr/bin/my_shutdown.sh StandardInput=tty TTYPath=/dev/tty3 TTYReset=yes TTYVHangup=yes WantedBy=my_shutdown.target 

  • In diesem Skript führe ich die erforderlichen Aktionen aus. Sie können dem Ziel aus Gründen der Flexibilität und Bequemlichkeit eine Vielzahl von Skripten hinzufügen:

my_shutdown.sh


 #!/bin/bash --login if [ -f /tmp/reboot ];then command="systemctl reboot" elif [ -f /tmp/shutdown ]; then command="systemctl poweroff" fi #    #, cp /home/user/data.txt /storage/user/ $command 

Hinweis Verwenden der Dateien / tmp / reboot und / tmp / shutdown. Sie können target nicht mit Parametern aufrufen. Sie können nur warten.


Aber ich benutze target, um Flexibilität in der Arbeit und eine garantierte Abfolge von Aktionen zu haben.


Das Interessanteste war jedoch später. Die Maschine muss ausgeschaltet / neu gestartet werden. Und es gibt zwei Möglichkeiten:


  • Ersetzen Sie Neustart, Herunterfahren und andere Befehle (sie sind immer noch Symlinks auf systemctl) durch Ihr eigenes Skript. Wechseln Sie innerhalb des Skripts zu my_shutdown.target. Die Skripte im Ziel rufen dann direkt systemctl auf, z. B. systemctl reboot
  • Eine einfachere, aber mir gefällt die Option nicht. Rufen Sie in allen Schnittstellen nicht shutdown / reboot / others auf, sondern rufen Sie direkt das Zielsystem auf

Ich habe die erste Option gewählt. In systemd sind Neustarts (wie das Ausschalten) Symlinks auf systemd.


 ls -l /sbin/poweroff lrwxrwxrwx 1 root root 14  30 18:23 /sbin/poweroff -> /bin/systemctl 

Sie können daher durch Ihre eigenen Skripte ersetzt werden:
neustarten


 #!/bin/sh touch /tmp/reboot sudo systemctl isolate my_shutdown.target fi 

Source: https://habr.com/ru/post/de478846/


All Articles