Оригинал: https://www.redhat.com/en/blog/replacing-rclocal-systemd
От переводчика: Автор статьи, безусловно, шарит в теме, но она написана в 2019 году и с тех пор кое-что поменялось, так что описанный здесь метод отчасти некорректен. Читайте примечание в самом тексте, я там всё объясню.
****************************************************************
У меня недавно возникли две разные проблемы на двух хостах под управлением Линукс. Они требуют разных решений, но одним способом: надо запустить команду во время или сразу после загрузки системы.
Файл rc.local был — и в ряде случаев остается — местом, куда сисадмины прописывают команды, которые должны выполняться во время загрузки. Использование этого файла не просто устарело — я за два часа попыток не смог заставить его работать. Это несмотря на то, что в документации к systemd упоминается "генератор", который создает сервисы systemd из файла rc.local, если он существует (похоже, это не худший способ заставить прекратить чем-то пользоваться — сделать так, чтобы не работало).
Подробности моих задач не имеют особого отношения к теме, поэтому в качестве содержимого нашего локального файла для автозагрузки я использую простую команду, которую будет легко отследить. Она будет добавлять строку с датой и временем в локальный лог, чтобы проверить, работает ли наш баш-скрипт, запускаемый при загрузке.
Разница между boot (аппаратная загрузка) и startup (запуск операционной системы)
Для настройки Линукс и решения проблем, возникающих при загрузке, важно понимать, процессы загрузки и запуска ОС. На самом деле, чтобы загрузить компьютер под управлением Линукс и чтобы он мог после этого работать нужны две разных последовательности событий: загрузка устройства (boot) и загрузка операционной системы (startup). Последовательность boot начинается с аппаратного включения и заканчивается инициализацией ядра и запуском системы инициализации. После этого управление передается прцессу startup, который завершает подготовку компьютера к работе.
В целом оба процесса довольно просты для понимания и состоят из следующих шагов, которые будут подробно описаны ниже:
1. BIOS Power-On Self-Test (POST)
2. Загрузчик (GRUB)
3. Ядро
4. Система инициализации (systemd)
Детальное описание обеих последовательностей загрузки вы можете найти в моей статье «Знакомство с процессами загрузки компьютера и запуска Линукс»: https://opensource.com/article/17/2/linux-boot-and-startup
Локальные загрузочные скрипты
Иногда сисадмины добавляют в процесс загрузки системы команды для конкретной машины - например, для локального запуска процессов, не входящих в стандартную процедуру загрузки systemd. Можно создать сервисы systemd для каждой программы, которую вы хотите запускать при загрузке, но традиционный файл rc.local давал возможность прописать их все в одном исполняемом файле. В systemd тоже можно применить этот подход и поместить все команды в один файл. Это изящное решение упрощает последующее добавление новых команд, не требуя при этом создавать юниты для каждой.
Оно заключается в том, чтобы создать один сервис для systemd, а все необходимые команды поместить в исполняемый файл. Задача состоит из двух частей: очевидно, что первым делом надо создать сам исполняемый файл. Вторая часть - написание сервисного модуля (юнита) для systemd, который будет запускать этот файл.
Создание исполняемого файла
Для сисадмина, знакомого с написанием bash-скриптов, это тривиальная задача. По сути мы напишем скрипт на баше и поместим его в каталог, который в соответствии с Linux Filesystem Hierarchical Standard (FHS) предназначен для локальных бинарников - это /usr/local/bin. Можно возразить, что лучше поместить его в другой каталог, но я считаю, что логичней всего именно /usr/local/bin, так как в этом случае админу будет проще запускать его из командной строки при необходимости: этот каталог присутствует в переменной $PATH любого пользователя, включая рута.
Создайте файл mystartup.sh с содержанием, указанным ниже, поместите его в /usr/local/bin и не забудьте сделать исполняемым. Путь к bash должен соответствовать вашему дистрибутиву. Например, в форках Дебиана путь будет /bin/bash.
#!/usr/bin/bash
################################################################################
# mystartup.sh
#
# This shell program is for testing a startup like rc.local using systemd.
# By David Both
# Licensed under GPL V2
#
################################################################################
# This program should be placed in /usr/local/bin
################################################################################
# This is a test entry
echo `date +%F" "%T` "Startup worked" >> /root/mystartup.log
Запустите эту программу из командной строки. После первого исполнения в каталоге /root должен появиться файл mystartup.log с датой и временем, а также с сообщением: "Startup worked". После создания лог-файла туда будут добавляться строки при каждом выполнении скрипта, чтобы вы могли убедиться, что он работает.
Запустите скрипт еще несколько раз. В результате должно получиться что-то вроде этого:
[root@testvm1 ~]# mystartup.sh
[root@testvm1 ~]# cat mystartup.log
2019-09-12 19:58:00 Startup worked
2019-09-12 19:58:17 Startup worked
2019-09-12 19:58:54 Startup worked
2019-09-12 19:59:00 Startup worked
2019-09-12 20:01:08 Startup worked
2019-09-12 20:04:01 Startup worked
2019-09-12 20:04:13 Startup worked
2019-09-12 20:06:11 Startup worked
2019-09-12 20:06:28 Startup worked
2019-09-16 09:51:21 Startup worked
2019-09-16 09:51:51 Startup worked
Теперь в файл mystartup.sh можно добавлять нужные вам при загрузке команды.
Создание сервиса systemd
Сейчас мы создадим стандартный модуль службы systemd. Файл будет простой - он нужен только для того, чтобы скрипт mystartup.sh запускался при загрузке ОС.
Создайте файл /usr/local/lib/systemd/system/mystartup.service со следующим содержимым:
################################################################################
# mystartup.service
#
# This service unit is for testing my systemd startup service
# By David Both
# Licensed under GPL V2
#
################################################################################
# This program should be placed in /usr/local/lib/systemd/system/.
# Create a symlink to it from the /etc/systemd/system directory.
################################################################################
[Unit]
Description=Runs /usr/local/bin/mystartup.sh
[Service]
ExecStart=/usr/local/bin/mystartup.sh
[Install]
WantedBy=multi-user.target
Делать его исполняемым не надо. Можно было бы поместить его и в /etc/systemd/system, но, поскольку он локальный, лучше в /usr/local.
* Примечание переводчика: дальше автор предлагает создать символьную ссылку на этот файл в каталоге /etc/systemd/system, но если вы это сделаете, то systemd пошлет вас на три буквы при попытке выполнить скрипт. На данный момент (сентябрь 2026 г.) надо выполнить команду типа systemctl enable mystartup (или, возможно, указать полный путь к скрипту). Тогда systemd сам создаст необходимую ссылку и добавит сервис в автозагрузку.
Если вы хотите запускать команды сразу после инициализации ядра (например, чтобы остановить парковку голов hdd), то в строке WantedBy=multi-user.target надо прописать другой уровень: WantedBy=basic.target
Протестируем сервис
Прежде чем перезагрузиться и проверить результат, протестируем юнит. Для начала убедимся, что systemd видит сервис:
[root@testvm1 ~]# systemctl status mystartup
● mystartup.service - Runs /usr/local/bin/mystartup.sh
Loaded: loaded (/usr/local/lib/systemd/system/mystartup.service; linked; vendor preset: disabled)
Active: inactive (dead)
[root@testvm1 ~]#
Судя по выводу команды, юнит был распознан. Теперь давайте запустим сервис. Команда systemctl start запускает скрипт, но не добавляет сервис в автозагрузку
[root@testvm1 ~]# systemctl start mystartup
Теперь загляните в лог-файл и проверьте, появилась ли там новая строка.
Добавление сервиса в автозагрузку
Осталось только добавить сервис, чтобы он запускался при загрузке системы:
[root@testvm1 ~]# systemctl enable mystartup
Created symlink /etc/systemd/system/multi-user.target.wants/mystartup.service →
/usr/local/lib/systemd/system/mystartup.service.
[root@testvm1 ~]#
Заключительный тест
До перезагрузки давайте посмотрим, как можно использовать команду journalctl для просмотра только той части журнала, которая касается mystartup.service. Также эту команду можно использовать для проверки работы скрипта, поскольку systemd логирует все свои действия.
Следующая команда с параметром -u показывает только строки журнала, относящиеся к юниту mystartup:
[root@testvm1 ~]# journalctl -u mystartup
-- Logs begin at Mon 2019-04-15 22:50:27 EDT, end at Mon 2019-09-16 11:44:30 EDT. --
Sep 16 11:09:28 testvm1 systemd[1]: Started Runs /usr/local/bin/mystartup.sh.
[root@testvm1 ~]#
Теперь давайте перезагрузимся и проверим логи:
[root@testvm1 ~]# systemctl status mystartup
● mystartup.service - Runs /usr/local/bin/mystartup.sh
Loaded: loaded (/usr/local/lib/systemd/system/mystartup.service; enabled; vendor preset: disabled)
Active: inactive (dead) since Mon 2019-09-16 11:45:59 EDT; 1min 30s ago
Process: 819 ExecStart=/usr/local/bin/mystartup.sh (code=exited, status=0/SUCCESS)
Main PID: 819 (code=exited, status=0/SUCCESS)
Sep 16 11:45:55 testvm1 systemd[1]: Started Runs /usr/local/bin/mystartup.sh.
[root@testvm1 ~]# journalctl -u mystartup
-- Logs begin at Mon 2019-04-15 22:50:27 EDT, end at Mon 2019-09-16 11:47:45 EDT. --
Sep 16 11:09:28 testvm1 systemd[1]: Started Runs /usr/local/bin/mystartup.sh.
-- Reboot --
Sep 16 11:45:55 testvm1 systemd[1]: Started Runs /usr/local/bin/mystartup.sh.
[root@testvm1 ~]#
Заключение
Скрипт, который мы написали на баше, запускается один раз при загрузке системы и завершает работу. Он не остается в памяти как фоновый процесс, поскольку не предназначен для этого.
На случай, если вы не заметили, описанная процедура по созданию локального сервиса и добавлению его в автозагрузку подходит для создания любого сервиса systemd. Теперь, когда мы знаем, как это делается, все не так уж сложно.
UPD: вскоре после публикации этой статьи я получил на электронную почту сообщение от Тома Мерфи. Он рассказал, что в самом systemd существует сервис rc-local. Я благодарен ему за письмо, так как не знал об этом.
Поддержку традиционного rc.local можно осуществить командой systemctl enable rc-local. Перечисленные в файле rc.local команды выполнятся при следующей загрузке. Разумеется, этой же командой можно запустить rc.local немедленно.
Тем не менее, rc.local по-прежнему остается устаревшим. Вот что написано в мануале для systemd-rc-local-generator: «Поддержка /etc/rc.local сохранена только для совместимости с некоторыми системами на System V. На сегодняшний день мы настоятельно не рекомендуем пользоваться этим скриптом. Вместо этого следует создавать соответствующие юниты с нужными зависимостями для каждого скрипта, который должен запускаться при старте системы».
ОТ ПЕРЕВОДЧИКА: Я выкладываю эти статьи прежде всего для тех, кто пытается приспособить линукс для домашнего использования (например, парковку головок на ноутбучном hdd можно останавливать в начале загрузки), поэтому на всякий случай резюмирую по шагам:
1. Создать скрипт и поместить его в /usr/local/bin. Приведенный в статье скрипт удобен тем, что дает возможность проверить его работу, но после заключительной проверки я бы на вашем месте закомментировала эту строку, особенно если у вас SSD.
2. Написать юнит и положить его в /usr/local/lib/systemd/system/. На самом деле дома можно сразу кидать в /etc/systemd/system, но если хотите цивильно по правилам, то надо в /usr/local.
3, Если вы поместили юнит в /etc, можете сначала запустить юнит командой start, как написано в статье. Если поместили в /usr/local, то надо запускать командой systemctl enable servicename. Ссылку создавать вообще не надо.
Использованные ресурсы:
Здесь некоторые названия ссылок говорят сами за себя, поэтому я не стала уточнять, что это.
Linux Filesystem Hierarchical Standard (FHS): http://www.linux-databook.info/?page_id=2609
https://en.wikipedia.org/wiki/GNU_GRUB
https://www.gnu.org/savannah-checkouts/gnu/grub/manual/grub/grub.html
https://en.wikipedia.org/wiki/Master_boot_record
https://en.wikipedia.org/wiki/Multiboot_specification
https://en.wikipedia.org/wiki/Systemd
Systemd bootup process: https://www.freedesktop.org/software/systemd/man/latest/bootup.html?__goaway_challenge=meta-refresh&__goaway_id=d2d3dc1830f49b19c195b6a9b4bce98c&__goaway_referer=https%3A%2F%2Fwww.redhat.com%2F
List all manpages from the systemd project: https://www.freedesktop.org/software/systemd/man/latest/index.html
An introduction to the Linux boot and startup processes: https://opensource.com/article/17/2/linux-boot-and-startup?extIdCarryOver=true&sc_cid=RHCTG0180000382526
No comments:
Post a Comment