Скрипты init.d: устройство, создание и настройка автозапуска в Linux

Команда 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 к работе

Выполнено: 0 / 5
⚠️ Внимание: команда 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 допустимо, но при следующем запуске системных утилит управления порядок может быть пересоздан.

📊 Какая система инициализации используется на вашем сервере?
SysVinit (классический init.d)
systemd
OpenRC
Не знаю / смешанная среда

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-каталогах.