Настройка скрипта автозапуска через SystemD-юнит (серверный подход)
Предисловие
Какие существуют подходы к автозагрузке в Linux?
Есть 3 самых популярных подхода решения задачи "я хочу, чтобы скрипт запускался после перезагрузки сервера":
- Через файл rc.local - устаревший подход, да и правка системных конфигов, даже таких, всегда чревата;
- @reboot в cron - это явный костыль, хотя многие со мной не согласятся;
- Скрипты в духе "sleep 60 && ..." - это зло. Они не дают контроля и работают буквально "на шару". Слишком много ответственности для простого bash-скрипта.
ИМХО автора
Для серверов единственно правильный путь решения этой проблемы сейчас - systemd unit
Почему это лучше, чем альтернативные подходы?
Подход автора лучше, потому что он:
- Дает значительно больше контроля над сервисом: его остановка, внеплановый запуск или сбор информации о его работе становятся значительно проще для администратора - для этого будут использоваться стандартные утилиты, к которым все привыкли (systemctl и journalctl);
- Разделяет ответственность: логика скрипта находится отдельно от логики, которая ответственна за его запуск;
- Позволяет менять логику скрипта отдельно, не меняя любой внешней логики (атомарность в подходе).
Пример настройки скрипта для перезапуска сервисов
Пишем сервисный файл (SystemD-юнит)
Я редактирую файл юнита. Допустим, он будет называться post-reboot-tasks, тогда:
nano /etc/systemd/system/post-reboot-tasks.service
В самом юните я делаю следующее:
[Unit]
Description=Post reboot tasks
After=network-online.target
Wants=network-online.target
[Service]
Type=oneshot
ExecStart=/opt/scripts/post-reboot.sh
RemainAfterExit=yes
[Install]
WantedBy=multi-user.target
Почему это работает?
Тут у читателя может возникнуть вопрос: А с чего ты взял, что оно сработает только при перезагрузке? Где ты здесь указываешь, что он будет выполнен именно и только после ребута системы?
Отвечаю:
В SystemD это делается не отдельной строкой в духе "AfterReboot=true", а самим фактом, что юнит включён в target, который достигается при загрузке. В этом примере вот эта строчка и говорит, "запускать при старте системы":
WantedBy=multi-user.target
Когда вы делаете:
systemctl enable post-reboot-tasks
SystemD создаёт symlink юнита в:
/etc/systemd/system/multi-user.target.wants/post-reboot-tasks.service
А multi-user.target - это стандартная "нормальная загрузка сервера без GUI" (условно, runlevel=3). Каждый раз, когда система доходит до этого Target'а (то есть после ребута/бут-цикла), SystemD подхватит юнит и запустит его.
Здесь "после ребута" = "после каждой загрузки", т.е. юнит будет выполняться каждый boot.
Подготовка скрипта
Здесь все просто. Я:
- Создаю директорию, которую ранее указывал в сервисном файле;
- Создаю и заполняю скрипт внутри нее (см. пример ниже);
- Выдаю необходимые права для запуска скрипта;
- Применяю изменения для SystemD, чтобы юнит появился в списке и попал в пул активных.
Команда, которой я это сделал:
mkdir -p /opt/scripts/ && \
nano /opt/scripts/post-reboot.sh && \
chmod +x /opt/scripts/post-reboot.sh && \
systemctl daemon-reexec && systemctl daemon-reload && \
systemctl enable --now post-reboot-tasks
Теперь, когда создан скрипт и юнит-файл попал в реестр SystemD, он будет запускаться при каждой перезагрузке
Пример наполнения скрипта
Перейдем к примерам скриптов. В моем случае, я бы хотел автоматический перезапуска ряда сервисов после того, как система загрузилась и получила IP-адрес. Именно это я и делаю здесь:
В случае, если вы захотите изменить его логику, вы можете просто отредактировать скрипт. Перезапускать юнит или указывать SystemD о его изменении не нужно.
Важное уточнение, которое я вынесу отдельно
Скрипт будет запускаться при загрузке системы только в том случае, если его статус - enabled. В противном случае скрипт не попадет в автозагрузку. Проверить это можно командой:
systemctl status post-reboot-tasks


