Увядзенне
Пры распрацоўцы пад 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 можна рознымі спосабамі, я выкарыстоўваў для гэтага опцыю загрузніка.
Выніковы запуск выглядае так:
- Стартуе загрузнік
- Загрузнік пачынае запуск прашыўкі, перадаючы параметр final.target
- Systemd пачынае запуск сістэмы. Паслядоўна ідзе да installer.target ці work.target ад basic.target праз іх залежнасці (напрыклад, multi-user.target). Апошнія і прыводзяць сістэму да працы ў патрэбным рэжыме
Падрыхтоўка прашыўкі да запуску
Пры стварэнні прашывак заўсёды ўзнікае задача ўзнаўлення стану сістэмы пры старце і яго захаванні пры выключэнні. Пад станам маюцца на ўвазе канфігурацыйныя файлы, дампы базы дадзеных, налады інтэрфейсаў і тд.
Systemd запускае працэсу ў адным таргеце раўналежна. Ёсць залежнасці, якія дазваляюць вызначыць паслядоўнасць запуску скрыптоў.
Як гэта працуе ў мяне ў праекце (
- Сістэма стартуе
- Запускаецца сэрвіс settings_restore.service.Ён правярае наяўнасць файла settings.txt у раздзеле з дадзенымі. Калі яго няма, то на яго месца кладзецца эталонны файл. Далей адбываецца аднаўленне налад сістэмы:
- пароля адміністратара
- hostname,
- часавага пояс
- лакаль
- Вызначэнне, ці ўвесь носьбіт выкарыстоўваецца. Па змаўчанні памер выявы невялікі - для выгоды капіявання і запісы на носьбіт. Пры старце правяраецца - ці ёсць яшчэ невыкарыстоўванае месца. Калі ёсць - дыск пераразбіваецца.
- Генерацыя machine-id з MAC-адрасы. Гэта важна для атрымання аднаго і таго ж адраса па DHCP
- налады сеткі
- Абмяжоўваецца памер логаў
- Падрыхтоўваецца да працы вонкавы дыск(калі ўключаная адпаведная опцыя і дыск новы)
- Запускацца postgresq
- запускаецца сэрвіс restore. Ён патрэбен для падрыхтоўкі самога zabbix і яго базы дадзеных:
- Правяраецца, ці ёсць ужо база даных zabbix. Калі няма - ствараецца з ініцыялізуюць дампаў (ідуць у пастаўцы zabbix)
- ствараецца спіс гадзінных паясоў (трэба для іх адлюстравання ў web-інтэрфейсе)
- Знаходзіцца бягучы IP, ён выводзіцца ў issue (запрашэнне для ўваходу ў кансолі)
- Змяняецца запрашэнне - з'яўляецца фраза Ready to work
- Прашыўка гатова да працы
Важныя файлы сэрвісаў, менавіта яны выстаўляюць паслядоўнасць іх запуску.
[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