
Uvod
Prilikom razvoja pod linuxom pojavljuju se zadatci stvaranja interaktivnih skripti koje se izvršavaju pri uključivanju ili gašenju sustava. U system V to je bilo jednostavno, ali sa systemd dovodi do promjena. No, ono ima vlastite tajmere.
Zašto su potrebni targeti
Često se piše da su targeti ekvivalent runlevelu u system V -init. U osnovi se ne slažem s tim. Ima ih više, mogu se dijeliti paketi po grupama i, primjerice, pokrenuti jednom naredbom grupu servisa, izvršiti dodatne radnje. Osim toga, nemaju hijerarhiju, nego samo zavisnosti.
Primjer targeta pri uključivanju (pregled mogućnosti) s pokretanjem interaktivne skripte
Opis samog targeta:
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.serviceOvaj target pokrenut će se kada se aktivira multi-user.target i pozvati installer.service. U tom slučaju može biti više takvih servisa.
cat installer.service
[Unit]
# opis
Description=installer interactive dialog
[Service]
# Pokrenuti jednom, kada se ostalo pokrene
Type=idle
# Naredba pokretanja - pozivanje skripte
ExecStart=\/usr\/bin\/installer.sh
# Interaktivna interakcija s korisnikom putem tty3
StandardInput=tty
TTYPath=\/dev\/tty3
TTYReset=yes
TTYVHangup=yes
[Install]
WantedBy=installer.targetI konačno, primjer izvršene skripte:
#!/bin/bash
# Переходим в tty3
chvt 3
echo "Install, y/n ?"
read user_answerNajvažnije je odabrati final.target - target kojem sustav treba doći pri pokretanju. Tijekom pokretanja systemd će proći kroz zavisnosti i pokrenuti sve što je potrebno.
Odabir final.target može se obaviti na razne načine, ja sam koristio opciju pokretača.
Završno pokretanje izgleda ovako:
- Pokreće se pokretač
- Pokretač počinje pokretati firmware, prosljeđujući parametar final.target
- Systemd započinje s pokretanjem sustava. Posljedovno ide do installer.target ili work.target sa basic.target preko njihovih zavisnosti (na primjer, multi-user.target). Posljednji dovode sustav do rada u potrebnom režimu.
Priprema firmwarea za pokretanje
Prilikom stvaranja firmwarea uvijek se javlja potreba za obnavljanjem stanja sustava pri pokretanju i njegovim očuvanjem pri gašenju. Pod stanjem se podrazumijevaju konfiguracijske datoteke, dumpovi baza podataka, postavke sučelja itd.
Systemd pokreće procese unutar jednog targeta paralelno. Postoje zavisnosti koje omogućuju određivanje redoslijeda pokretanja skripti.
Kako to funkcionira u mom projektu ( )
- Sustav se pokreće
- Pokreće se usluga settings_restore.service. Provjerava postojanje datoteke settings.txt u dijelu s podacima. Ako je nema, zamjenjuje se uzorkom. Zatim dolazi do obnove postavki sustava:
- administratorska lozinka
- hostname,
- vremenska zona
- lokal
- Utvrđuje se koristi li se sav medij. Zadana veličina slike je mala — radi jednostavnosti kopiranja i zapisivanja na medij. Pri pokretanju provjerava se — ima li još neiskorištenog mjesta. Ako ima — disk se ponovo particionira.
- Generiranje machine-id iz MAC adrese. To je važno za dobivanje istog adresnog mjesta putem DHCP-a.
- Mrežne postavke
- Ograničava se veličina logova
- Priprema se vanjski disk (ako je uključena odgovarajuća opcija i disk je nov)
- Pokreće se postgresq
- pokreće se usluga restore. Ona je potrebna za pripremu samog zabbixa i njegove baze podataka:
- Provjerava se postoji li već baza podataka zabbix. Ako ne, stvara se iz inicijalizacijskih dumpova (dolaze u isporuci zabbixa)
- stvara se popis vremenskih zona (potrebno za njihovo prikazivanje u web sučelju)
- Nalazi se trenutni IP, ispisuje se u issue (poziv za prijavu u konzolu)
- Mijenja se poziv — pojavljuje se fraza Ready to work
- Firmware je spreman za rad
Važne su datoteke usluga, one određuju redoslijed njihova pokretanja
[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.targetKao što se može vidjeti, postavio sam ovisnosti tako da prvo radi moj skript, a zatim se pokreće mreža i starta SGBD.
I druga usluga (priprema zabbixa)
#!/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.targetOvdje je malo složenije. Pokretanje također u multi-user.target, ali NAKON pokretanja SGBD postgresql i mog setting_restore. Ali PRIJE pokretanja zabbix usluga.
Usluga s timerom za logrotate
Systemd može zamijeniti CRON. Ozbiljno. Preciznost nije do minute, već do sekunde (a možda će zatrebati). Može se stvoriti monotoni timer, pozivan po vremenskom okvira pomoćom događaja.
Taj monotoni timer, koji broji vrijeme od pokretanja stroja, sam stvorio.
Za to će biti potrebne 2 datoteke
logrotateTimer.service — samo opis usluge:
[Unit]
Description=run logrotate
[Service]
ExecStart=logrotate \/etc\/logrotate.conf
TimeoutSec=300Sve je jednostavno — opis komande pokretanja.
Druga datoteka logrotateTimer.timer — ona određuje rad timer-a:
[Unit]
Description=Run logrotate
[Timer]
OnBootSec=15min
OnUnitActiveSec=15min
[Install]
WantedBy=timers.targetŠto je ovdje?
- opis timera
- Vrijeme prvog pokretanja, počevši od učitavanja sustava
- period daljnjih pokretanja
- Ovisnost o usluzi timera. U suštini, ovo je linija koja stvara timer
Interaktivni skript prilikom gašenja i njegov cilj gašenja
U drugom razvoju, morao sam napraviti složeniju varijantu gašenja stroja — preko vlastitog cilja, kako bih izvršio mnoge radnje. Obično se preporučuje stvaranje oneshot servisa s opcijom RemainAfterExit, no to ne omogućuje stvaranje interaktivnog skripta.
Problem je u tome što se naredbe koje se pokreću opcijom ExecOnStop izvršavaju izvan TTY-a! Provjerite jednostavno — umetnite naredbu tty i spremite njezin izlaz.
Stoga sam implementirao gašenje preko vlastitog cilja. Ne tvrdim da je 100% ispravno, ali radi!
Kako je to učinjeno (u općim crtama):
Stvorio sam cilj my_shutdown.target, koji nije zavisio od drugih:
my_shutdown.target
[Unit]
Description=moje gašenje
AllowIsolate=yes
Wants=my_shutdown.service Prilikom prelaska u ovaj cilj (kroz systemctl isolate my_shutdown.target), pokreće se servis my_shutdown.service, čija je zadaća jednostavna — izvršiti skript my_shutdown.sh:
[Unit]
Description=MOJE gašenje
[Service]
Type=oneshot
ExecStart=\/usr\/bin\/my_shutdown.sh
StandardInput=tty
TTYPath=\/dev\/tty3
TTYReset=yes
TTYVHangup=yes
WantedBy=my_shutdown.target- Unutra ovog skripta izvršavam potrebne radnje. Mogu dodati mnogo skripti u cilj, za fleksibilnost i praktičnost:
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/
$commandNapomena. Korištenje datoteka \/tmp\/reboot i \/tmp\/shutdown. Nije moguće pozvati cilj s parametrima. Moguće je samo usluge.
No koristim cilj kako bih imao fleksibilnost u radu i jamstveni redoslijed izvršavanja radnji.
Međutim, najzanimljivije je bilo kasnije. Stroju je potrebno isključiti\/ponovo pokrenuti. A tu su dva izbora:
- Zamijeniti naredbe reboot, shutdown i druge (one su ionako simboličke poveznice na systemctl) svojim skriptom. Unutar skripta — prijelaz u my_shutdown.target. A skripte unutar cilja zatim izravno pozivaju systemctl, na primjer, systemctl reboot
- Jednostavnija, ali ne sviđa mi se opcija. U svim sučeljima pozivati ne shutdown\/reboot\/drugo, nego izravno pozivati cilj systemctl isolate my_shutdown.target
Odlučio sam se za prvu opciju. U systemd reboot (kao i poweroff) su simboličke poveznice na systemd.
ls -l \/sbin\/poweroff
lrwxrwxrwx 1 root root 14 ruj 30 18:23 \/sbin\/poweroff -> \/bin\/systemctlStoga se oni mogu zamijeniti svojim skriptama:
restart
#!/bin/sh
touch /tmp/reboot
sudo systemctl isolate my_shutdown.target
fiIzvor: habr.com
