Освоение файлов служб Systemd: Подробное руководство
Создавайте надежные файлы служб systemd с правильными секциями модулей, поведением перезапуска, журналами, безопасностью и таймерами.
Освоение файлов служб Systemd: Полное руководство
Файлы служб Systemd сообщают Linux, как запускать, останавливать, перезапускать и контролировать ваше приложение. Если ваша служба запускается вручную, но не работает при загрузке, перезапускается слишком агрессивно или записывает журналы не в то место, обычно проблема кроется в файле модуля.
Это руководство посвящено созданию и настройке файлов модулей служб systemd с нуля. Вы увидите основные секции, работающий пример службы на Python, распространенные команды для устранения неполадок, а также несколько средств контроля безопасности и ресурсов, которые стоит использовать в production.
Понимание файлов модулей Systemd
Systemd использует файлы модулей для описания различных системных ресурсов, таких как службы, сокеты, устройства, точки монтирования и многое другое. Файл модуля службы, обычно с расширением .service, определяет, как systemd должен управлять конкретным демоном или приложением.
Эти файлы организованы в секции, каждая из которых содержит пары "ключ-значение", представляющие директивы конфигурации. Основные секции, на которых мы сосредоточимся: [Unit], [Service] и [Install].
Анатомия файла службы Systemd
Типичный файл службы systemd имеет следующую структуру:
[Unit]
Description=Краткое описание службы.
After=network.target
[Service]
Type=simple
ExecStart=/usr/local/bin/my_application --config /etc/my_app.conf
Restart=on-failure
User=myuser
Group=mygroup
[Install]
WantedBy=multi-user.target
Давайте разберем каждую секцию и ее распространенные директивы:
Секция [Unit]
Эта секция предоставляет метаданные о модуле и определяет его взаимосвязь с другими модулями. Она используется для зависимостей и упорядочивания.
Description=: Человекочитаемое имя службы. Это то, что вы увидите в выводеsystemctl status.Documentation=: URL-адреса или пути к документации для службы.Requires=: Определяет строгие зависимости. Если модуль, указанный здесь, не запустится, этот модуль также не запустится.Wants=: Определяет слабые зависимости. Если модуль, указанный здесь, не запустится, этот модуль все равно попытается запуститься.Before=: Гарантирует, что этот модуль запускается до модулей, перечисленных здесь.After=: Управляет только упорядочиванием. Например,After=network.targetзапускает этот модуль после базового сетевого target, но не гарантирует внешнюю связность. Сетезависимым службам может потребоватьсяAfter=network-online.targetплюс сервис ожидания сети вашего дистрибутива.Conflicts=: Если запускается модуль, указанный здесь, этот модуль будет остановлен, и наоборот.
Секция [Service]
Эта секция настраивает поведение самой службы. Здесь вы определяете, как запускать, останавливать и управлять процессом.
Type=: Указывает тип запуска процесса. Распространенные значения:simple(по умолчанию): Основной процесс — это тот, который указан вExecStart=. Systemd считает, что служба запущена сразу после разветвления процессаExecStart=.forking: ПроцессExecStart=порождает дочерний процесс, а родительский завершается. Systemd считает службу запущенной, когда родительский процесс завершается. С этим типом часто нужно указыватьPIDFile=.oneshot: Похож наsimple, но ожидается, что процесс завершится после выполнения своей работы. Полезно для скриптов настройки.notify: Демон отправляет уведомление systemd, когда он успешно запущен. Это предпочтительный тип для современных демонов, которые его поддерживают.dbus: Служба получает имя D-Bus.
ExecStart=: Команда для запуска службы. Для большинства типов служб используйте одну командуExecStart=. Несколько строкExecStart=допустимы дляType=oneshot, где они выполняются последовательно.ExecStop=: Команда для остановки службы.ExecReload=: Команда для перезагрузки конфигурации службы без перезапуска.Restart=: Определяет, когда служба должна автоматически перезапускаться. Распространенные значения:no(по умолчанию): Никогда не перезапускать.on-success: Перезапускать только если служба завершилась чисто (код выхода 0).on-failure: Перезапускать, если служба завершилась с ненулевым кодом выхода, была завершена сигналом или произошел тайм-аут.on-abnormal: Перезапускать, если завершена сигналом или произошел тайм-аут.on-abort: Перезапускать только если завершена нечисто сигналом.always: Всегда перезапускать, независимо от статуса выхода.
RestartSec=: Время ожидания перед перезапуском службы (по умолчанию 100 мс).User=: Пользователь, от имени которого запускать службу.Group=: Группа, от имени которой запускать службу.WorkingDirectory=: Каталог, в который нужно перейти перед выполнением команд.Environment=: Устанавливает переменные окружения для службы.EnvironmentFile=: Читает переменные окружения из файла.PIDFile=: Путь к PID-файлу (часто используется сType=forking).StandardOutput=/StandardError=: Управляет тем, куда направляются stdout и stderr, например,journal,nullилиinherit. В распространенных дистрибутивах на основе systemd вывод службы обычно попадает в журнал, если настройки менеджера по умолчанию не были изменены.
Секция [Install]
Эта секция определяет, как модуль должен быть включен или отключен, обычно путем создания символических ссылок.
WantedBy=: Указывает target, который должен "хотеть" эту службу, когда она включена. Распространенные значения:multi-user.target: Для служб, которые должны запускаться, когда система достигает многопользовательского режима командной строки.graphical.target: Для служб, которые должны запускаться, когда система достигает графического режима входа.
Создание вашего первого файла службы Systemd
Давайте создадим простой файл службы для гипотетического Python-скрипта с именем my_app.py, расположенного в /opt/my_app/my_app.py.
1. Создайте файл службы:
Файлы служб для пользовательских приложений обычно размещаются в /etc/systemd/system/. Назовем наш файл my_app.service.
# Создайте каталог, если он не существует
sudo mkdir -p /etc/systemd/system/
# Создайте файл службы с помощью текстового редактора
sudo nano /etc/systemd/system/my_app.service
2. Добавьте следующее содержимое в my_app.service:
[Unit]
Description=Мое пользовательское Python-приложение
After=network.target
[Service]
Type=simple
User=appuser
Group=appgroup
WorkingDirectory=/opt/my_app/
ExecStart=/usr/bin/python3 /opt/my_app/my_app.py
Restart=on-failure
[Install]
WantedBy=multi-user.target
Пояснение к примеру:
Description: Четко идентифицирует наше приложение.After=network.target: Гарантирует доступность сети перед запуском.Type=simple: Предполагает, чтоmy_app.pyявляется основным процессом и не разветвляется.User=appuser,Group=appgroup: Указывает пользователя и группу, от имени которых должно запускаться приложение. Убедитесь, что эти пользователи и группы существуют в вашей системе и имеют соответствующие разрешения. Возможно, вам потребуется их создать:sudo groupadd appgroup sudo useradd -r -g appgroup appuser sudo chown -R appuser:appgroup /opt/my_app/WorkingDirectory: Устанавливает контекст для скрипта.ExecStart: Команда для запуска Python-скрипта. Убедитесь, что/usr/bin/python3— правильный путь к вашему интерпретатору Python и что скрипт является исполняемым.Restart=on-failure: Если скрипт аварийно завершится, systemd попытается перезапустить его.WantedBy=multi-user.target: Эта служба будет запускаться автоматически при загрузке системы в многопользовательскую среду.
3. Перезагрузите конфигурацию менеджера systemd:
После создания или изменения файла службы вы должны сообщить systemd о необходимости перезагрузить его конфигурацию.
sudo systemctl daemon-reload
4. Включите и запустите службу:
- Включить: Это заставляет службу запускаться автоматически при загрузке.
sudo systemctl enable my_app.service - Запустить: Это запускает службу немедленно.
sudo systemctl start my_app.service
5. Проверьте статус службы:
Чтобы проверить, работает ли ваша служба, и увидеть возможные ошибки:
sudo systemctl status my_app.service
Если есть проблемы, команда status часто показывает сообщения об ошибках или журналы из journald.
6. Просмотр журналов:
Systemd интегрируется с journald для ведения журнала. Вы можете просматривать журналы для вашей службы с помощью:
sudo journalctl -u my_app.service
Вы также можете следить за журналами в реальном времени:
sudo journalctl -f -u my_app.service
Другие полезные команды
- Остановить службу:
sudo systemctl stop my_app.service - Перезапустить службу:
sudo systemctl restart my_app.service - Перезагрузить конфигурацию (если поддерживается приложением):
sudo systemctl reload my_app.service - Отключить автозапуск при загрузке:
sudo systemctl disable my_app.service
Расширенная конфигурация и лучшие практики
Вопросы безопасности
- Запускайте службы от имени пользователей, не являющихся root: Всегда указывайте
User=иGroup=, если это не абсолютно необходимо. Это следует принципу наименьших привилегий. - Изолируйте службы: Рассмотрите возможности изоляции, такие как
PrivateTmp=true,ProtectSystem=strict,ProtectHome=trueиNoNewPrivileges=true. Тестируйте их с вашим приложением, так как они могут блокировать легитимные записи в файлы.PrivateTmp=true: Предоставляет службе собственные частные каталоги/tmpи/var/tmp.ProtectSystem=strict: Делает большую часть файловой системы доступной только для чтения для службы. ИспользуйтеReadWritePaths=для каталогов, в которые служба должна записывать.NoNewPrivileges=true: Предотвращает получение службой новых привилегий.
Обработка сложных запусков
Type=forkingсPIDFile=: Для старых приложений, которые разветвляются, убедитесь, чтоPIDFile=указывает на правильный файл.Type=notify: Если ваше приложение это поддерживает, это самый надежный способ для systemd узнать, когда оно действительно готово.ExecStartPre=иExecStartPost=: Команды для выполнения до и послеExecStart=. Полезно для задач настройки или очистки.
Контроль ресурсов
Systemd позволяет ограничивать использование ресурсов:
CPUWeight=: Относительный вес ЦП для службы.MemoryMax=: Максимальный объем памяти, который может использовать служба.IOWeight=: Относительный вес ввода-вывода, где это поддерживается ядром и настройкой cgroup.
Пример:
[Service]
# ... другие директивы ...
MemoryMax=512M
CPUWeight=50
Таймеры vs. Cron
Таймеры Systemd предлагают современную альтернативу традиционным cron-задачам. Они более гибкие и лучше интегрируются с системой журналирования и управления зависимостями systemd.
- Cron: Запланированные задачи, определенные в файлах
crontab. - Таймеры Systemd (модули
.timer): Эти модули планируют модули.service. Вы определяете файл.timer, который указывает, когда должен запускаться соответствующий файл.service.
Пример:
Чтобы запускать скрипт ежедневно в 3:00 утра:
my_script.service: Служба для запуска.[Unit] Description=Мой ежедневный скрипт [Service] Type=oneshot ExecStart=/opt/my_scripts/run_daily.sh User=scriptusermy_script.timer: Таймер, который планирует службу.[Unit] Description=Запускать мой ежедневный скрипт раз в день [Timer] # Запускать в 03:00 каждый день OnCalendar=*-*-* 03:00:00 # Запускать вскоре после загрузки, если запланированное время было пропущено, пока машина была выключена. Persistent=true [Install] WantedBy=timers.target
Чтобы использовать это:
- Поместите оба файла в
/etc/systemd/system/. - Выполните
sudo systemctl daemon-reload. - Включите и запустите таймер:
sudo systemctl enable my_script.timerиsudo systemctl start my_script.timer.
Таймеры предлагают такие преимущества, как Persistent=true (запускает пропущенные задачи при загрузке), календарные события (например, hourly, daily, weekly) и лучшую интеграцию с journalctl.
Устранение распространенных неполадок
- Служба не запускается: Проверьте
systemctl status <имя_службы>иjournalctl -u <имя_службы>. Ищите опечатки, неверные пути, отсутствующие зависимости или ошибки разрешений. - Неверный
Type=: Если служба немедленно завершается с ошибкой или зависает,Type=может быть неверным. Попробуйтеsimpleилиforkingи убедитесь, чтоPIDFileкорректен при использованииforking. - Отказано в доступе: Убедитесь, что указанные
User=иGroup=имеют права на чтение/запись необходимых файлов и каталогов. - Переменные окружения: Если ваше приложение полагается на определенные переменные окружения, убедитесь, что они правильно установлены с помощью
Environment=илиEnvironmentFile=. - Зависимости: Проверьте, что
After=,Wants=иRequires=соответствуют вашим намерениям.After=упорядочивает запуск; он сам по себе не подтягивает другой модуль.
Перед включением нового модуля на production-хосте выполните:
sudo systemd-analyze verify /etc/systemd/system/my_app.service
Это выявляет многие синтаксические ошибки и ошибки в директивах до того, как вы начнете полагаться на службу при загрузке.
Ключевой вывод
Напишите наименьший файл службы, который точно описывает ваше приложение, затем намеренно добавьте политику перезапуска, ведение журнала, ограничения безопасности и лимиты ресурсов. После каждого изменения выполняйте systemctl daemon-reload, проверяйте модуль и смотрите systemctl status плюс journalctl -u, прежде чем доверять ему в production.