Як увімкнути автозавантаження скриптів в 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 проти 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 на наявність записів з позначками часу під час послідовності завантаження.
