Systemd, interaktywne skrypty i timery

Systemd, interaktywne skrypty i timery

Wprowadzenie

Podczas programowania dla systemu Linux pojawia się zadanie tworzenia interaktywnych skryptów, które są wykonywane po włączeniu lub wyłączeniu systemu. W systemie V było to łatwe, ale w systemie systemd wprowadza poprawki. Ale może mieć własne timery.

Dlaczego potrzebujemy celów?

Często pisze się, że cel służy jako analogia poziomu pracy w systemie V -init. Zasadniczo się nie zgadzam. Jest ich więcej i można podzielić pakiety na grupy i np. jednym poleceniem uruchomić grupę usług i wykonać dodatkowe akcje. Co więcej, nie mają hierarchii, a jedynie zależności.

Przykład elementu docelowego po włączeniu (przegląd funkcji) z uruchomionym skryptem interaktywnym

Opis samego celu:

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

Ten cel zostanie uruchomiony po uruchomieniu multi-user.target i wywołaniu installer.service. Może jednak istnieć kilka takich usług.

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

I na koniec przykład wykonania skryptu:

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

Najważniejszą rzeczą jest wybranie final.target - celu, do którego system powinien dotrzeć przy uruchomieniu. Podczas procesu uruchamiania systemd przejrzy zależności i uruchomi wszystko, czego potrzebuje.
Istnieją różne sposoby wyboru pliku final.target, ja użyłem w tym celu opcji modułu ładującego.

Ostateczne uruchomienie wygląda następująco:

  1. Стартует загрузчик
  2. Program ładujący rozpoczyna uruchamianie oprogramowania sprzętowego, przekazując parametr final.target
  3. Systemd zaczyna uruchamiać system. Kolejno przechodzi do installer.target lub work.target z basic.target poprzez ich zależności (na przykład multi-user.target). Te ostatnie powodują, że system działa w pożądanym trybie

Przygotowanie oprogramowania do uruchomienia

Podczas tworzenia oprogramowania sprzętowego zawsze pojawia się zadanie przywrócenia stanu systemu podczas uruchamiania i zapisania go podczas zamykania. Stan oznacza pliki konfiguracyjne, zrzuty bazy danych, ustawienia interfejsu itp.

Systemd uruchamia procesy w tym samym miejscu docelowym równolegle. Istnieją zależności, które pozwalają określić kolejność uruchamiania skryptów.

Как это работает у меня в проекте ( https://habr.com/ru/post/477008/ https://github.com/skif-web/monitor)

  1. System zostanie uruchomiony
  2. Uruchomiona zostanie usługa settings_restore.service, która sprawdza obecność pliku settings.txt w sekcji danych. Jeśli go tam nie ma, to w jego miejsce umieszczany jest plik referencyjny, a następnie przywracane są ustawienia systemu:
    • hasło administratora
    • nazwa hosta,
    • strefa czasowa
    • widownia
    • Określa, czy używane są wszystkie multimedia. Domyślnie rozmiar obrazu jest mały – aby ułatwić kopiowanie i nagrywanie na nośniki. Podczas uruchamiania sprawdza, czy jest jeszcze wolne miejsce. Jeśli tak, dysk jest ponownie podzielony na partycje.
    • Generowanie identyfikatora maszyny z adresu MAC. Jest to ważne, aby uzyskać ten sam adres przez DHCP
    • Ustawienia sieci
    • Ограничивается размер логов
    • Dysk zewnętrzny jest przygotowywany do pracy (jeśli odpowiednia opcja jest włączona, a dysk jest nowy)
  3. Uruchom postgresq
  4. Usługa przywracania zostanie uruchomiona. Konieczne jest przygotowanie samego Zabbix i jego bazy danych:
    • Sprawdza, czy istnieje już baza danych Zabbix. Jeśli nie, jest tworzony ze zrzutów inicjujących (dołączonych do Zabbix)
    • tworzona jest lista stref czasowych (potrzebna do wyświetlenia ich w interfejsie WWW)
    • Znaleziono aktualne IP, wyświetla się problem (zaproszenie do zalogowania się do konsoli)
  5. Zaproszenie ulega zmianie – pojawia się komunikat Gotowy do pracy
  6. Oprogramowanie sprzętowe jest gotowe do użycia

Pliki usług są ważne, to one ustalają kolejność ich uruchamiania

[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

Jak widać, zainstalowałem zależności, aby mój skrypt najpierw działał, a dopiero potem sieć się uruchomiła i uruchomił się system DBMS.

И второй сервис(подготовка 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

Здесь немного сложнее.Запуск так же в multi-user.target, но ПОСЛЕ запуска СУБД postgresql и моего setting_restore. Но ПЕРЕД запуском служб zabbix.

Usługa timera dla logrotate

Systemd może zastąpić CRON. Poważnie. Co więcej, dokładność nie jest co do minuty, ale co do sekundy (a jeśli jest taka potrzeba) Lub możesz stworzyć monotonny timer, wywoływany przekroczeniem limitu czasu zdarzenia.
Był to monotonny timer odliczający czas od uruchomienia maszyny, którą stworzyłem.
Będzie to wymagało 2 plików
logrotateTimer.service - faktyczny opis usługi:

[Unit]
Description=run logrotate

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

To proste - opis polecenia uruchomienia.
Drugi plik logrotateTimer.timer to miejsce, w którym działają timery:

[Unit]
Description=Run logrotate

[Timer]
OnBootSec=15min
OnUnitActiveSec=15min

[Install]
WantedBy=timers.target

Co jest tutaj:

  • opis timera
  • Czas pierwszego uruchomienia, począwszy od rozruchu systemu
  • okres kolejnych startów
  • Zależność od usługi timera.W rzeczywistości jest to ciąg znaków tworzący timer

Interaktywny skrypt podczas zamykania i cel zamknięcia

W innym rozwoju musiałem wykonać bardziej złożoną wersję wyłączenia maszyny - poprzez własny cel, aby wykonać wiele czynności. Zwykle zaleca się utworzenie usługi oneshot z opcją RemainAfterExit, jednak uniemożliwia to utworzenie interaktywnego skryptu.

Ale faktem jest, że polecenia uruchamiane przez opcję ExecOnStop są wykonywane poza TTY! Łatwo to sprawdzić - wklej polecenie tty i zapisz jego wynik.

Поэтому я реализовал выключение через свой таргет. На 100% правильность не претендую, но это работает!
Jak to zostało zrobione (ogólnie):
Stworzyłem cel my_shutdown.target, który nie był od nikogo zależny:
moje_zamknięcie.target

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

Przechodząc do tego celu (poprzez systemctl isolate my_shutdwn.target), uruchamia usługę my_shutdown.service, której zadanie jest proste - wykonać skrypt 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

  • Wewnątrz tego skryptu wykonuję niezbędne działania. Dla elastyczności i wygody możesz dodać wiele skryptów do celu:

moje_zamknięcie.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

Notatka. Korzystanie z plików /tmp/reboot i /tmp/shutdown. Nie można wywołać celu z parametrami. Możliwy jest jedynie serwis.

Ale używam celu, żeby mieć elastyczność w pracy i gwarantowaną kolejność działań.

Najciekawsze jednak nastąpiło później. Należy wyłączyć/uruchomić maszynę ponownie. I są 2 opcje:

  • Zastąp polecenia restart, shutdown i inne (są to nadal dowiązania symboliczne do systemctl) swoim skryptem. Wewnątrz skryptu przejdź do my_shutdown.target. Następnie skrypty wewnątrz obiektu docelowego bezpośrednio wywołują systemctl, na przykład systemctl restart
  • Prostsza opcja, ale nie podoba mi się. We wszystkich interfejsach nie wywołuj zamknięcia/restartu/innego, ale bezpośrednio wywołaj docelowy systemctl isolate my_shutdown.target

Я выбрал первый вариант. В systemd reboot(как и poweroff) являются симлинками на systemd.

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

Dlatego możesz zastąpić je własnymi skryptami:
restart

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

Źródło: www.habr.com

Dodaj komentarz