Systemd, interaktiva skript och timer

Systemd, interaktiva skript och timer

Introduktion

Vid utveckling för Linux uppstår uppgifter kring skapandet av interaktiva skript som körs vid systemstart eller nedstängning. I System V var detta enkelt, men med systemd kommer det att bli förändringar. Å andra sidan har det sina egna timers.

Vad är syftet med targets

Det skrivs ofta att targets fungerar som en motsvarighet till runlevel i system V-init. Jag håller helt med om att det inte är korrekt. Det finns fler och man kan kategorisera paket i grupper och till exempel starta en grupp av tjänster med ett kommando och utföra ytterligare åtgärder. Dessutom finns det ingen hierarki, bara beroenden.

Exempel på ett target vid start (översikt av möjligheter) med körning av ett interaktivt skript

Beskrivning av själva targetet:

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

Detta target kommer att starta när multi-user.target startas och anropar installer.service. Mängden sådana tjänster kan vara flera.

cat installer.service
[Unit]
# beskrivning
Description=installer interaktiv dialog

[Service]
# Körs en gång när övriga startas
Type=idle
# Startkommando - anrop av skript
ExecStart=\/usr\/bin\/installer.sh
# Interaktiv interaktion med användaren via tty3
StandardInput=tty
TTYPath=\/dev\/tty3
TTYReset=yes
TTYVHangup=yes

[Install]
WantedBy=installer.target

Och slutligen, exempel på det körda skriptet:

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

Det viktigaste är att välja final.target - det target som systemet ska nå vid start. Under systemstart kommer systemd att följa beroenden och starta allt som behövs.
Final.target kan väljas på olika sätt, jag använde en boot-option för detta.

Den slutgiltiga uppstarten ser ut så här:

  1. Bootloadern startar
  2. Bootloadern börjar starta firmware, med parameter final.target
  3. Systemd börjar starta systemet. Det går stegvis till installer.target eller work.target från basic.target via deras beroenden (till exempel, multi-user.target). Dessa leder systemet till att fungera i det önskade läget.

Förberedelse av firmware för start

Vid skapandet av firmware uppstår alltid uppgiften att återställa systemets tillstånd vid start och att bevara det vid stängning. Med tillstånd menas konfigurationsfiler, databas-dumpar, gränssnittsinställningar, etc.

Systemd kör processer parallellt inom ett target. Det finns beroenden som kan definiera sekvensen för att köra skript.

Så här fungerar det i mitt projekt ( https://habr.com/ru/post/477008/ https://github.com/skif-web/monitor)

  1. Systemet startar
  2. Tjänsten settings_restore.service startas. Den kontrollerar om filen settings.txt finns i datasektionen. Om den inte finns, ersätts den med en standardfil. Därefter återställs systeminställningarna:
    • administratörslösenord
    • hostname,
    • tidszon
    • lokal
    • Bestämmer om hela lagringsutrymmet används. Som standard är bildstorleken liten — för bekvämlighet vid kopiering och bränning på enheten. Vid start kontrolleras om det fortfarande finns oanvänt utrymme. Om det finns — partitioneras disken om.
    • Generering av machine-id från MAC-adressen. Detta är viktigt för att få samma adress via DHCP.
    • Nätverksinställningar
    • Loggstorleken begränsas
    • Förbereder en extern disk (om den motsvarande funktionen är aktiverad och disken är ny)
  3. startar postgresq
  4. tjänsten restore startas. Den behövs för att förbereda zabbix och dess databas:
    • Kontrollerar om databasen zabbix redan finns. Om inte — skapas den från de initierande dumpfilerna (de ingår i zabbix-lösningen).
    • en lista över tidszoner skapas (nödvändigt för att visa dem i webgränssnittet)
    • Den aktuella IP:n hittas, den visas i inbjudan (prompt för att logga in i konsolen)
  5. Inbjudan ändras — frasen 'Ready to work' dyker upp
  6. Firmware är redo att användas

Tjänstefilerna är viktiga, de sätter sekvensen för deras start.

[Unit]
Description=återställ systeminställningar
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

Som man ser har jag ställt in beroenden så att mitt skript körs först, och sedan startas nätverket och databashanteraren.

Och den andra tjänsten (förberedelse av 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

Här är det lite krångligare. Starten sker också i multi-user.target, men EFTER databashanteraren postgresq och mitt setting_restore. Men FÖRE starten av zabbix-tjänsterna.

Tjänst med timer för logrotate

Systemd kan ersätta CRON. Seriöst. Dessutom med noggrannhet ner till sekunder (ifall det skulle behövas). Man kan skapa en monoton timer som triggas av en timeout från ett händelse.
Den monotoniska timern, som räknar tiden från maskinens uppstart, har jag skapat.
För detta behövs 2 filer
logrotateTimer.service — själva beskrivningen av tjänsten:

[Unit]
Description=kör logrotate

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

Allt är enkelt — beskrivning av kommandot för att starta.
Den andra filen logrotateTimer.timer — det är den som ställer in timerarbetet:

[Unit]
Description=Kör logrotate

[Timer]
OnBootSec=15min
OnUnitActiveSec=15min

[Install]
WantedBy=timers.target

Vad finns här:

  • timerbeskrivning
  • Tiden för första start, från och med systemstart
  • period för efterföljande starter
  • Beroende av timerservice. Faktiskt är det denna rad som gör timern

Interaktivt skript vid avstängning och dess avstängningsmål

I en annan utveckling var jag tvungen att göra en mer komplex variant av avstängningen av maskinen — genom ett eget mål, för att utföra många åtgärder. Det rekommenderas vanligtvis att skapa en oneshot-tjänst med alternativet RemainAfterExit, men det tillåter inte att skapa ett interaktivt skript.

Problemet är att kommandon som körs med alternativet ExecOnStop körs utanför TTY! Det är lätt att kontrollera — sätt in kommandot tty och spara dess utmatning.

Därför implementerade jag avstängning via mitt eget mål. Jag gör inga anspråk på 100 % korrekthet, men det fungerar!
Så här gjorde jag det (i stora drag):
Jag skapade målet my_shutdown.target, som inte var beroende av någon annan:
my_shutdown.target

[Enhet]
Beskrivning=min avstängning
TillåtIsolera=ja
VillHa=my_shutdown.service 

När man går till detta mål (via systemctl isolate my_shutdown.target), startade det tjänsten my_shutdown.service, vars uppgift är enkel — att utföra skriptet my_shutdown.sh:

[Enhet]
Beskrivning=MIN avstängning

[Tjänst]
Typ=oneshot
ExecStart=\/usr\/bin\/my_shutdown.sh
StandardInput=tty
TTYVäg=\/dev\/tty3
TTYÅterställning=ja
TTYVHangup=ja

ÖnskasAv=my_shutdown.target

  • Inuti detta skript utför jag de nödvändiga åtgärderna. Man kan lägga till många skript i målet för flexibilitet och bekvämlighet:

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

Obs. Användning av filer \/tmp\/reboot och \/tmp\/shutdown. Man kan inte anropa målet med parametrar. Endast tjänster är tillåtna.

Men jag använder målet för att ha flexibilitet i arbetet och garantera ordningen för åtgärdernas utförande.

Men det mest intressanta kom sedan. Maskinen måste ju stängas av\/startas om. Här finns det två alternativ:

  • Byta ut kommandona reboot, shutdown och andra (de är ändå symboliska länkar till systemctl) mot mitt skript. Inom skriptet — gå till my_shutdown.target. De interna skripten i målet anropar sedan direkt systemctl, till exempel, systemctl reboot
  • Ett enklare, men jag gillar det inte, alternativ. Använda alla gränssnitt för att kalla på inte shutdown\/reboot\/övriga, utan direkt anropa målet systemctl isolate my_shutdown.target

Jag valde det första alternativet. I systemd är reboot (som och poweroff) symboliska länkar till systemd.

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

Därför kan de ersättas med sina egna skript:
starta om

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

Källa: habr.com

Köp pålitlig webbhotell med DDoS-skydd, VPS VDS-servrar 🔥 Köp pålitlig webbhotell med DDoS-skydd, VPS VDS-servrar | ProHoster