Systemd, interactieve scripts en timers

Systemd, interactieve scripts en timers

Introductie

Bij het ontwikkelen voor Linux ontstaat de taak om interactieve scripts te maken die worden uitgevoerd wanneer het systeem wordt in- of uitgeschakeld. In systeem V was dit eenvoudig, maar bij systemd zijn er aanpassingen nodig. Maar het kan zijn eigen timers hebben.

Waarom hebben we doelen nodig?

Er wordt vaak geschreven dat target dient als een analoog van runlevel in systeem V -init. Ik ben het daar fundamenteel mee oneens. Er zijn er meer en je kunt pakketten in groepen verdelen en bijvoorbeeld met één commando een groep services starten en extra acties uitvoeren. Bovendien kennen ze geen hiërarchie, alleen afhankelijkheden.

Voorbeeld van doel indien ingeschakeld (overzicht van functies) met actief interactief script

Beschrijving van het doel zelf:

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

Dit doel start wanneer multi-user.target wordt gestart en installer.service aanroept. Er kunnen echter meerdere van dergelijke diensten zijn.

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

En tot slot een voorbeeld van het script dat wordt uitgevoerd:

#!/bin/bash
# Переходим в tty3
chvt 3
echo "Install, y/n ?"
read user_answer

Het belangrijkste is om final.target te selecteren: het doel waar het systeem bij het opstarten moet aankomen. Tijdens het opstartproces doorloopt systemd de afhankelijkheden en start alles wat het nodig heeft.
Er zijn verschillende manieren om final.target te selecteren, hiervoor heb ik de loader optie gebruikt.

De uiteindelijke lancering ziet er als volgt uit:

  1. De bootloader start
  2. De bootloader begint de firmware te starten door de parameter final.target door te geven
  3. Systemd begint het systeem te starten. Gaat opeenvolgend naar installer.target of work.target vanuit basic.target via hun afhankelijkheden (bijvoorbeeld multi-user.target). Deze laatste brengen het systeem in de gewenste modus aan het werk

De firmware voorbereiden voor lancering

Bij het maken van firmware ontstaat altijd de taak om de systeemstatus bij het opstarten te herstellen en deze op te slaan bij het afsluiten. Status betekent configuratiebestanden, databasedumps, interface-instellingen, enz.

Systemd voert processen in hetzelfde doel parallel uit. Er zijn afhankelijkheden waarmee u de opstartvolgorde van scripts kunt bepalen.

Hoe werkt het in mijn project ( https://habr.com/ru/post/477008/ https://github.com/skif-web/monitor)

  1. Het systeem start
  2. De service settings_restore.service wordt gestart en controleert op de aanwezigheid van het bestand settings.txt in de gegevenssectie. Als het er niet is, wordt er een referentiebestand voor in de plaats geplaatst en worden vervolgens de systeeminstellingen hersteld:
    • administrator wachtwoord
    • hostnaam,
    • tijdzone
    • plaats
    • Bepaalt of alle media worden gebruikt. Het beeldformaat is standaard klein, zodat u gemakkelijk kunt kopiëren en opnemen op media. Bij het opstarten wordt gecontroleerd of er nog ongebruikte ruimte is. Als dit het geval is, wordt de schijf opnieuw gepartitioneerd.
    • Machine-ID genereren op basis van MAC-adres. Dit is belangrijk voor het verkrijgen van hetzelfde adres via DHCP
    • Netwerkinstellingen
    • Beperkt de grootte van logboeken
    • De externe schijf wordt klaargemaakt voor gebruik (als de overeenkomstige optie is ingeschakeld en de schijf nieuw is)
  3. Begin postgresq
  4. De herstelservice wordt gestart. Het is nodig om zabbix zelf en zijn database voor te bereiden:
    • Controleert of er al een zabbix-database is. Als dit niet het geval is, wordt het gemaakt op basis van initialisatiedumps (meegeleverd met zabbix)
    • er wordt een lijst met tijdzones gemaakt (nodig om deze in de webinterface weer te geven)
    • Het huidige IP-adres wordt gevonden en het probleem wordt weergegeven (uitnodiging om in te loggen op de console)
  5. De uitnodiging verandert - de zin Klaar om te werken verschijnt
  6. De firmware is klaar voor gebruik

De servicebestanden zijn belangrijk, zij bepalen de volgorde van lancering

[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

Zoals je kunt zien, heb ik afhankelijkheden geïnstalleerd zodat mijn script eerst zou werken, en pas daarna zou het netwerk omhoog gaan en het DBMS zouden starten.

En de tweede service (zabbix-voorbereiding)

#!/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

Het is hier iets ingewikkelder. De lancering vindt ook plaats in multi-user.target, maar NA het starten van de postgresql DBMS en mijn setting_restore. Maar VOORDAT u zabbix-services start.

Timerservice voor logroteren

Systemd kan CRON vervangen. Ernstig. Bovendien is de nauwkeurigheid niet tot op de minuut nauwkeurig, maar tot op de seconde (wat als dat nodig is). Of je kunt een monotone timer maken, opgeroepen door een time-out van een gebeurtenis.
Het was de eentonige timer die de tijd telt vanaf het begin van de machine die ik heb gemaakt.
Hiervoor zijn 2 bestanden nodig
logrotateTimer.service - de daadwerkelijke beschrijving van de service:

[Unit]
Description=run logrotate

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

Het is eenvoudig: beschrijving van het startcommando.
In het tweede bestand logrotateTimer.timer werken de timers:

[Unit]
Description=Run logrotate

[Timer]
OnBootSec=15min
OnUnitActiveSec=15min

[Install]
WantedBy=timers.target

Wat is hier:

  • timerbeschrijving
  • Eerste starttijd, vanaf het opstarten van het systeem
  • periode van verdere lanceringen
  • Afhankelijkheid van de timerservice. In feite is dit de string die de timer maakt

Interactief script bij het afsluiten en uw afsluitdoel

In een andere ontwikkeling moest ik een complexere versie uitvoeren van het uitschakelen van de machine - via mijn eigen doelwit, om veel acties uit te voeren. Normaal gesproken wordt aanbevolen om een ​​oneshot-service te maken met de optie RemainAfterExit, maar dit voorkomt dat u een interactief script maakt.

Maar feit is dat de opdrachten die door de ExecOnStop-optie worden gelanceerd, buiten de TTY worden uitgevoerd! Het is eenvoudig te controleren: plak de opdracht tty en sla de uitvoer op.

Daarom heb ik de afsluiting via mijn doel geïmplementeerd. Ik beweer niet dat ik 100% gelijk heb, maar het werkt!
Hoe het werd gedaan (in algemene termen):
Ik heb een doel my_shutdown.target gemaakt, dat van niemand afhankelijk was:
mijn_shutdown.target

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

Toen het naar dit doel ging (via systemctl isolate my_shutdwn.target), lanceerde het de my_shutdown.service-service, waarvan de taak eenvoudig is: het uitvoeren van het my_shutdown.sh-script:

[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

  • Binnen dit script voer ik de nodige acties uit. U kunt veel scripts aan het doel toevoegen voor flexibiliteit en gemak:

mijn_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

Opmerking. Gebruik de bestanden /tmp/reboot en /tmp/shutdown. U kunt het doel niet aanroepen met parameters. Alleen service is mogelijk.

Maar ik gebruik Target om flexibiliteit in het werk te hebben en een gegarandeerde volgorde van acties.

Het meest interessante kwam echter later. De machine moet worden uitgeschakeld/opnieuw gestart. En er zijn 2 opties:

  • Vervang de reboot-, shutdown- en andere opdrachten (het zijn nog steeds symlinks naar systemctl) door uw script. Ga in het script naar my_shutdown.target. En de scripts in het doel roepen vervolgens systemctl rechtstreeks aan, bijvoorbeeld systemctl reboot
  • Een eenvoudiger optie, maar ik vind het niet leuk. Roep in alle interfaces niet shutdown/reboot/other aan, maar roep direct het doel systemctl isolate my_shutdown.target aan

Ik heb voor de eerste optie gekozen. In systemd zijn opnieuw opstarten (zoals poweroff) symbolische koppelingen naar systemd.

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

Daarom kunt u ze vervangen door uw eigen scripts:
opnieuw op te starten

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

Bron: www.habr.com

Voeg een reactie