
Introduction
Lors du développement sous Linux, des tâches sont nécessaires pour créer des scripts interactifs exécutés lors du démarrage ou de l'arrêt de la système. Avec system V, cela était facile, mais avec systemd, des modifications sont apportées. Cependant, il sait gérer ses propres minuteries.
À quoi servent les targets
On dit souvent que les targets sont l'analogue des runlevels dans system V -init. Je ne suis pas du tout d'accord. Il y en a plus et il est possible de regrouper des paquets par groupes et, par exemple, de lancer un ensemble de services avec une seule commande, d'effectuer des actions supplémentaires. En outre, il n'y a pas de hiérarchie, seulement des dépendances.
Exemple de target au démarrage (aperçu des fonctionnalités) avec lancement d'un script interactif
Description de la target elle-même :
cat installer.target
[Unit]
Description=Mon installateur
Requires=multi-user.target
Conflicts=rescue.service rescue.target
After=multi-user.target rescue.service rescue.target
AllowIsolate=yes
Wants=installer.serviceCette target se lancera lorsque multi-user.target sera démarré et appellera installer.service. Plusieurs de ces services peuvent exister.
cat installer.service
[Unit]
# description
Description=dialogue interactif de l'installateur
[Service]
# À lancer une fois, lorsque le reste sera démarré
Type=idle
# Commande de lancement - appel du script
ExecStart=\/usr\/bin\/installer.sh
# Interaction interactive avec l'utilisateur via tty3
StandardInput=tty
TTYPath=\/dev\/tty3
TTYReset=yes
TTYVHangup=yes
[Install]
WantedBy=installer.targetEt enfin, un exemple de script exécuté :
#!/bin/bash
# Переходим в tty3
chvt 3
echo "Install, y/n ?"
read user_answerLe plus important est de choisir final.target, la target à laquelle le système doit parvenir au démarrage. Pendant le démarrage, systemd suivra les dépendances et lancera tout ce qui est nécessaire.
Le choix de final.target peut se faire de différentes manières, j'ai utilisé pour cela l'option de l'amorçage.
Le démarrage final ressemble à ceci :
- Le chargeur de démarrage démarre
- Le chargeur de démarrage commence à lancer le firmware, en passant le paramètre final.target
- Systemd commence à démarrer le système. Il va successivement de installer.target ou work.target à basic.target via leurs dépendances (par exemple, multi-user.target). Ces dernières amènent le système à fonctionner dans le mode souhaité.
Préparation du firmware pour le lancement
Lors de la création de firmwares, il y a toujours la tâche de restaurer l'état du système au démarrage et de le conserver à l'arrêt. L'état comprend des fichiers de configuration, des dumps de base de données, des paramètres d'interfaces, etc.
Systemd lance des processus dans une même target en parallèle. Il existe des dépendances qui permettent de déterminer l'ordre de lancement des scripts.
Comment cela fonctionne dans mon projet ( )
- Le système démarre
- Le service settings_restore.service est lancé. Il vérifie la présence du fichier settings.txt dans la section des données. S'il est absent, un fichier de référence est placé à sa place. Ensuite, la restauration des paramètres du système commence :
- du mot de passe administrateur
- hostname,
- fuseau horaire
- localisation
- Détermination de l'utilisation complète du support. Par défaut, la taille de l'image est petite — pour faciliter la copie et l'enregistrement sur le support. Au démarrage, il est vérifié s'il y a encore de l'espace inutilisé. Si c'est le cas, le disque est redimensionné.
- Génération du machine-id à partir de l'adresse MAC. C'est important pour obtenir la même adresse via DHCP
- Paramètres réseau
- Limite la taille des journaux
- Préparation d'un disque externe (si l'option correspondante est activée et que le disque est neuf)
- Démarrage de postgresq
- le service restore est lancé. Il est nécessaire à la préparation même de zabbix et de sa base de données :
- Vérification de l'existence de la base de données zabbix. Si elle n'existe pas, elle est créée à partir des dumps d'initialisation (inclus dans la livraison de zabbix)
- une liste des fuseaux horaires est créée (nécessaire pour leur affichage dans l'interface web)
- Obtention de l'IP actuelle, qui est affichée dans l'invite (invitation à se connecter à la console)
- Changement de l'invite — la phrase 'Prêt à travailler' apparaît
- Le firmware est prêt à l'emploi
Les fichiers de services sont importants, car c'est eux qui définissent l'ordre de leur lancement
[Unit]
Description=restaurer les paramètres système
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.targetComme on peut le voir, j'ai défini des dépendances pour que mon script s'exécute d'abord, puis que le réseau monte et que le SGBD démarre.
Et le second service (préparation de 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.targetC'est un peu plus compliqué. Le lancement se fait aussi dans multi-user.target, mais APRÈS le lancement du SGBD postgresql et de mon setting_restore. Mais AVANT le lancement des services zabbix.
Service avec un minuteur pour logrotate
Systemd peut remplacer CRON. Sérieusement. De plus, la précision n'est pas à la minute, mais à la seconde (au cas où cela serait nécessaire). On peut également créer un minuteur monotone, déclenché par un timeout d'événements.
C'est précisément le minuteur monotone, comptant le temps depuis le démarrage de la machine, que j'ai créé.
Pour cela, deux fichiers seront nécessaires
logrotateTimer.service — la description du service à proprement parler :
[Unit]
Description=exécuter logrotate
[Service]
ExecStart=logrotate \/etc\/logrotate.conf
TimeoutSec=300C'est simple — la description de la commande d'exécution.
Le second fichier logrotateTimer.timer — c'est lui qui définit le fonctionnement des minuteurs :
[Unit]
Description=Exécuter logrotate
[Timer]
OnBootSec=15min
OnUnitActiveSec=15min
[Install]
WantedBy=timers.targetQue contient ceci :
- description du minuteur
- Temps de premier démarrage, à partir du chargement du système
- période des démarrages ultérieurs
- Dépendance au service des minuteurs. En fait, c'est cette ligne qui crée le minuteur
Script interactif lors de l'arrêt et son target d'arrêt
Dans un autre développement, j'ai dû faire une version plus complexe pour éteindre la machine — via mon propre target, afin d'effectuer de nombreuses actions. Il est généralement recommandé de créer un service oneshot avec l'option RemainAfterExit, mais cela n'offre pas la possibilité de créer un script interactif.
Le problème est que les commandes exécutées avec l'option ExecOnStop s'exécutent en dehors de TTY ! Vérifiez simplement — insérez la commande tty et conservez sa sortie.
C'est pourquoi j'ai mis en œuvre l'arrêt via mon propre target. Je ne prétends pas à 100 % de précision, mais cela fonctionne !
Comment cela a été fait (en gros) :
J'ai créé le target my_shutdown.target, qui ne dépendait de personne :
my_shutdown.target
[Unit]
Description=mon arrêt
AllowIsolate=yes
Wants=my_shutdown.service En passant à ce target (via systemctl isolate my_shutdwn.target), il lançait le service my_shutdown.service, dont la tâche était simple — exécuter le script my_shutdown.sh :
[Unit]
Description=MON arrêt
[Service]
Type=oneshot
ExecStart=\/usr\/bin\/my_shutdown.sh
StandardInput=tty
TTYPath=\/dev\/tty3
TTYReset=yes
TTYVHangup=yes
WantedBy=my_shutdown.target- À l'intérieur de ce script, j'exécute les actions nécessaires. On peut ajouter de nombreux scripts au target, pour plus de flexibilité et de commodité :
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/
$commandRemarque. Utilisation des fichiers \/tmp\/reboot et \/tmp\/shutdown. On ne peut pas appeler le target avec des paramètres. On peut seulement appeler le service.
Mais j'utilise le target pour avoir de la flexibilité dans le travail et un ordre d'exécution des actions garanti.
Cependant, le plus intéressant est ce qui vient après. Il faut éteindre/redémarrer la machine. Et ici, il y a deux options :
- Remplacer les commandes reboot, shutdown et autres (qui sont toutes des liens symboliques vers systemctl) par mon propre script. À l'intérieur du script — passage au my_shutdown.target. Et les scripts à l'intérieur du target appellent ensuite directement systemctl, par exemple systemctl reboot.
- Une option plus simple, mais qui ne me plaît pas. Dans toutes les interfaces, appeler non pas shutdown/reboot/ autres, mais appeler directement le target systemctl isolate my_shutdown.target.
J'ai choisi la première option. Dans systemd, reboot (comme poweroff) est un lien symbolique vers systemd.
ls -l \/sbin\/poweroff
lrwxrwxrwx 1 root root 14 sep 30 18:23 \/sbin\/poweroff -> \/bin\/systemctlC'est pourquoi ils peuvent être remplacés par mes propres scripts :
reboot
#!/bin/sh
touch /tmp/reboot
sudo systemctl isolate my_shutdown.target
fiSource : habr.com
