Systemd, interactieve scripts en timers

Systemd, interactieve scripts en timers

Introductie

Bij het ontwikkelen onder Linux zijn er taken zoals het maken van interactieve scripts die worden uitgevoerd wanneer het systeem wordt in- of uitgeschakeld. In systeem V was dit eenvoudig te doen, maar met systemd worden er aanpassingen gemaakt. Het kan echter wel zijn eigen timers beheren.

Waarom hebben we doelstellingen nodig?

Er wordt vaak geschreven dat targets dienen 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 een groep services starten met één commando, en extra acties uitvoeren. Bovendien hebben ze geen hiërarchie, alleen afhankelijkheden.

Voorbeeld van doel wanneer ingeschakeld (functieoverzicht) met interactieve scriptlancering

Beschrijving van het doelwit 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 wordt gestart wanneer multi-user.target wordt gestart en installer.service wordt aangeroepen. Er kunnen meerdere van dergelijke services 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 een uitvoerbaar script:

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

Het belangrijkste is om final.target te selecteren: het doel dat het systeem moet bereiken bij het opstarten. Tijdens het opstartproces doorloopt systemd de afhankelijkheden en start alles wat nodig is.
Er zijn verschillende manieren om final.target te selecteren. Ik heb hiervoor de bootloaderoptie gebruikt.

De laatste run ziet er als volgt uit:

  1. De bootloader start
  2. De bootloader start de firmware-lancering door de parameter final.target door te geven
  3. Systemd start het systeem op. Het gaat sequentieel naar installer.target of work.target vanuit basic.target via hun afhankelijkheden (bijvoorbeeld multi-user.target). Deze laatste zorgen ervoor dat het systeem in de vereiste modus werkt.

De firmware voorbereiden voor lancering

Bij het aanmaken van firmware is er altijd een taak om de systeemstatus bij het opstarten te herstellen en bij het afsluiten op te slaan. De status omvat configuratiebestanden, databasedumps, interface-instellingen, enzovoort.

Systemd voert processen in één doel parallel uit. Er zijn afhankelijkheden waarmee u de volgorde van scriptstarts kunt bepalen.

Hoe het werkt 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. Deze controleert of het bestand settings.txt in de datasectie aanwezig is. Als dit niet aanwezig is, wordt er een referentiebestand geplaatst. Vervolgens worden de systeeminstellingen hersteld:
    • beheerderswachtwoord
    • hostnaam,
    • tijdzone
    • locatie
    • Bepalen of de volledige schijf wordt gebruikt. Standaard is de image klein, zodat kopiëren en schrijven naar de schijf gemakkelijker is. Bij het opstarten wordt gecontroleerd of er nog vrije ruimte is. Zo ja, dan wordt de schijf opnieuw gepartitioneerd.
    • Genereer machine-ID op basis van MAC-adres. Dit is belangrijk om hetzelfde adres via DHCP te krijgen.
    • Netwerkinstellingen
    • De grootte van de logs is beperkt
    • Er wordt een externe schijf voorbereid voor gebruik (als de overeenkomstige optie is ingeschakeld en de schijf nieuw is)
  3. PostgreSQ starten
  4. De herstelservice wordt gestart. Deze is nodig om Zabbix zelf en de bijbehorende database voor te bereiden:
    • Controleert of er al een Zabbix-database bestaat. Zo niet, dan wordt deze aangemaakt op basis van initialisatiedumps (meegeleverd met Zabbix).
    • er wordt een lijst met tijdzones gemaakt (nodig om ze in de webinterface weer te geven)
    • Het huidige IP-adres is gevonden en wordt weergegeven in het probleem (uitnodiging om de console te betreden)
  5. De uitnodiging verandert - de zin Klaar om te werken verschijnt
  6. De firmware is klaar om te werken

De servicebestanden zijn belangrijk, zij bepalen de volgorde van hun 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 u kunt zien, heb ik afhankelijkheden geïnstalleerd zodat mijn script eerst wordt uitgevoerd en pas daarna het netwerk wordt opgestart en het DBMS wordt gestart.

En de tweede dienst (het bereiden van zabbix)

#!/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 de lancering van de postgresql DBMS en mijn setting_restore. Maar VÓÓR de lancering van de Zabbix-services.

Service met timer voor logrotate

Systemd kan CRON vervangen. Echt waar. En de nauwkeurigheid is niet tot op de minuut, maar tot op de seconde (wat als het nodig is). Of je kunt een monotone timer maken die wordt aangeroepen door een time-out van een gebeurtenis.
Het was precies deze monotone timer die de tijd aftelt vanaf het moment dat de machine die ik creëerde, werd gestart.
Hiervoor zijn 2 bestanden nodig
logrotateTimer.service — de feitelijke beschrijving van de service:

[Unit]
Description=run logrotate

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

Het is simpel: beschrijving van het lanceercommando.
Het tweede bestand logrotateTimer.timer zorgt ervoor dat de timers werken:

[Unit]
Description=Run logrotate

[Timer]
OnBootSec=15min
OnUnitActiveSec=15min

[Install]
WantedBy=timers.target

Wat is hier:

  • timerbeschrijving
  • Eerste uitvoeringstijd, vanaf het opstarten van het systeem
  • periode van verdere lanceringen
  • Afhankelijkheid van de timerservice. Deze regel doet feitelijk de timer.

Interactief script over afsluiten en uw eigen afsluitdoel

In een andere ontwikkeling moest ik een complexere versie van het afsluiten van de machine maken - via een eigen doel, om meerdere acties uit te voeren. Normaal gesproken wordt aanbevolen om een ​​eenmalige service te maken met de optie RemainAfterExit, maar dit maakt het niet mogelijk om een ​​interactief script te maken.

Het probleem is dat de opdrachten die door de optie ExecOnStop worden gestart, buiten de TTY worden uitgevoerd! Dit is eenvoudig te controleren: voer de tty-opdracht in en sla de uitvoer op.

Daarom heb ik de shutdown via mijn target geïmplementeerd. Ik beweer niet dat het 100% correct is, maar het werkt!
Hoe het werd gedaan (in algemene termen):
Een doel gemaakt, my_shutdown.target, dat van niemand afhankelijk is:
mijn_afsluiting.doel

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

Bij het overschakelen naar dit doel (via systemctl isolate my_shutdwn.target) werd de service my_shutdown.service gestart, waarvan de taak eenvoudig is: het uitvoeren van het script 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 dit script voer ik de benodigde acties uit. Je kunt meerdere scripts aan het doel toevoegen voor meer flexibiliteit en gemak:

mijn_afsluiting.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

Let op: Gebruik de bestanden /tmp/reboot en /tmp/shutdown. Je kunt target niet met parameters aanroepen. Je kunt alleen service aanroepen.

Maar ik gebruik target om flexibiliteit in mijn werk te hebben en een gegarandeerde volgorde van handelingen.

Het meest interessante gebeurde echter later. De machine moet worden uitgezet en opnieuw opgestart. Er zijn twee opties:

  • Vervang de reboot-, shutdown- en andere commando's (het zijn nog steeds symbolische links naar systemctl) door je eigen script. Ga in het script naar my_shutdown.target. De scripts in het target roepen dan systemctl rechtstreeks aan, bijvoorbeeld systemctl reboot.
  • Een eenvoudigere optie, maar ik vind hem niet prettig. Roep in alle interfaces niet shutdown/reboot/others aan, maar roep de doelserver direct systemctl isolate my_shutdown.target aan.

Ik koos de eerste optie. In systemd staan ​​bij reboot (en poweroff) symbolische links naar systemd.

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

Ze kunnen daarom vervangen worden door uw eigen scripts:
opnieuw op te starten

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

Bron: www.habr.com

Koop betrouwbare hosting voor sites met DDoS-bescherming, VPS VDS-servers 🔥 Koop betrouwbare websitehosting met DDoS-bescherming, VPS- en VDS-servers | ProHoster