Запуск Python-скрипта как службы systemd

В Linux Python-скрипт можно запустить как системную службу с помощью ‘’‘systemd’’’ .

Это удобно для Telegram-ботов, веб-сервисов и других приложений, которые должны:

  • автоматически запускаться вместе с сервером;
  • работать в фоновом режиме;
  • автоматически перезапускаться после ошибки;
  • управляться стандартными командами systemctl;
  • сохранять журнал работы в journalctl.

В этой статье рассмотрим два варианта запуска Python-приложения на примере Telegram-бота ‘’‘noProdaction’’’.

TOC

Способ №1 — запуск через отдельный shell-скрипт

В этом варианте systemd запускает вспомогательный shell-скрипт, который, в свою очередь, запускает Python-приложение.

Схема запуска:

systemd
   │
   ▼
noProdaction.sh
   │
   ▼
python3
   │
   ▼
main.py

Создание скрипта запуска

Создаём каталог для скрипта запуска, если его ещё нет:

mkdir -p /opt/Python/Telegram/start

Создаём файл:

nano /opt/Python/Telegram/start/noProdaction.sh

Добавляем:

#!/bin/bash

exec /usr/bin/python3.11 /opt/Python/Telegram/noProdaction/noprodaction-1/main.py
Сохраняем файл:

<pre>
Ctrl + O
Enter
Ctrl + X
</pre>

Делаем его исполняемым:

<syntaxhighlight lang="bash">
chmod +x /opt/Python/Telegram/start/noProdaction.sh

Шаблон:Note

Создание systemd unit

Создаём unit-файл:

nano /etc/systemd/system/noprodaction.service

Добавляем:

[Unit]
Description=Telegram bot noProdaction
After=network-online.target
Wants=network-online.target

[Service]
Type=simple
User=root
WorkingDirectory=/opt/Python/Telegram/noProdaction/noprodaction-1
ExecStart=/opt/Python/Telegram/start/noProdaction.sh

Restart=on-failure
RestartSec=5

NoNewPrivileges=true
PrivateDevices=true
ProtectSystem=full

[Install]
WantedBy=multi-user.target
Сохраняем файл:

<pre>
Ctrl + O
Enter
Ctrl + X
</pre>

=== Активация службы ===

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

<syntaxhighlight lang="bash">
systemctl daemon-reload

Добавляем службу в автозагрузку:

systemctl enable noprodaction.service

Запускаем:

systemctl start noprodaction.service

Можно выполнить обе команды сразу:

systemctl enable --now noprodaction.service

Проверка работы

Проверяем состояние:

systemctl status noprodaction.service

При нормальной работе должна присутствовать строка:

Active: active (running)

Например:

● noprodaction.service - Telegram bot noProdaction
     Loaded: loaded (/etc/systemd/system/noprodaction.service; enabled)
     Active: active (running)
   Main PID: 173 (python3.11)
      Tasks: 2
     Memory: 34.1M
        CPU: 1.766s

Способ №2 — запуск Python напрямую из systemd

Шаблон:Note

Схема становится проще:

systemd
   │
   ▼
python3
   │
   ▼
main.py

Создаём unit-файл:

nano /etc/systemd/system/noprodaction.service

Добавляем:

[Unit]
Description=Telegram bot noProdaction
After=network-online.target
Wants=network-online.target

[Service]
Type=simple

User=root
WorkingDirectory=/opt/Python/Telegram/noProdaction/noprodaction-1

ExecStart=/usr/bin/python3.11 /opt/Python/Telegram/noProdaction/noprodaction-1/main.py

Restart=on-failure
RestartSec=5

NoNewPrivileges=true
PrivateDevices=true
ProtectSystem=full

[Install]
WantedBy=multi-user.target
После создания файла выполняем:

<syntaxhighlight lang="bash">
systemctl daemon-reload
systemctl enable --now noprodaction.service

Проверяем:

systemctl status noprodaction.service

Управление службой

После создания unit-файла ботом можно управлять стандартными командами systemd.

Действие Команда

Проверить состояние

systemctl status noprodaction

-

Запустить

systemctl start noprodaction

-

Остановить

systemctl stop noprodaction

-

Перезапустить

systemctl restart noprodaction

-

Добавить в автозагрузку

systemctl enable noprodaction

-

Убрать из автозагрузки

systemctl disable noprodaction

}

Просмотр логов

Одно из преимуществ запуска через systemd — вывод Python-приложения автоматически попадает в системный журнал.

Посмотреть последние записи:

journalctl -u noprodaction.service

Последние 100 строк:

journalctl -u noprodaction.service -n 100

Следить за журналом в реальном времени:

journalctl -u noprodaction.service -f

Выйти из режима просмотра:

Ctrl + C

Посмотреть журнал только с момента последней загрузки сервера:

journalctl -u noprodaction.service -b

После изменения Python-кода

Если был изменён только код main.py или других файлов приложения, выполнять daemon-reload не требуется.

Достаточно перезапустить приложение:

systemctl restart noprodaction

Если же был изменён сам файл:

/etc/systemd/system/noprodaction.service

необходимо выполнить:

systemctl daemon-reload
systemctl restart noprodaction

Автоматический перезапуск

В нашем unit-файле используются параметры:

Restart=on-failure
RestartSec=5

Это означает, что если Python-процесс аварийно завершится, systemd подождёт 5 секунд и попробует запустить его снова.

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

systemctl show noprodaction.service -p NRestarts

Проверка пути к Python

Путь к Python может отличаться в зависимости от системы.

Проверить его можно командой:

which python3

Например:

/usr/bin/python3

Версию Python:

python3 --version

Если конкретная версия Python не требуется, в unit-файле можно использовать:

ExecStart=/usr/bin/python3 /opt/Python/Telegram/noProdaction/noprodaction-1/main.py

Запуск через Python Virtual Environment

Если проект использует собственное виртуальное окружение Python, рекомендуется запускать Python непосредственно из него.

Например, если виртуальное окружение находится здесь:

/opt/Python/Telegram/noProdaction/venv/

то ExecStart будет выглядеть так:

ExecStart=/opt/Python/Telegram/noProdaction/venv/bin/python /opt/Python/Telegram/noProdaction/noprodaction-1/main.py

Активировать venv командой source внутри systemd не требуется.

Рекомендуемый unit-файл

Для обычного Python-приложения итоговый вариант может выглядеть следующим образом:

[Unit]
Description=Telegram bot noProdaction
After=network-online.target
Wants=network-online.target

[Service]
Type=simple
User=root

WorkingDirectory=/opt/Python/Telegram/noProdaction/noprodaction-1
ExecStart=/usr/bin/python3.11 /opt/Python/Telegram/noProdaction/noprodaction-1/main.py

Restart=on-failure
RestartSec=5

NoNewPrivileges=true
PrivateDevices=true
ProtectSystem=full

[Install]
WantedBy=multi-user.target
После создания:

<syntaxhighlight lang="bash">
systemctl daemon-reload
systemctl enable --now noprodaction.service

Проверяем:

systemctl status noprodaction.service

и журнал:

journalctl -u noprodaction.service -f

Примечание по безопасности

В примерах выше служба запускается от пользователя:

User=root

Для собственного сервера это работает, однако большинству Python-приложений права root не нужны.

Более безопасный вариант — создать отдельного системного пользователя для приложения и запускать службу от его имени.

Например:

useradd --system --home /opt/Python/Telegram/noProdaction --shell /usr/sbin/nologin noprodaction

После этого необходимо выдать пользователю права на необходимые файлы приложения и заменить в unit-файле:

User=root

на:

User=noprodaction

Шаблон:Warning

Итог

Для простого Python-приложения рекомендуется использовать ‘’‘Способ №2’’’:

             Загрузка Linux
                    │
                    ▼
                 systemd
                    │
                    ▼
         noprodaction.service
                    │
                    ▼
                Python
                    │
                    ▼
                main.py
                    │
                    ▼
             Telegram Bot

После этого приложение автоматически запускается вместе с системой, работает в фоне и при аварийном завершении автоматически запускается снова.