Команда service myapp start возвращает ошибку «unrecognized service», хотя файл скрипта лежит в /etc/init.d — чаще всего причина в отсутствии прав на исполнение или в неверной шапке LSB-заголовка, без которой система не регистрирует сервис. Каталог /etc/init.d — это классическое хранилище стартовых скриптов в дистрибутивах Linux с системой инициализации SysVinit, а также в ряде современных систем, где поддержка совместимости сохранена.
Разберёмся, как устроены эти скрипты, как написать собственный, привязать его к уровням запуска и диагностировать типичные сбои. Материал ориентирован на администраторов серверов и пользователей, которые обслуживают старые системы или встраиваемые устройства, где systemd отсутствует.
Что такое init.d и как это работает
Init — первый процесс, который запускает ядро Linux после загрузки (PID 1). Он отвечает за старт всех остальных служб. В классической схеме SysVinit конфигурация читается из файла /etc/inittab, а сами сценарии запуска сервисов лежат в каталоге /etc/init.d/.
Каждый скрипт в этом каталоге — обычный shell-файл, который принимает стандартные аргументы: start, stop, restart, status. Система вызывает их автоматически при смене уровня запуска (runlevel) или вручную через команды service имя start либо прямой вызов /etc/init.d/имя start.
Автозапуск при загрузке обеспечивается символическими ссылками в каталогах /etc/rc0.d … /etc/rc6.d. Имена ссылок начинаются с буквы S (start) или K (kill) и двухзначного номера приоритета — чем меньше число, тем раньше выполняется скрипт.
Структура каталога и уровни запуска
Уровни запуска определяют, какие службы должны работать в текущем режиме системы. Точное назначение уровней может отличаться между дистрибутивами, поэтому перед настройкой сверьтесь с документацией вашей системы.
| Runlevel | Типичное назначение | Каталог ссылок |
|---|---|---|
| 0 | Остановка системы (halt) | /etc/rc0.d |
| 1 | Однопользовательский режим | /etc/rc1.d |
| 2–5 | Многопользовательские режимы (зависят от дистрибутива) | /etc/rc2.d – /etc/rc5.d |
| 6 | Перезагрузка | /etc/rc6.d |
Текущий уровень можно посмотреть командой runlevel или who -r. Сменить уровень на лету позволяет команда init N или telinit N, где N — номер уровня. Делать это на рабочем сервере стоит с пониманием последствий: переход в уровень 1 остановит сетевые службы.
Как создать собственный скрипт init.d
Минимальный рабочий скрипт состоит из shebang, LSB-заголовка и конструкции case, обрабатывающей аргументы. Заголовок важен: утилиты вроде update-rc.d и insserv читают из него зависимости и порядок запуска.
#!/bin/sh
BEGIN INIT INFO
Provides: myapp
Required-Start: $network $local_fs
Required-Stop: $network $local_fs
Default-Start: 2 3 4 5
Default-Stop: 0 1 6
Short-Description: My application daemon
END INIT INFO
case "$1" in
start)
echo "Starting myapp"
/usr/local/bin/myapp --daemon
;;
stop)
echo "Stopping myapp"
killall myapp
;;
restart)
$0 stop
$0 start
;;
status)
pgrep myapp >/dev/null && echo "running" || echo "stopped"
;;
*)
echo "Usage: $0 {start|stop|restart|status}"
exit 1
;;
esac
exit 0
Порядок действий после создания файла:
- 📄 Сохраните скрипт в
/etc/init.d/myappот имени root - 🔑 Назначьте права на исполнение:
chmod +x /etc/init.d/myapp - 🔗 Зарегистрируйте автозапуск:
update-rc.d myapp defaults(Debian/Ubuntu) илиchkconfig --add myapp(RHEL/CentOS) - ✅ Проверьте работу:
service myapp start, затемservice myapp status
☑️ Готовность скрипта init.d к работе
⚠️ Внимание: командаkillallзавершает все процессы с указанным именем в системе. Для серьёзных сервисов надёжнее сохранять PID в файл (/var/run/myapp.pid) и завершать процесс по нему — иначе есть риск остановить чужой экземпляр программы.
Управление автозапуском сервисов
В системах семейства Debian/Ubuntu для управления ссылками автозапуска используется утилита update-rc.d. Команда update-rc.d myapp defaults создаёт ссылки во всех стандартных уровнях, а update-rc.d -f myapp remove удаляет их. В дистрибутивах линейки Red Hat аналогичную роль выполнял chkconfig: chkconfig myapp on и chkconfig myapp off.
Если нужно лишь временно отключить службу, не удаляя ссылки, достаточно переименовать соответствующие S-ссылки или воспользоваться опциями отключения конкретной утилиты вашего дистрибутива. Прямое ручное редактирование ссылок в rcN.d допустимо, но при следующем запуске системных утилит управления порядок может быть пересоздан.
init.d и systemd: как они сосуществуют
В современных дистрибутивах основной системой инициализации стал systemd, однако поддержка скриптов init.d в нём сохранена через генератор совместимости. Если unit-файл для сервиса отсутствует, systemd автоматически оборачивает скрипт из /etc/init.d во временный unit, ориентируясь на LSB-заголовок.
Из-за этого команда systemctl status myapp может показывать сервис как LSB-сервис. Важно понимать: при наличии одноимённого unit-файла приоритет отдаётся ему, а скрипт в init.d игнорируется. Перед правками проверьте, чем именно управляется служба: systemctl cat имя.
Почему скрипт работает из init.d, но systemctl его «не видит»
Скорее всего, systemd-кэш не обновлён после добавления файла. Выполните systemctl daemon-reload, чтобы генератор перечитал /etc/init.d. Также проверьте, что LSB-заголовок содержит корректное поле Provides — без него сервис может не зарегистрироваться.
Типичные ошибки и их диагностика
Большинство проблем с init.d-скриптами сводится к нескольким повторяющимся причинам. Начинать диагностику стоит с самого простого — прав и синтаксиса.
- 🚫 «Permission denied» — отсутствует бит исполнения; исправляется через
chmod +x - 🧾 Сервис не появляется в автозапуске — пустой или битый LSB-заголовок; проверьте, что блок
### BEGIN INIT INFOоформлен без лишних символов - 💥 Скрипт стартует, но процесс сразу умирает — программа не уходит в фон; добавьте демонизацию или запуск через
start-stop-daemon - 🔤 «bad interpreter» — в shebang указан несуществующий интерпретатор или файл сохранён с Windows-переносами строк (CRLF)
Полезный приём отладки — запустить скрипт с трассировкой: sh -x /etc/init.d/myapp start. Вывод покажет каждую выполняемую строку и место сбоя. Также проверяйте коды возврата: скрипт обязан возвращать 0 при успехе и ненулевой код при ошибке — иначе система инициализации не сможет корректно отследить состояние службы.
⚠️ Внимание: не редактируйте скрипты системных служб (сеть, журналирование, базы данных) без резервной копии. Ошибка в скрипте, от которого зависят другие сервисы, может привести к тому, что система не завершит загрузку. Сохраняйте копию оригинала перед любыми правками.
Лучшие практики написания скриптов
Аккуратный init-скрипт экономит часы отладки. Используйте готовые функции-обёртки из /lib/lsb/init-functions (в Debian-подобных системах) — они дают стандартные сообщения об ошибках и корректные коды возврата. Для запуска и остановки демонов предпочтителен start-stop-daemon: он умеет работать с PID-файлами, менять пользователя и проверять, не запущен ли процесс уже.
Храните исполняемый файл приложения отдельно от скрипта, пишите логи в /var/log/, а PID — в /var/run/. Если скрипт должен работать от непривилегированного пользователя, явно указывайте смену пользователя при запуске, а не полагайтесь на контекст вызова.
Часто задаваемые вопросы
Чем init.d отличается от systemd unit-файлов?
Init.d использует shell-скрипты с последовательным запуском, а systemd — декларативные unit-файлы с параллельным стартом, отслеживанием процессов и автоматическим перезапуском. Systemd умеет всё то же, но управление зависимостями и журналирование в нём встроены в саму систему.
Как узнать, какие скрипты запускаются при загрузке?
Просмотрите содержимое каталогов ls /etc/rc$(runlevel | awk '{print $2}').d/ — ссылки, начинающиеся с S, стартуют на текущем уровне. Альтернатива — команда service --status-all, показывающая состояние всех зарегистрированных сервисов.
Почему скрипт работает вручную, но не стартует при загрузке?
Частые причины: скрипт не зарегистрирован через update-rc.d/chkconfig, неверно указаны уровни в Default-Start, либо сервису нужна сеть, а в Required-Start отсутствует $network. Проверьте также, не переопределяет ли systemd этот сервис своим unit-файлом.
Можно ли использовать init.d-скрипты в современном Ubuntu или Debian?
Да, systemd сохраняет обратную совместимость: скрипты с корректным LSB-заголовком автоматически преобразуются в unit-обёртки. Однако для новых сервисов предпочтительнее писать нативные unit-файлы — они дают больше возможностей контроля.
Как безопасно удалить свой скрипт из автозагрузки?
Сначала остановите сервис (service myapp stop), затем удалите ссылки: update-rc.d -f myapp remove или chkconfig --del myapp. Только после этого удаляйте сам файл из /etc/init.d/ — обратный порядок оставит «битые» ссылки в rc-каталогах.