Как приложение Django на Raspberry Pi может безопасно запустить обновление самого себя с помощью apt и systems?
Я распространяю веб-приложение Django/Python в виде пакета Debian на Raspberry Pi. Я бы хотел реализовать кнопку "Обновить" в веб-интерфейсе, которая запускает sudo apt update && sudo apt -y install my_app
для обновления пакета приложений.
Проблема в том, что приложение пытается заменить само себя, пока оно еще запущено — сам код, инициирующий обновление, является частью того, что будет обновлено.
То, что я пробовал:
Кнопка обновления отправляет сообщение в серверную часть Django.
Серверная часть использует подпроцесс python для запуска модуля oneshot systemd (gui-package-update.service), который запускает команды обновления (через sudo).
Само приложение Django запускается при загрузке с помощью другой системной службы и запускается от имени пользователя без прав root.
Я настроил sudoers так, чтобы пользователь Django мог запускать sudo systemctl start gui-package-update.service
без пароля.
Второй вызов api POST используется для отслеживания состояния обновления, а именно:
- запуск - сервер запущен
- обновление - сервер получил команду POST для запуска обновления
- 404/500 - в данный момент сервер перезагружается
Ошибки 404/500 обнаруживаются во внешнем интерфейсе JS и интерпретируются как означающие, что сервер "перезагружается".
Проблема:
Этот подход работает только в первый раз после перезагрузки. При последующих попытках служба oneshot больше не запускается. Хотя в состоянии сбоя нет служб, я попытался сбросить настройки службы с помощью systemctl reset-failed
, но это не устранило проблему.
Я подозреваю, что сценарий postinst нового пакета Debian запускает systemctl daemon-reexec или systemctl daemon-reload, что мешает активному процессу systemd, запущенному приложением во время обновления.
Вопрос:
Каков надежный шаблон проектирования, позволяющий приложению Django запускать собственное обновление с помощью кнопки графического интерфейса, учитывая ограничения apt, systemd и тот факт, что выполняется замена кода?
Я ищу подходы, которые:
- Избегайте конфликтов между перезагрузкой системы и запущенными службами
- Разрешить повторное обновление без необходимости перезагрузки
- Безупречная работа с пакетами Debian и скриптами postinst
Я не собираюсь настаивать на использовании одноразового сервиса, это просто тот подход, который я испробовал.
Вот пример моего служебного файла unit:
[Unit]
Description=Update specific ODIN16 GUI and collateral packages
After=network.target
[Service]
Type=oneshot
ExecStart=/bin/sh -c 'apt update && apt-get install -y my_app'
StandardOutput=append:/var/log/gui-package-update.log
StandardError=append:/var/log/gui-package-update.log
User=root
Group=root
RemainAfterExit=no
Restart=no
[Install]
WantedBy=multi-user.target