Systemd, scripts interactivos e temporizadores

Systemd, scripts interactivos e temporizadores

Introdución

Ao desenvolver para Linux, xorde a tarefa de crear scripts interactivos que se executan cando o sistema está acendido ou apagado. No sistema V isto foi sinxelo, pero con systemd fai axustes. Pero pode ter os seus propios temporizadores.

Por que necesitamos obxectivos?

Adoita escribirse que o destino serve como análogo do nivel de execución no sistema V -init. Estou fundamentalmente en desacordo. Hai máis deles e podes dividir paquetes en grupos e, por exemplo, lanzar un grupo de servizos cun só comando e realizar accións adicionais. Ademais, non teñen xerarquía, só dependencias.

Exemplo de destino cando está activado (descrición xeral das funcións) cun script interactivo en execución

Descrición do propio obxectivo:

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

Este obxectivo comezará cando se inicie multi-user.target e chame a installer.service. Non obstante, pode haber varios destes servizos.

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

E, finalmente, un exemplo do script que se está executando:

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

O máis importante é seleccionar final.target: o obxectivo ao que debería chegar o sistema ao iniciarse. Durante o proceso de inicio, systemd pasará polas dependencias e lanzará todo o que necesite.
Hai diferentes formas de seleccionar final.target, usei a opción do cargador para iso.

O lanzamento final é así:

  1. Iníciase o cargador de arranque
  2. O cargador de arranque comeza a iniciar o firmware pasando o parámetro final.target
  3. Systemd comeza a iniciar o sistema. Vai secuencialmente a installer.target ou work.target desde basic.target a través das súas dependencias (por exemplo, multi-user.target). Estes últimos traen o sistema a funcionar no modo desexado

Preparando o firmware para o lanzamento

Ao crear firmware, sempre xorde a tarefa de restaurar o estado do sistema ao iniciar e gardalo ao apagar. Estado significa ficheiros de configuración, volcados de bases de datos, configuración da interface, etc.

Systemd executa procesos no mesmo destino en paralelo. Hai dependencias que che permiten determinar a secuencia de inicio dos scripts.

Como funciona no meu proxecto ( https://habr.com/ru/post/477008/ https://github.com/skif-web/monitor)

  1. O sistema comeza
  2. Iníciase o servizo settings_restore.service, que comproba a presenza do ficheiro settings.txt na sección de datos. Se non está alí, colócase un ficheiro de referencia no seu lugar. A continuación, restablece a configuración do sistema:
    • contrasinal de administrador
    • nome de host,
    • franxa horaria
    • local
    • Determina se se están a utilizar todos os medios. De xeito predeterminado, o tamaño da imaxe é pequeno, para facilitar a copia e a gravación no soporte. No inicio, comproba se aínda hai espazo sen utilizar. Se o hai, o disco está reparticionado.
    • Xerando o ID da máquina a partir do enderezo MAC. Isto é importante para obter o mesmo enderezo a través de DHCP
    • Configuración da rede
    • Limita o tamaño dos rexistros
    • A unidade externa estase preparando para traballar (se a opción correspondente está activada e a unidade é nova)
  3. Iniciar postgresq
  4. Comeza o servizo de restauración. É necesario para preparar o propio zabbix e a súa base de datos:
    • Comproba se xa hai unha base de datos zabbix. Se non, créase a partir de volcados de inicialización (incluído con zabbix)
    • créase unha lista de fusos horarios (necesario para mostralos na interface web)
    • Atópase a IP actual, móstrase no problema (invitación para iniciar sesión na consola)
  5. A invitación cambia: aparece a frase Listo para traballar
  6. O firmware está listo para o seu uso

Os ficheiros de servizo son importantes, son os que marcan a secuencia do seu lanzamento

[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

Como podes ver, instalei dependencias para que primeiro funcionase o meu script, e só entón a rede subiría e comezase o DBMS.

E o segundo servizo (preparación 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.target

Aquí é un pouco máis complicado. O lanzamento tamén está en multi-user.target, pero DESPOIS de iniciar o DBMS postgresql e o meu setting_restore. Pero ANTES de comezar os servizos zabbix.

Servizo de temporizador para logrotate

Systemd pode substituír CRON. En serio. Ademais, a precisión non está á altura do minuto, senón ao segundo (e se é necesario) ou pode crear un temporizador monótono, chamado por un tempo de espera dun evento.
Era o temporizador monótono que conta o tempo desde o inicio da máquina que creei.
Isto requirirá 2 ficheiros
logrotateTimer.service - a descrición real do servizo:

[Unit]
Description=run logrotate

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

É sinxelo: descrición do comando de inicio.
O segundo ficheiro logrotateTimer.timer é onde funcionan os temporizadores:

[Unit]
Description=Run logrotate

[Timer]
OnBootSec=15min
OnUnitActiveSec=15min

[Install]
WantedBy=timers.target

Que hai aquí:

  • descrición do temporizador
  • Primeira hora de inicio, a partir do inicio do sistema
  • período de novos lanzamentos
  • Dependencia do servizo de temporizador. De feito, esta é a cadea que fai o temporizador

Script interactivo ao apagar e o seu destino de apagado

Noutro desenvolvemento, tiven que facer unha versión máis complexa de apagar a máquina, a través do meu propio obxectivo, para realizar moitas accións. Normalmente recoméndase crear un servizo oneshot coa opción RemainAfterExit, pero isto impide que cree un script interactivo.

Pero o feito é que os comandos lanzados pola opción ExecOnStop execútanse fóra do TTY! É doado comprobar: pega o comando tty e garda a súa saída.

Polo tanto, implementei o peche a través do meu obxectivo. Non pretendo ser 100% correcto, pero funciona!
Como se fixo (en termos xerais):
Creei un destino my_shutdown.target, que non dependía de ninguén:
meu_apagar.obxectivo

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

Ao ir a este destino (a través de systemctl isolate my_shutdwn.target), lanzou o servizo my_shutdown.service, cuxa tarefa é sinxela: executar o 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

  • Dentro deste script realizo as accións necesarias. Podes engadir moitos scripts ao destino para obter flexibilidade e comodidade:

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

Nota. Usando os ficheiros /tmp/reboot e /tmp/shutdown. Non pode chamar target con parámetros. Só é posible o servizo.

Pero uso target para ter flexibilidade no traballo e unha orde de accións garantida.

Con todo, o máis interesante veu despois. A máquina debe ser apagada/reiniciada. E hai 2 opcións:

  • Substitúe os comandos de reinicio, apagado e outros (aínda son ligazóns simbólicas a systemctl) polo teu script. Dentro do script, vai a my_shutdown.target. E os scripts dentro do destino chaman directamente a systemctl, por exemplo, systemctl reboot
  • Unha opción máis sinxela, pero non me gusta. En todas as interfaces, non chame a shutdown/reboot/other, senón que chame directamente ao systemctl de destino isolate my_shutdown.target

Eu escollín a primeira opción. En systemd, o reinicio (como poweroff) son ligazóns simbólicas a systemd.

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

Polo tanto, pode substituílos polos seus propios scripts:
reinicio

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

Fonte: www.habr.com

Engadir un comentario