Systemd, interactieve scripts en timers

Systemd, interactieve scripts en timers

Inleiding

Bij het ontwikkelen onder Linux ontstaat de taak om interactieve scripts te creëren die worden uitgevoerd bij het opstarten of afsluiten van het systeem. Dit was eenvoudig met System V, maar systemd brengt wijzigingen aan. Echter, het kan zijn eigen timers aan.

Waarom zijn targets nodig

Vaak wordt geschreven dat targets als het equivalent van runlevels in System V-init dienen. Hier ben ik helemaal niet mee eens. Er zijn meer en je kunt pakketten in groepen verdelen en bijvoorbeeld een groep services met één commando starten of aanvullende acties uitvoeren. Bovendien hebben ze geen hiërarchie, alleen afhankelijkheden.

Voorbeeld target bij opstarten (overzicht van mogelijkheden) met het starten van een interactief script

Beschrijving van de target zelf:

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

Deze target wordt opgestart wanneer multi-user.target wordt geactiveerd en roept installer.service aan. Hierbij kunnen er meerdere van dergelijke services zijn.

cat installer.service
[Unit]
# beschrijving
Description=installer interactieve dialoog

[Service]
# Eenmalig starten, wanneer de rest is opgestart
Type=idle
# Startcommando - aanroepen van het script
ExecStart=/usr/bin/installer.sh
# Interactieve interactie met de gebruiker via tty3
StandardInput=tty
TTYPath=/dev/tty3
TTYReset=yes
TTYVHangup=yes

[Install]
WantedBy=installer.target

En tenslotte, een voorbeeld van het uit te voeren script:

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

Het belangrijkste is om final.target te kiezen - de target waaraan het systeem moet komen bij het opstarten. Tijdens het opstartproces zal systemd de afhankelijkheden doorlopen en alles noodzakelijke opstarten.
Het kiezen van final.target kan op verschillende manieren, ik heb hiervoor de optie van de bootloader gebruikt.

De uiteindelijke opstart ziet er als volgt uit:

  1. De bootloader start op
  2. De bootloader begint met het opstarten van de firmware, door het parameter final.target door te geven
  3. Systemd begint met het starten van het systeem. Het gaat geleidelijk naar installer.target of work.target vanaf basic.target via hun afhankelijkheden (bijvoorbeeld multi-user.target). Laatstgenoemden brengen het systeem in de gewenste werkmodus.

Voorbereiding van de firmware voor opstarten

Bij het creëren van firmware ontstaat altijd de taak om de systeemstatus bij opstarten te herstellen en deze bij uitschakeling op te slaan. Met status wordt gedoeld op configuratiebestanden, database-dumps, interface-instellingen, enzovoort.

Systemd start processen in één target parallel. Er zijn afhankelijkheden die de volgorde van het starten van scripts bepalen.

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

  1. Het systeem start op
  2. De service settings_restore.service wordt gestart. Deze controleert of het bestand settings.txt aanwezig is in de datasectie. Als dat niet het geval is, wordt er een standaardbestand neergelegd. Vervolgens wordt het systeem hersteld.
    • beheerderswachtwoord
    • hostname,
    • tijdzone
    • locale
    • Bepalen of het opslagmedium volledig gebruikt wordt. Standaard is de afbeeldingsgrootte klein — voor eenvoudig kopiëren en schrijven op het medium. Bij de start wordt gecontroleerd of er nog ongebruikte ruimte is. Als die er is, wordt de schijf opnieuw gepartitioneerd.
    • Generatie van machine-id uit het MAC-adres. Dit is belangrijk voor het verkrijgen van hetzelfde adres via DHCP.
    • Netwerkinstellingen
    • Degrootte van logbestanden wordt beperkt.
    • De externe schijf wordt voorbereid (als de bijbehorende optie is ingeschakeld en de schijf nieuw is).
  3. wordt gestart postgresq
  4. de service restore wordt gestart. Dit is nodig voor de voorbereiding van zabbix en zijn database:
    • Er wordt gecontroleerd of er al een zabbix-database bestaat. Zo niet, dan wordt deze aangemaakt vanaf de initiële dumps (die bij zabbix worden geleverd).
    • er wordt een lijst van tijdzones aangemaakt (nodig voor de weergave in de webinterface).
    • Het huidige IP wordt gevonden en weergegeven in de issue (de uitnodiging voor inloggen in de console).
  5. De prompt verandert — de zin 'Klaar om te werken' verschijnt.
  6. De firmware is klaar voor gebruik.

De bestanden van de services zijn belangrijk, zij bepalen de volgorde van hun start.

[Unit]
Description=herstel systeeminstellingen
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 te zien is, heb ik afhankelijkheden ingesteld, zodat eerst mijn script wordt uitgevoerd, en pas daarna het netwerk wordt opgestart en de DBMS wordt gestart.

En de tweede service (voorbereiding 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

Hier is het iets ingewikkelder. De opstart gebeurt ook in multi-user.target, maar NA de opstart van de DBMS postgresql en mijn setting_restore. Maar VOORDAT de zabbix-diensten worden gestart.

Service met een timer voor logrotate.

Systemd kan CRON vervangen. Serieus. En de nauwkeurigheid is niet tot op een minuut, maar tot op een seconde (voor het geval dat). En je kunt een monotone timer maken, die wordt aangeroepen op basis van een timeout van een gebeurtenis.
De monotone timer, die de tijd vanaf de opstart van de machine telt, heb ik gemaakt.
Hiervoor zijn 2 bestanden nodig.
logrotateTimer.service — dit is de beschrijving van de service:

[Unit]
Description=voer logrotate uit

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

Heel eenvoudig — beschrijving van het startcommando.
Het tweede bestand logrotateTimer.timer — dit stelt de werking van de timers in:

[Unit]
Description=Voer logrotate uit

[Timer]
OnBootSec=15min
OnUnitActiveSec=15min

[Install]
WantedBy=timers.target

Wat is hier aanwezig:

  • beschrijving van de timer
  • Tijd van de eerste start, vanaf het opstarten van de systemen
  • periode van daaropvolgende starts
  • Afhankelijkheid van de timer-service. Eigenlijk is dit de regel die de timer maakt

Interactief script bij uitschakeling en zijn doeltarget voor uitschakeling

In een andere ontwikkeling moest ik een complexere versie van het uitschakelen van de machine maken — via mijn eigen target, om meerdere acties uit te voeren. Gewoonlijk wordt aangeraden een oneshot-service te creëren met de optie RemainAfterExit, maar dat maakt het niet mogelijk een interactief script te maken.

Het punt is dat de commando's die met de optie ExecOnStop worden uitgevoerd, buiten TTY plaatsvinden! Het is eenvoudig te controleren — voer het commando tty in en sla de uitvoer op.

Daarom heb ik het uitschakelen via mijn eigen target geïmplementeerd. Ik claim niet 100% correctheid, maar het werkt!
Hoe dit gedaan werd (in grote lijnen):
Ik heb het target my_shutdown.target gemaakt, dat van niemand afhankelijk was:
my_shutdown.target

[Unit]
Beschrijving=mijn uitschakeling
AllowIsolate=yes
Wants=my_shutdown.service 

Bij het overschakelen naar dit target (via systemctl isolate my_shutdwn.target), startte het de service my_shutdown.service, waarvan de taak eenvoudig is — het uitvoeren van het script my_shutdown.sh:

[Unit]
Beschrijving=MIJN uitschakeling

[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. Je kunt veel scripts aan het target toevoegen voor flexibiliteit en gemak:

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

Opmerking. Gebruik van de bestanden /tmp/reboot en /tmp/shutdown. Je kunt het target niet met parameters aanroepen. Alleen service kan dat.

Maar ik gebruik het target om flexibiliteit in de uitvoering te garanderen en een gegarandeerde volgorde van acties.

Maar het meest interessante kwam daarna. De machine moet immers worden uitgeschakeld/herstart. En hier zijn er 2 opties:

  • Vervang de commando's reboot, shutdown en anderen (ze zijn toch symbolische koppelingen naar systemctl) door je eigen script. Binnen dat script ga je naar my_shutdown.target. En de scripts binnen het target roepen dan direct systemctl aan, bijvoorbeeld systemctl reboot.
  • Een eenvoudigere, maar minder aantrekkelijke optie. In alle interfaces zou je niet shutdown/reboot/andre moeten aanroepen, maar rechtstreeks het target systemctl isolate my_shutdown.target aanroepen.

Ik koos voor de eerste optie. In systemd zijn reboot (net als poweroff) symbolische koppelingen naar systemd.

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

Daarom kunnen ze worden vervangen door je eigen scripts:
herstarten

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

Bron: habr.com

Koop betrouwbare webhosting met bescherming tegen DDoS, VPS VDS servers 🔥 Koop betrouwbare webhosting met bescherming tegen DDoS, VPS VDS servers | ProHoster