15%

Сэкономьте 15% на всех хостинговых услугах

Проверьте свои навыки и получите скидку на любой тарифный план

Используйте код:

Skills
Начать
10.11.2023

Как включить автозагрузку скриптов в Ubuntu: три готовых к использованию метода

Включение автозагрузки скриптов в Ubuntu означает настройку операционной системы для автоматического выполнения одного или нескольких shell-скриптов или служб при запуске системы без какого-либо ручного вмешательства. Это достигается с помощью трёх основных механизмов: устаревшего каталога /etc/init.d/ на основе SysVinit, совместимого шима /etc/rc.local и современного фреймворка сервисных юнитов systemd — последний является авторитетным, рекомендуемым подходом во всех выпусках Ubuntu начиная с 15.04.

Для системных администраторов, запускающих рабочие нагрузки в среде VPS Хостинга, автоматизация запуска — это не удобство, а требование надёжности. Неправильно настроенная или отсутствующая запись автозапуска означает, что критически важные демоны, агенты мониторинга, скрипты резервного копирования или пользовательские сетевые конфигурации незаметно не запускаются после перезагрузки, вызывая сбои в работе сервисов, которые сложно диагностировать постфактум.

Почему автоматизация запуска скриптов важна на серверах Ubuntu

Каждый производственный сервер Ubuntu со временем накапливает операционные скрипты: процедуры предварительного прогрева базы данных, триггеры ротации логов, инициализаторы VPN-туннелей, загрузчики правил брандмауэра и проверки работоспособности приложений. Без структурированного механизма автозагрузки эти скрипты полностью зависят от ручного выполнения — один пропущенный шаг после обновления ядра или экстренной перезагрузки может привести к каскадному простою.

Экосистема автоматизации запуска Ubuntu значительно эволюционировала:

  • SysVinit (до Ubuntu 15.04): Последовательный, медленный, основанный на скриптах. Каждый сервис блокировал следующий.
  • Upstart (Ubuntu 6.10–15.04): Событийно-ориентированный, более быстрый, но теперь устаревший.
  • systemd (Ubuntu 15.04+): Параллельная активация служб, графы зависимостей, активация сокетов, управление ресурсами на основе cgroup и структурированное ведение журналов через journald.

Понимание того, с каким уровнем вы работаете — и почему — предотвращает развёртывание работающего решения в тестовой среде, которое незаметно ломается в производственной.

Метод 1: Использование каталога /etc/init.d/ (SysVinit / LSB-скрипты)

Как это работает

Каталог /etc/init.d/ является традиционным местом хранения init-скриптов Linux Standard Base (LSB). Каждый скрипт в этом каталоге является shell-скриптом, который реагирует на стандартизированные команды: start, stop, restart, status и опционально reload. Утилита update-rc.d создаёт символические ссылки в каталогах уровней запуска /etc/rcN.d/, определяя, когда и в каком порядке скрипт выполняется во время последовательностей загрузки и завершения работы.

На современных системах Ubuntu, работающих под управлением systemd, эти скрипты по-прежнему поддерживаются через уровень совместимости под названием systemd-sysv-generator, который автоматически преобразует LSB init-скрипты в транзитные юниты systemd. Это означает, что ваши скрипты /etc/init.d/ по-прежнему будут выполняться, но они будут обёрнуты systemd, а не выполняться напрямую SysVinit.

Пошаговая реализация

Шаг 1: Создайте скрипт

Напишите скрипт и убедитесь, что он следует соглашению о заголовке LSB. Минимальный, безопасный для производства пример:

#!/bin/bash
### BEGIN INIT INFO
# Provides:          examplescript
# Required-Start:    $remote_fs $syslog $network
# Required-Stop:     $remote_fs $syslog
# Default-Start:     2 3 4 5
# Default-Stop:      0 1 6
# Short-Description: Example autoload script
# Description:       Runs a custom initialization task at boot
### END INIT INFO

case "$1" in
  start)
    echo "Starting examplescript..."
    /usr/local/bin/examplescript.sh &
    ;;
  stop)
    echo "Stopping examplescript..."
    pkill -f examplescript.sh
    ;;
  restart)
    $0 stop
    $0 start
    ;;
  status)
    pgrep -f examplescript.sh > /dev/null && echo "Running" || echo "Stopped"
    ;;
  *)
    echo "Usage: $0 {start|stop|restart|status}"
    exit 1
    ;;
esac
exit 0

Шаг 2: Поместите скрипт в /etc/init.d/

sudo cp examplescript /etc/init.d/examplescript

Шаг 3: Сделайте его исполняемым

sudo chmod +x /etc/init.d/examplescript

Шаг 4: Зарегистрируйте его в системе уровней запуска

sudo update-rc.d examplescript defaults

Аргумент defaults регистрирует скрипт для запуска на уровнях 2, 3, 4 и 5 и остановки на уровнях 0, 1 и 6 — стандартное поведение для большинства серверных демонов.

Шаг 5: Проверьте регистрацию

ls -la /etc/rc2.d/ | grep examplescript

Вы должны увидеть символическую ссылку S01examplescript, указывающую обратно на /etc/init.d/examplescript.

Критическая ошибка

Наиболее распространённой ошибкой при использовании этого метода является пропуск блока заголовка LSB. Без него update-rc.d не может определить порядок зависимостей, а systemd-sysv-generator может назначить неверный порядок выполнения относительно доступности сети или монтирования файловых систем. Всегда явно определяйте зависимости Required-Start.

Чтобы удалить скрипт из автозапуска без его удаления:

sudo update-rc.d examplescript disable

Для полного удаления:

sudo update-rc.d examplescript remove

Метод 2: Использование /etc/rc.local (шим совместимости)

Как это работает

/etc/rc.local — это устаревший механизм, который выполняет shell-скрипт один раз после запуска всех стандартных служб многопользовательского уровня. Это простейший возможный метод автозапуска — без управления службами, без объявлений зависимостей, без логики перезапуска. В Ubuntu 18.04 и более поздних версиях поддержка rc.local обеспечивается юнитом systemd rc-local.service, который отключён по умолчанию и должен быть явно включён.

Когда использовать

Используйте /etc/rc.local только для:

  • Однократных команд инициализации, которые не нужно управлять как службами
  • Быстрого прототипирования или тестирования перед формализацией юнита systemd
  • Простого экспорта переменных среды или настройки параметров ядра

Не используйте /etc/rc.local для долго работающих демонов. Поскольку он выполняется в блокирующем, последовательном режиме без контроля процессов, зависшая команда в rc.local задержит или предотвратит завершение последовательности загрузки.

Пошаговая реализация

Шаг 1: Проверьте, существует ли /etc/rc.local

ls -la /etc/rc.local

Если он не существует, создайте его:

sudo bash -c 'cat > /etc/rc.local << EOF
#!/bin/bash
exit 0
EOF'
sudo chmod +x /etc/rc.local

Шаг 2: Включите юнит systemd rc-local (Ubuntu 18.04+)

sudo systemctl enable rc-local
sudo systemctl start rc-local

Шаг 3: Добавьте вашу команду перед exit 0

sudo nano /etc/rc.local

Вставьте вашу команду выше строки exit 0:

#!/bin/bash
/usr/local/bin/examplescript.sh >> /var/log/examplescript.log 2>&1 &
exit 0

Символ & в конце необходим для любой долго выполняющейся команды — он переводит процесс в фоновый режим, чтобы rc.local не блокировался.

Шаг 4: Проверьте выполнение

sudo systemctl status rc-local

Критическая ошибка

В Ubuntu 20.04 и 22.04 rc-local.service имеет жёстко заданный таймаут в 30 секунд. Если выполнение вашего скрипта занимает более 30 секунд, systemd пометит службу как неудачную, и последующие команды в rc.local не будут выполнены. Явно перенаправляйте вывод и переводите долго выполняющиеся процессы в фоновый режим.

Метод 3: Использование сервисных юнитов systemd (рекомендуется)

Почему systemd является правильным подходом для производства

systemd — это не просто замена SysVinit, это полноценный менеджер системы и сессий, который обеспечивает разрешение зависимостей, параллельный запуск, активацию сокетов и D-Bus, порождение служб по требованию, контроль процессов с автоматическим перезапуском, изоляцию ресурсов на основе cgroup и агрегацию структурированных журналов через journald. Для любой рабочей нагрузки, выполняемой на Выделенном сервере или производственном VPS, юниты systemd являются единственным подходящим механизмом для управления автозагружаемыми скриптами.

Анатомия файла юнита systemd

Файл юнита .service разделён на три обязательных раздела:

  • [Unit]: Метаданные, человекочитаемое описание и объявления зависимостей (After=, Requires=, Wants=).
  • [Service]: Параметры выполнения — двоичный файл или скрипт для запуска, тип службы, политика перезапуска, переменные среды и параметры изоляции безопасности.
  • [Install]: Определяет, какая цель systemd активирует этот юнит (WantedBy=multi-user.target является стандартом для серверных демонов).

Пошаговая реализация

Шаг 1: Подготовьте скрипт

Убедитесь, что ваш скрипт является исполняемым и расположен по стабильному пути:

sudo cp examplescript.sh /usr/local/bin/examplescript.sh
sudo chmod +x /usr/local/bin/examplescript.sh

Шаг 2: Создайте файл юнита

sudo nano /etc/systemd/system/examplescript.service

Файл юнита производственного уровня с усилением безопасности:

[Unit]
Description=Example Autoload Script
Documentation=https://your-internal-wiki/examplescript
After=network-online.target
Wants=network-online.target

[Service]
Type=simple
ExecStart=/usr/local/bin/examplescript.sh
ExecReload=/bin/kill -HUP $MAINPID
Restart=on-failure
RestartSec=5s
StandardOutput=journal
StandardError=journal
SyslogIdentifier=examplescript
User=nobody
Group=nogroup
NoNewPrivileges=true
ProtectSystem=strict
PrivateTmp=true

[Install]
WantedBy=multi-user.target

Шаг 3: Перезагрузите демон systemd

После создания или изменения любого файла юнита необходимо перезагрузить индекс конфигурации демона:

sudo systemctl daemon-reload

Шаг 4: Включите службу для автозапуска

sudo systemctl enable examplescript.service

Это создаёт символическую ссылку в /etc/systemd/system/multi-user.target.wants/, указывающую на ваш файл юнита.

Шаг 5: Немедленно запустите службу

sudo systemctl start examplescript.service

Шаг 6: Проверьте статус и журналы

sudo systemctl status examplescript.service
sudo journalctl -u examplescript.service -f

Понимание типов служб systemd

Директива Type= в разделе [Service] является одним из наиболее неправильно понимаемых параметров. Выбор неверного типа приводит к тому, что systemd неправильно сообщает о готовности службы, что ведёт к сбоям зависимостей.

ТипПоведениеВариант использования
simpleПроцесс, запущенный ExecStart, является основным процессом. Systemd считает его готовым немедленно.Скрипты и простые демоны, которые не разветвляются
forkingПроцесс разветвляется и родительский процесс завершается. Systemd отслеживает дочерний процесс через PID-файл.Традиционные Unix-демоны (например, Apache с PidFile)
oneshotПроцесс выполняется до завершения и завершается. Systemd ожидает перед запуском зависимых юнитов.Однократные задачи инициализации, скрипты настройки
notifyПроцесс сигнализирует о готовности через sd_notify().Демоны с нативной интеграцией systemd
idleВыполнение откладывается до тех пор, пока не будут отправлены все активные задания.Фоновые задачи с низким приоритетом

Для скрипта, который выполняется один раз при загрузке и завершается, используйте Type=oneshot с RemainAfterExit=yes, чтобы юнит оставался в состоянии «активен» после завершения скрипта.

Дополнительно: Управление порядком зависимостей с помощью After= и Wants=

Распространённый производственный сбой происходит, когда скрипт, требующий сетевого подключения, запускается до полной инициализации сетевого стека. Правильная цепочка зависимостей для скриптов, зависящих от сети:

After=network-online.target
Wants=network-online.target

Это отличается от After=network.target, который только гарантирует, что сетевые интерфейсы были настроены, но не то, что они фактически работают и доступны. Зависимость network-online.target требует, чтобы systemd-networkd-wait-online.service или аналогичный подтвердил подключение.

Сравнение: все три метода в одном обзоре

Функция/etc/init.d//etc/rc.localЮнит systemd
Рекомендуется для производстваНетНетДа
Поддержка параллельного запускаНетНетДа
Контроль процессов / автоперезапускНетНетДа
Управление зависимостямиОграниченное (заголовки LSB)НетПолное
Структурированное ведение журналовНетНетДа (journald)
Изоляция безопасностиНетНетДа
СложностьНизкаяОчень низкаяСредняя
Поддерживается в Ubuntu 22.04+Через уровень совместимостиЧерез rc-local.serviceНативно
Подходит для долго работающих демоновЧастичноНетДа
Подходит для однократных задач инициализацииДаДаДа (oneshot)

Распространённые ошибки и способы их избежать

Запуск скриптов от имени root без необходимости. Директивы User= и Group= в файлах юнитов systemd позволяют снизить привилегии. Скрипту, которому нужно только записывать в /var/log/myapp/, не нужны права root — создайте выделенного системного пользователя и назначьте владельца каталога соответствующим образом.

Отсутствие перенаправления вывода в rc.local. Без перенаправления вывода любой вывод echo или сообщения об ошибках от команд rc.local попадают на системную консоль и теряются. Всегда добавляйте >> /var/log/yourscript.log 2>&1.

Непоследовательное использование абсолютных путей. Службы systemd работают в минимальной среде без пользовательского PATH. Всегда используйте абсолютные пути для каждого двоичного файла, указанного в ExecStart, включая интерпретаторы, такие как /usr/bin/python3 или /bin/bash.

Забывание daemon-reload после редактирования файлов юнитов. systemd кэширует содержимое файлов юнитов. Если вы редактируете файл .service и не запускаете sudo systemctl daemon-reload, systemd продолжит использовать старую конфигурацию.

Размещение файлов юнитов в /lib/systemd/system/ для пользовательских скриптов. Каталог /lib/systemd/system/ управляется менеджером пакетов. Пользовательские файлы юнитов принадлежат /etc/systemd/system/, который имеет приоритет и сохраняется при обновлениях пакетов.

Практическая матрица решений: какой метод использовать

Используйте эту схему для выбора подходящего метода для вашего конкретного сценария:

  • Долго работающий демон, который должен перезапускаться при сбое — юнит systemd с Restart=on-failure
  • Однократный скрипт настройки, который должен завершиться до запуска других служб — юнит systemd с Type=oneshot и зависимостями Before=
  • Скрипт, требующий полного подключения к сети — юнит systemd с After=network-online.target
  • Быстрая однострочная команда для тестирования или прототипирования/etc/rc.local (временно)
  • Устаревшее приложение, поставляемое с LSB init-скриптом/etc/init.d/ с update-rc.d
  • Всё, что работает в производстве — systemd, всегда

Для администраторов, управляющих несколькими серверами Ubuntu, рассмотрите возможность сочетания управления юнитами systemd с инструментами управления конфигурацией, такими как Ansible, который может идемпотентно развёртывать и включать файлы .service на всём вашем парке серверов. Если вам нужна управляемая среда с полным root-доступом для реализации этих конфигураций, VPS Хостинг с Панелью управления VPS обеспечивает гибкость для управления службами systemd напрямую без ограничений.

Для команд, выполняющих ресурсоёмкие рабочие нагрузки, требующие скриптов запуска для инициализации GPU-драйверов, сред CUDA или серверов вывода ML, среды GPU Хостинга особенно выигрывают от цепочек зависимостей After= systemd, обеспечивая полную загрузку драйверов до того, как службы приложений попытаются привязаться к аппаратным ресурсам.

Если ваши автозагружаемые скрипты взаимодействуют с конфигурациями веб-сервера или процедурами инициализации базы данных, связанными со средой панели управления, установки VPS с cPanel требуют особой осторожности — cPanel управляет собственным уровнем контроля служб, и пользовательские юниты systemd должны быть определены так, чтобы избежать конфликтов с хуками управления службами cPanel.

Технический контрольный список ключевых выводов

Перед развёртыванием любого скрипта запуска на сервере Ubuntu проверьте следующее:

  • Расположение файла юнита: Пользовательские скрипты размещаются в /etc/systemd/system/, а не в /lib/systemd/system/
  • Бит исполняемости: Проверьте с помощью ls -la /path/to/script.sh — бит x должен быть установлен
  • Абсолютные пути: Каждый двоичный файл в ExecStart использует полный путь; без зависимости от $PATH
  • Объявления зависимостей: After=network-online.target для любого скрипта, зависящего от сети
  • Тип службы: Type=simple для постоянных демонов, Type=oneshot для скриптов с однократным выполнением
  • Минимизация привилегий: User=, Group=, NoNewPrivileges=true, ProtectSystem=strict
  • Настройка ведения журналов: StandardOutput=journal и StandardError=journal для интеграции с journald
  • Выполнение daemon-reload: Всегда запускайте sudo systemctl daemon-reload после создания или редактирования файлов юнитов
  • Enable vs. start: enable создаёт символическую ссылку автозапуска; start запускает немедленно — оба требуются
  • Проверено с journalctl: Подтвердите успешное выполнение с помощью sudo journalctl -u yourservice.service --since "5 minutes ago"

FAQ

В чём разница между systemctl enable и systemctl start?

systemctl enable создаёт символическую ссылку, которая заставляет службу автоматически запускаться при следующей загрузке. systemctl start запускает службу немедленно в текущем сеансе. Обычно при первоначальной настройке новой службы требуются обе команды.

Почему моя служба systemd завершается с ошибкой «executable not found», хотя скрипт существует?

Это почти всегда означает, что путь ExecStart неверен или скрипту не хватает бита исполняемости. Проверьте с помощью which yourscript и ls -la /path/to/script. Также убедитесь, что первая строка вашего скрипта является допустимым shebang (#!/bin/bash или #!/usr/bin/env python3), поскольку systemd по умолчанию не вызывает shell для ExecStart.

Можно ли запустить скрипт при загрузке только один раз, а не при каждой загрузке?

Используйте Type=oneshot с директивой ConditionPathExists=!/var/run/myscript.done в разделе [Unit]. Скрипт создаёт файл-маркер при первом запуске; последующие загрузки пропускают выполнение, поскольку условие не выполняется.

Поддерживается ли /etc/rc.local в Ubuntu 22.04?

Да, но он отключён по умолчанию. Необходимо вручную включить юнит rc-local.service с помощью sudo systemctl enable rc-local и убедиться, что файл существует и является исполняемым. Он поддерживается как мера совместимости, а не рекомендуемая практика.

Как проверить, почему скрипт запуска не выполнился?

Запустите sudo journalctl -u yourservice.service -b, чтобы просмотреть все записи журнала для этого юнита с момента последней загрузки. При сбоях rc.local проверьте sudo systemctl status rc-local.service и просмотрите /var/log/syslog на наличие записей с временными метками во время последовательности загрузки.

15%

Сэкономьте 15% на всех хостинговых услугах

Проверьте свои навыки и получите скидку на любой тарифный план

Используйте код:

Skills
Начать