Как приложение 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
Вернуться на верх