Systemd, інтэрактыўныя скрыпты і таймеры

Systemd, інтэрактыўныя скрыпты і таймеры

Увядзенне

Пры распрацоўцы пад linux узнікаюць задачы стварэння інтэрактыўных скрыптоў, выкананых пры ўключэнні ці завяршэнні працы сістэмы. У system V гэта рабілася лёгка, але з systemd уносіць карэктывы. Затое яно ўмее свае таймеры.

Навошта патрэбны target

Часта пішуць, што target служаць аналагам runlevel у system V -init. У корані не згодзен. Іх больш і можна падзяляць пакеты па групах і, напрыклад, запускаць адной камандай групу сэрвісаў, выконваць дадатковыя дзеянні. Акрамя таго, у іх няма іерархіі, толькі залежнасці.

Прыклад target пры ўключэнні(агляд магчымасці) з запускам інтэрактыўнага скрыпту

Апісанне самога target:

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

Дадзены target запусціцца, калі будзе запушчаны multi-user.target і выкліча installer.service. Пры гэтым такіх сервісаў можа быць некалькі.

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

І нарэшце, прыклад выкананага скрыпту:

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

Самае галоўнае - абраць final.target - target, да якога сістэма павінна прыйсці пры запуску. У працэсе запуску systemd пройдзе па залежнасцях і запусціць усё патрэбнае.
Выбраць final.target можна рознымі спосабамі, я выкарыстоўваў для гэтага опцыю загрузніка.

Выніковы запуск выглядае так:

  1. Стартуе загрузнік
  2. Загрузнік пачынае запуск прашыўкі, перадаючы параметр final.target
  3. Systemd пачынае запуск сістэмы. Паслядоўна ідзе да installer.target ці work.target ад basic.target праз іх залежнасці (напрыклад, multi-user.target). Апошнія і прыводзяць сістэму да працы ў патрэбным рэжыме

Падрыхтоўка прашыўкі да запуску

Пры стварэнні прашывак заўсёды ўзнікае задача ўзнаўлення стану сістэмы пры старце і яго захаванні пры выключэнні. Пад станам маюцца на ўвазе канфігурацыйныя файлы, дампы базы дадзеных, налады інтэрфейсаў і тд.

Systemd запускае працэсу ў адным таргеце раўналежна. Ёсць залежнасці, якія дазваляюць вызначыць паслядоўнасць запуску скрыптоў.

Як гэта працуе ў мяне ў праекце ( https://habr.com/ru/post/477008/ https://github.com/skif-web/monitor)

  1. Сістэма стартуе
  2. Запускаецца сэрвіс settings_restore.service.Ён правярае наяўнасць файла settings.txt у раздзеле з дадзенымі. Калі яго няма, то на яго месца кладзецца эталонны файл. Далей адбываецца аднаўленне налад сістэмы:
    • пароля адміністратара
    • hostname,
    • часавага пояс
    • лакаль
    • Вызначэнне, ці ўвесь носьбіт выкарыстоўваецца. Па змаўчанні памер выявы невялікі - для выгоды капіявання і запісы на носьбіт. Пры старце правяраецца - ці ёсць яшчэ невыкарыстоўванае месца. Калі ёсць - дыск пераразбіваецца.
    • Генерацыя machine-id з MAC-адрасы. Гэта важна для атрымання аднаго і таго ж адраса па DHCP
    • налады сеткі
    • Абмяжоўваецца памер логаў
    • Падрыхтоўваецца да працы вонкавы дыск(калі ўключаная адпаведная опцыя і дыск новы)
  3. Запускацца postgresq
  4. запускаецца сэрвіс restore. Ён патрэбен для падрыхтоўкі самога zabbix і яго базы дадзеных:
    • Правяраецца, ці ёсць ужо база даных zabbix. Калі няма - ствараецца з ініцыялізуюць дампаў (ідуць у пастаўцы zabbix)
    • ствараецца спіс гадзінных паясоў (трэба для іх адлюстравання ў web-інтэрфейсе)
    • Знаходзіцца бягучы IP, ён выводзіцца ў issue (запрашэнне для ўваходу ў кансолі)
  5. Змяняецца запрашэнне - з'яўляецца фраза Ready to work
  6. Прашыўка гатова да працы

Важныя файлы сэрвісаў, менавіта яны выстаўляюць паслядоўнасць іх запуску.

[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

Як бачна, я паставіў залежнасці, што б спачатку адпрацаваў мой скрыпт, а толькі потым паднімалася сетка і стартавала СКБД.

І другі сэрвіс(падрыхтоўка 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.

Сэрвіс з таймерам для logrotate

Systemd можа замяніць CRON. Сур'ёзна. Прычым дакладнасць не да хвіліны, а да секунды (а раптам спатрэбіцца). А можна стварыць манатонны таймер, які выклікаецца па таймаўце ад падзеі.
Менавіта манатонны таймер, які лічыць час ад запуску машыны, я і стварыў.
Для гэтага спатрэбіцца 2 файлы
logrotateTimer.service – уласна апісанне сэрвісу:

[Unit]
Description=run logrotate

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

Усё проста - апісанне каманда запуску.
Другі файл logrotateTimer.timer - вось ён і задае працу таймераў:

[Unit]
Description=Run logrotate

[Timer]
OnBootSec=15min
OnUnitActiveSec=15min

[Install]
WantedBy=timers.target

Што тут ёсць:

  • апісанне таймера
  • Час першага запуску, пачынаючы ад загрузкі сістэм
  • перыяд далейшых запускаў
  • Залежнасць ад службы таймераў. Фактычна, гэта радок і робіць таймер

Інтэрактыўны скрыпт пры выключэнні і свой таргет выключэння

У іншай распрацоўцы мне прыйшлося рабіць больш складаны варыянт выключэння машыны праз уласны таргет, каб выканаць мноства дзеянняў. Звычайна рэкамендуецца стварыць сэрвіс oneshot з опцыяй RemainAfterExit, але гэта не дае стварыць інтэрактыўны скрыпт.

А справа ў тым, што каманды, якія запускаюцца опцыяй ExecOnStop выконваюцца па-за TTY! Праверыць проста - устаўце каманду tty і захавайце яе выснову.

Таму я рэалізаваў выключэнне праз свой таргет. На 100% правільнасць не прэтэндую, але гэта працуе!
Як гэта рабілася(у агульных рысах):
Стварыў таргет my_shutdown.target, які ні ад кога не залежаў:
my_shutdown.target

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

Пры пераходзе ў гэты таргет (праз systemctl isolate my_shutdwn.target), ён запускаў сэрвіс my_shutdown.service, задача якога простая – выканаць скрыпт 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

  • Унутры гэтага скрыпту я выконваю патрэбныя дзеянні. Можна ў таргет дадаць шмат скрыптоў, для гнуткасці і выгоды:

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/
    $command

Заўвага. Выкарыстанне файлаў /tmp/reboot і /tmp/shutdown. Вы не можаце выклікаць target з параметрамі. Можна толькі сервіс.

Але я выкарыстоўваю target, каб мець гнуткасць у працы і гарантаваны парадак выканання дзеянняў.

Аднак самае цікавае было потым. Машыну ж трэба выключыць/перазагрузіць. І тут ёсць 2 варыянты:

  • Замяніць каманды reboot,shutdown і іншыя (яны ўсё роўна з'яўляюцца сімлінкамі на systemctl) на свой скрыпт. Усярэдзіне скрыпту - пераход у my_shutdown.target. А скрыпты ўсярэдзіне таргетаў потым выклікаюць напроста systemctl, напрыклад, systemctl reboot
  • Прасцейшы, але мне не які падабаецца варыянт. Ва ўсіх інтэрфейсах выклікаць не shutdown/reboot/іншыя, а наўпрост выклікаць таргет 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

Таму іх можна замяніць на свае скрыпты:
перазагружаць

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

Крыніца: habr.com

Дадаць каментар