Скрипт Python как сервис rc в операционной системе FreeBSD

Вопрос или проблема

У меня есть скрипт на Python, который я хотел бы запустить в тюремной среде FreeNAS. Он работает без демона сервиса, но я хотел бы запустить его как сервис. Я создал следующий скрипт сервиса /etc/rc.d/attendance

#!/bin/sh

# PROVIDE: attendance
# REQUIRE: DAEMON
# KEYWORD: shutdown

. /etc/rc.subr

name=attendance
rcvar=attendance_enable

load_rc_config $name

: ${attendance_enable="NO"}

pidfile="/var/run/${name}.pid"
command="/root/Zkteco/app.py"
command_interpreter=/usr/local/bin/python
run_rc_command "$1"

Я также добавил attendance_enable="YES" в файл /etc/rc.conf.

Когда я запускаю service attendance start, я получаю

Starting attendance
limits: /root/Zkteco/app.py: Нет такого файла или директории
/etc/rc.d/attendance: WARNING: не удалось запустить attendance

Хотя директория существует, я пытался переместить её из /etc/rc.d/attendance в /usr/local/etc/rc.d/attendance, и, похоже, это сработало, но она так и не идёт в фоновый процесс, и мне приходится использовать Ctrl+C, чтобы остановить её.

Есть ли какие-либо рекомендации? Скрипт представляет собой веб-приложение, использующее FastAPI и Uvicorn, имеет ли это какое-либо значение? Как я могу увидеть логи ошибок сервиса при попытке его запустить.

Что я пытался

Я создал исполняемый файл с помощью pyinstaller --onefile из скрипта. И изменил

#!/bin/sh
.
.
.
command="/usr/local/bin/app"
run_rc_command "$1

Это изменение заставило сервис запуститься, но опять же, он так и не уходит в фоновый режим.

Файлы в /etc/rc.d считаются частью операционной системы и управляются ею. Вы должны только работать с /usr/local/etc/rc.d. Обратите внимание, что это расположение также предназначено для rc-скриптов всех пакетов.

rc.d – это рамка управления сервисами. Но она не делает ничего для создания “сервиса” или, точнее, “демонизации процесса”. Это предусмотрено для обеспечения унифицированного способа работы с “сервисами”.

Вы заметили, что ваше приложение не ушло в фоновый режим, как вы ожидали, и вам нужно было использовать CTRL-C для выхода. Ваши ожидания не соответствуют тому, что должно происходить. Это ответственность вашего скрипта/программы/приложения – демонизироваться (уйти в фоновый режим и т.д.). Скрипт rc – это лишь обертка вокруг вашего приложения.

Я не очень хорошо знаю ваше приложение и uvicorn. Но из документации видно, что uvicorn, похоже, не поддерживает самодемонизацию. Обычно это происходит при указании параметра командной строки. Ваш основной тест для этого: можете ли вы запустить ваше приложение из командной строки и заставить его отсоединиться от консоли самостоятельно?

Если вы хотите чистый способ на Python для этого, то посмотрите на PEP 3143 – Стандартная библиотека демонов.

Легкий подход для FreeBSD – использовать daemon(8). Простой пример:

#!/bin/sh

# REQUIRE: LOGIN

. /etc/rc.subr

name=attendance
rcvar=`set_rcvar`
pidfile="/var/run/${name}.pid"
attendance_user="somenotrootuser"
command="/usr/sbin/daemon"
command_args="-c -f -P ${pidfile} -u ${attendance_user} -r /usr/local/bin/app"

load_rc_config $name
run_rc_command "$1"

(См. Есть ли простой способ создать скрипт rc для FreeBSD? и Скрипт FreeBSD rc.d не запускается как демон)

Я замечаю, что вы разместили ваше приложение в /root. Это может свидетельствовать о том, что вы запускаете вещи от имени root. Хорошей практикой является запуск вещей от непривилегированного пользователя.

Это должно сработать для вашего простого случая. Если это полноценный производственный развертывание, вам нужно будет сделать больше, но это связано с uvicorn, а не с FreeBSD. Если вы прочитаете https://www.uvicorn.org/deployment/, вы заметите, что в производственной среде вы часто будете использовать gunicorn и, возможно, nginx тоже.

Вышеуказанное – это “правильный” способ делать вещи в FreeBSD. Если вам нравится использовать FreeBSD, это хорошо для изучения и работы.

Но если вы больше программист Python/веб и находите настройку gunicorn и т.д. трудной, то вам может быть проще установить supervisord. Он легко устанавливается как пакет sysutils/py-supervisor/. Это можно рассматривать как альтернативу rc/daemon, и вы найдете гораздо больше примеров онлайн, специально для использования с веб-проектами на Python.

Ответ или решение

Для успешного запуска вашего Python-скрипта как службы (service) в системе FreeBSD, необходимо следовать нескольким ключевым шагам. Рассмотрим их подробнее, чтобы избежать распространённых ошибок и настроить сервис эффективно.

1. Правильное размещение скрипта

Первоначально важно отметить, что скрипты службы должны находиться в каталоге /usr/local/etc/rc.d/, а не в /etc/rc.d/, который управляется системой. Убедитесь, что ваш скрипт, например, attendance, расположен в правильной директории:

/usr/local/etc/rc.d/attendance

2. Корректный синтаксис скрипта службы

Ваш первоначальный скрипт выглядит неплохо с точки зрения структуры, но важно обратить внимание на несколько моментов. Пример оптимизированного скрипта может выглядеть так:

#!/bin/sh

# PROVIDE: attendance
# REQUIRE: DAEMON
# KEYWORD: shutdown

. /etc/rc.subr

name=attendance
rcvar=`set_rcvar`

pidfile="/var/run/${name}.pid"
command="/usr/sbin/daemon"
command_args="-P ${pidfile} -u someuser /usr/local/bin/python /root/Zkteco/app.py" 

load_rc_config $name
run_rc_command "$1"

В этом примере:

  • Я использую daemon, чтобы запустить вашу программу в фоновом режиме.
  • Замените someuser на имя пользователя, под которым должна работать служба. Запуск от имени пользователя с привилегиями root может быть небезопасен.
  • Проверьте путь к вашему app.py, убедитесь, что он прописан правильно.

3. Настройка конфигурации

Убедитесь, что в вашем файле конфигурации /etc/rc.conf добавлена следующая строка:

attendance_enable="YES"

4. Проверка наличия ошибок

Чтобы диагностировать проблемы при запуске, вы можете использовать следующие методы:

  • Проверьте общий лог системы, который может содержать ошибки, связанные с вашей службой:
cat /var/log/messages
  • Можно также поменять command_args в вашем скрипте, чтобы перенаправить стандартный вывод в файл логирования:
command_args="-P ${pidfile} -u someuser /usr/local/bin/python /root/Zkteco/app.py >> /var/log/attendance.log 2>&1"

Это перенаправит как стандартный, так и ошибочный вывод в файл attendance.log.

5. Управление фоновым процессом

Как вы правильно заметили, сервер Uvicorn не завершает процесс (daemonize). Для этого либо используйте daemon в вашем скрипте, как показано выше, либо используйте инструменты, такие как supervisord, которые проще для управления процессами на Python. Установите supervisord следующей командой:

pkg install py38-supervisor

Затем создайте конфигурационный файл для вашего приложения, указав команду запуска вашего Python-скрипта.

[program:attendance]
command=/usr/local/bin/python /root/Zkteco/app.py
autostart=true
autorestart=true
stderr_logfile=/var/log/attendance.err.log
stdout_logfile=/var/log/attendance.out.log

Это обеспечит управление вашим приложением и возможность автоматического перезапуска в случае сбоев.

Заключение

Следуя этим шагам, вы сможете успешно создать и запустить службу для вашего Python-скрипта на базе FreeBSD. Не забудьте внимательно проверять логи для диагностики возможных проблем.

Оцените материал
Добавить комментарий

Капча загружается...