
1. Introdução
Ao desenvolver para linux, há tarefas de criação de scripts interativos que são executados quando o sistema é ligado ou desligado. No sistema V, isso foi feito facilmente, mas com o systemd ele faz ajustes. Mas pode fazer seus temporizadores.
Por que precisamos de um alvo
Costuma-se escrever que o destino serve como um análogo do nível de execução no sistema V-init. Eu discordo fundamentalmente. Existem mais deles e você pode separar pacotes em grupos e, por exemplo, executar um grupo de serviços com uma equipe e executar ações adicionais. Além disso, eles não têm hierarquia, apenas dependências.
Exemplo de destino na inicialização (visão geral do recurso) com o lançamento de um script interativo
Descrição do próprio destino:
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
Esse destino será iniciado quando o multi-user.target for iniciado e chamar installer.service. Além disso, pode haver vários desses serviços.
cat installer.service [Unit]
E, finalmente, um exemplo de um script executável:
O mais importante é escolher final.target - target, ao qual o sistema deve chegar na inicialização. No processo de inicialização, o systemd examinará as dependências e executará tudo o que você precisa.
Existem várias maneiras de selecionar final.target, usei a opção bootloader para isso.
O lançamento final é assim:
- O carregador de inicialização inicia
- O carregador de inicialização inicia o firmware passando o parâmetro final.target
- Systemd inicia a inicialização do sistema. Ele vai seqüencialmente para installer.target ou work.target do basic.target através de suas dependências (por exemplo, multiusuário.target). Este último e levar o sistema a trabalhar no modo desejado
Preparando o firmware para inicialização
Ao criar firmware, sempre há a tarefa de restaurar o estado do sistema na inicialização e salvá-lo quando desligado. Estado significa arquivos de configuração, despejos de banco de dados, configurações de interface e assim por diante.
O Systemd inicia o processo em um destino em paralelo. Existem dependências que permitem determinar a sequência de execução do script.
Como funciona no meu projeto ( https://habr.com/en/post/477008/ https://github.com/skif-web/monitor )
- O sistema inicia
- O serviço settings_restore.service é iniciado e verifica o arquivo settings.txt na seção de dados. Se não estiver lá, o arquivo de referência é colocado em seu lugar.Em seguida, as configurações do sistema são restauradas:
- senha de administrador
- nome do host
- fuso horário
- localidade
- Determina se toda a mídia está em uso. Por padrão, o tamanho da imagem é pequeno - para a conveniência de copiar e gravar na mídia. Na inicialização, é verificado se ainda há espaço não utilizado. Se houver - o disco será reparticionado.
- Gere o ID da máquina a partir do endereço MAC. Isso é importante para obter o mesmo endereço via DHCP.
- Configurações de rede
- O tamanho do log é limitado
- A unidade externa está preparada para o trabalho (se a opção correspondente estiver ativada e a unidade for nova)
- Execute o postgresq
- o serviço de restauração é iniciado. É necessário preparar o próprio zabbix e seu banco de dados:
- Verifica se já existe um banco de dados zabbix. Caso contrário, ele é criado a partir da inicialização dos dumps (eles são fornecidos com o zabbix)
- uma lista de fusos horários é criada (necessária para exibi-los na interface da web)
- O IP atual é encontrado, é exibido em questão (convite para entrar no console)
- O convite muda - a frase Pronto para trabalhar aparece
- O firmware está pronto para uso.
Os arquivos de serviço são importantes, são eles que definem a sequência para seu lançamento
[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 você pode ver, eu fiz as dependências para executar primeiro o meu script, e só então a rede aumentaria e o DBMS seria iniciado.
E o segundo serviço (preparando o zabbix)
Isso é um pouco mais complicado.O lançamento também está no multi-user.target, mas DEPOIS de executar o DBMS do postgresql e meu setting_restore. Mas ANTES de executar os serviços zabbix.
Serviço com um timer para logrotate
O Systemd pode substituir o CRON. Sério. Além disso, a precisão não é de um minuto, mas de um segundo (e se for necessário) E você pode criar um cronômetro monótono, chamado pelo tempo limite do evento.
Foi o cronômetro monótono que conta o tempo desde o início da máquina que eu criei.
Isso exigirá 2 arquivos
logrotateTimer.service - a descrição real do serviço:
[Unit] Description=run logrotate [Service] ExecStart=logrotate /etc/logrotate.conf TimeoutSec=300
É simples - uma descrição do comando de inicialização.
O segundo arquivo logrotateTimer.timer é o que define os cronômetros para funcionarem:
[Unit] Description=Run logrotate [Timer] OnBootSec=15min OnUnitActiveSec=15min [Install] WantedBy=timers.target
O que há lá:
- descrição do temporizador
- Primeira vez iniciando na inicialização do sistema
- período de novos lançamentos
- Dependência de Serviço do Temporizador: Na verdade, esta é a linha e faz o temporizador
Script interativo no encerramento e destino de encerramento personalizado
Em outro desenvolvimento, tive que criar uma versão mais complexa do desligamento da máquina - através do meu próprio destino, a fim de executar muitas ações. Geralmente, é recomendável criar o serviço oneshot com a opção RemainAfterExit, mas isso impede a criação de um script interativo.
Mas o fato é que os comandos lançados pela opção ExecOnStop são executados fora do TTY! A verificação é simples - insira o comando tty e salve sua saída.
Portanto, implementei o desligamento através do meu destino. Não pretendo estar 100% correto, mas funciona!
Como foi feito (em termos gerais):
Criou o destino my_shutdown.target, que não dependia de ninguém:
my_shutdown.target
[Unit] Description=my shutdown AllowIsolate=yes Wants=my_shutdown.service
Ao mudar para esse destino (via systemctl isolar my_shutdwn.target), ele lançou o serviço my_shutdown.service, cuja tarefa é simples - 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, eu executo as ações necessárias. Você pode adicionar muitos scripts ao destino, para flexibilidade e conveniência:
my_shutdown.sh
Nota Usando os arquivos / tmp / reboot e / tmp / shutdown. Você não pode chamar target com parâmetros. Você só pode atender.
Mas eu uso o target para ter flexibilidade no trabalho e uma sequência garantida de ações.
No entanto, a coisa mais interessante foi depois. A máquina deve ser desligada / reiniciada. E existem 2 opções:
- Substitua reboot, shutdown e outros comandos (eles ainda são links simbólicos no systemctl) por seu próprio script.No interior do script, vá para my_shutdown.target. E os scripts dentro do destino chamam diretamente systemctl, por exemplo, reinicialização do systemctl
- Um mais simples, mas eu não gosto da opção. Em todas as interfaces, não chame shutdown / reboot / others, mas chame diretamente o systemctl de destino isolate my_shutdown.target
Eu escolhi a primeira opção. No systemd, reinicialização (como poweroff) são links simbólicos no systemd.
ls -l /sbin/poweroff lrwxrwxrwx 1 root root 14 30 18:23 /sbin/poweroff -> /bin/systemctl
Portanto, eles podem ser substituídos por seus próprios scripts:
reiniciar