Стратегия автоматического обновления программного обеспечения / прошивки

9

У меня есть проект среднего размера, который как раз близок к завершению фазы «неаккуратных кофеиновых прототипов для демонстраций клиентов» и переходит в фазу «думать о будущем». Проект состоит из устройств на базе Linux с программным обеспечением и встроенным программным обеспечением, а также центрального административного веб-сервера. В настоящее время существует 10 прототипов, производство которых ожидается на уровне порядка 1000-х.

Не будучи достаточно сведущим в искусстве автоматического обновления и не имея достаточного времени, я быстро разработал собственную стратегию развертывания / автоматического обновления программного обеспечения и, честно говоря, это отстой. В настоящее время он состоит из следующего:

  • Хостинг-репозиторий Git (GitLab) с веткой производственного выпуска (обратите внимание, что источник веб-сервера также находится в этом же репо, как и некоторые другие вещи).
  • Кнопка «развернуть обновление» в веб-интерфейсе, которая:
    1. Извлекает последнюю версию из ветки производственного выпуска в локальную область репозитория, а также копирует ее во временную область подготовки пакета.
    2. Запускает сценарий санации (хранящийся в репозитории) в промежуточной области для удаления несвязанных исходных файлов (например, источника сервера, источника встроенного ПО и т. Д.) И файлов .git.
    3. Записывает текущий git-хеш в файл в пакете обновления (цель станет ясна ниже).
    4. Если все прошло хорошо, он распаковывает его и готовит его для обслуживания, перезаписывая предыдущий пакет gzip файлом с тем же именем, а затем удаляет промежуточную область.
    5. Обратите внимание, что теперь на сервере имеется две копии текущего программного обеспечения устройства, которые, как ожидается, будут синхронизированы: полное локальное git-репо в последней производственной ветви и готовый к работе пакет gzipped, который теперь должен представлять, что та же версия
  • Программное обеспечение на устройстве содержится в каталоге с именем /opt/example/current, который является символической ссылкой на текущую версию программного обеспечения.
  • Функция автоматического обновления на устройстве, которое при загрузке:
    1. Проверяет наличие do_not_updateфайла и не предпринимает никаких дальнейших действий, если он существует (для устройств dev см. Ниже).
    2. Считывает текущий хеш коммита из вышеупомянутого текстового файла.
    3. Делает HTTP-запрос к серверу с этим хэшем в качестве параметра запроса. Сервер либо ответит 304 (хеш - текущая версия), либо предоставит пакет обновления gzipped.
    4. Устанавливает пакет обновления, если он был получен, в /opt/example:
      1. Извлечение обновленной информации о программном обеспечении в папку с именем stage.
      2. Запуск постинсталляционного скрипта из пакета обновлений, который делает такие вещи, как внесение необходимых локальных изменений для этого обновления и т. Д.
      3. Копирование текущей корневой папки программного обеспечения в previous( previousсначала удаляет существующую , если она есть).
      4. Копирование stageпапки в latest( latestсначала удаляет существующую , если она есть).
      5. Обеспечение currentсимволической ссылки для указания latest.
      6. Перезагрузка устройства (обновления прошивки, если они есть, применяются при перезагрузке).

Существует также проблема первоначального развертывания на вновь созданных устройствах. Устройства в настоящее время основаны на SD-картах (здесь есть свои проблемы, выходящие за рамки), поэтому этот процесс состоит из:

  1. Существует образ SD, на котором есть стабильная более ранняя версия программного обеспечения.
  2. SD-карта создается из этого изображения.
  3. При первой загрузке происходит различная первоначальная инициализация, специфичная для устройства (на основе серийного номера), а затем автообновление захватывает и устанавливает последнюю производственную версию программного обеспечения, как обычно.

Кроме того, мне нужна была поддержка устройств разработки. Для девелоперских устройств:

  • На устройстве поддерживается полный локальный репозиторий git.
  • currentСимволическая указывает на каталог развития.
  • Существует локальный do_not_updateфайл, который предотвращает автоматическое обновление кода разработки с производственным обновлением.

Теперь процесс развертывания теоретически должен быть следующим:

  1. Как только код будет готов к развертыванию, отправьте его в ветку релиза.
  2. Нажмите кнопку «развернуть обновление» на сервере.
  3. Обновление активировано, и устройства будут автоматически обновляться при следующей проверке.

Тем не менее, есть тонны проблем на практике:

  • Код веб-сервера находится в том же репо, что и код устройства, и на сервере есть локальное git-репо, из которого я выполняю. Последний код веб-сервера не находится в той же ветви, что и последний код устройства. Структура каталогов проблематична. Когда кнопка «развернуть обновление» извлекает последнюю версию из производственной ветви, она перетаскивает ее в подкаталог кода сервера. Это означает, что при развертывании на сервере с нуля я должен вручную «заполнить» этот подкаталог, вставив в него производственную ветвь устройства, потому что, вероятно, из-за ошибки пользователя git, если я не попытаюсь выполнить развертывание, извлеките код устройства из ветви веб-сервера родительского каталога . Я думаю, что это решаемо, если сделать промежуточную область не подкаталогом локального git-репо сервера.
  • В настоящее время веб-сервер не поддерживает git-хэш программного обеспечения устройства. При запуске сервера он выполняет git rev-parse HEADв своем локальном хранилище программного обеспечения устройства для получения текущего хэша. По причинам, которые я не могу обернуть вокруг себя, это также вызывает массу логических ошибок, которые я не буду здесь описывать. Достаточно сказать, что иногда перезапуск сервера портит работу, особенно если сервер новый и не работает филиал репо уже вытащен. Я бы с радостью поделился источником этой логики, если бы запросил, но этот пост становится длинным.
  • Если сценарий дезинфекции (на стороне сервера) по какой-то причине дает сбой, то на сервере остается обновленное хранилище, но с несинхронизированным / отсутствующим пакетом обновлений, таким образом, git rev-parse HEADбудет возвращен хеш, который не соответствует тому, что на самом деле происходит. обслуживаются устройства, и проблемы здесь должны быть исправлены вручную в командной строке сервера. Т.е. сервер не знает, что пакет обновлений не верен, он просто всегда так полагает. Это в сочетании с предыдущими пунктами делает сервер чрезвычайно хрупким на практике.
  • Одна из самых больших проблем : в настоящее время на устройстве не работает отдельный демон обновления. Из-за сложностей, ожидающих появления беспроводного доступа в Интернет и некоторых хакерских атак в последнюю минуту, именно основное программное обеспечение управления устройством само проверяет и обновляет устройство. Это означает, что если каким-то образом плохо протестированная версия попадает в производство, а управляющее программное обеспечение не может запуститься, все существующие устройства по существу блокируются, так как они больше не могут обновляться. Это был бы абсолютный кошмар в производстве. То же самое для одного устройства, если оно теряет мощность в неудачное время.
  • Другая важная проблема : нет поддержки добавочных обновлений. Если устройство, скажем, какое-то время не включается, то при следующем обновлении оно пропускает несколько выпусков версий, оно должно иметь возможность выполнить прямое обновление с пропуском версий. Следствием этого является обновление развертывания - это кошмар, когда нужно убедиться, что любое данное обновление может быть применено поверх любой предыдущей версии. Кроме того, поскольку git-хэши используются для идентификации версий, а не номеров версий, лексикографическое сравнение версий для облегчения постепенных обновлений в настоящее время невозможно.
  • Новое требование, которое я в настоящее время не поддерживаю, состоит в том, что будут существовать некоторые параметры конфигурации для каждого устройства (пары ключ / значение), которые должны быть настроены на стороне административного сервера. Я не возражаю против того, чтобы эти параметры для каждого устройства отправлялись обратно на устройство в том же HTTP-запросе, что и обновление программного обеспечения (возможно, я мог бы инкапсулировать его в HTTP-заголовки / файлы cookie), хотя я не слишком обеспокоен этим, поскольку могу всегда делайте это отдельным HTTP-запросом.
  • Есть небольшое осложнение из-за того, что существуют две (и более в будущем) версии аппаратного обеспечения. Текущая версия аппаратного обеспечения фактически сохраняется как переменная среды в ее исходном образе SD (они не могут самоидентифицироваться), и все программное обеспечение совместимо со всеми версиями устройств. Обновления микропрограммного обеспечения выбираются на основе этой переменной среды, а пакет обновления содержит микропрограммное обеспечение для всех версий аппаратного обеспечения. Я могу жить с этим, хотя это немного неуклюже.
  • В настоящее время не существует способа вручную загрузить обновление на устройство (короче говоря, эти устройства имеют два адаптера Wi-Fi, один для подключения к Интернету и один в режиме AP, который пользователь использует для настройки устройства; в будущем Я намерен добавить функцию «обновить программное обеспечение» в локальный веб-интерфейс устройства). Это не так уж важно, но влияет на способ установки обновлений.
  • Куча других разочарований и общей небезопасности.

Итак ... это было долго. Но мой вопрос сводится к следующему:

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

Я надеюсь, что вопрос ясен. Я понимаю, что это немного нечетко, но я на 100% уверен, что это проблема, которая была решена ранее и успешно решена, я просто не знаю, каковы в настоящее время принятые стратегии.

Джейсон С
источник
2
Поскольку ваши устройства основаны на Linux, я бы порекомендовал вам использовать один из существующих менеджеров пакетов Linux (apt, yum, rpm и т. Д.). В качестве первого шага проверьте, не входит ли ваша базовая установка Linux.
Барт ван Инген Шенау
Не могли бы вы дать представление о размерах системы? мы говорим о МБ или ГБ?
Ибенини
MB; Пакеты обновлений, как правило, имеют размер 1-2 МБ.
Джейсон С,

Ответы:

1

Не могли бы вы предоставить больше информации о дистрибутиве Linux, загрузчике и архитектуре (x86, ARM, MIPS?), Которые используются?

Я постараюсь угадать в любом случае и, надеюсь, направить вас в правильном направлении.

Если это дистрибутив на основе Yocto с U-Boot, я бы порекомендовал взглянуть на mender.io или swupdate . Эти проекты, похоже, хорошо соответствуют критериям. Их основная цель - обеспечить атомарные обновления.

Одна из самых больших проблем: в настоящее время на устройстве не работает отдельный демон обновления. Из-за сложностей в ожидании доступа к интернету через Wi-Fi и некоторых хакерских атак в последнюю очередь, это основное программное обеспечение для управления устройством, которое проверяет и обновляет устройство. Это означает, что если каким-то образом плохо протестированная версия попадает в производство, а управляющее программное обеспечение не может запуститься, все существующие устройства по существу блокируются, так как они больше не могут обновляться. Это был бы абсолютный кошмар в производстве. То же самое для одного устройства, если оно теряет мощность в неудачное время.

Mender предоставляет набор инструментов, включая демон (и набор сценариев systemd), написанный на Go, который снимет эту нагрузку с ваших плеч. Этот проект довольно прост в использовании с Yocto (они предоставляют мета-слой для многих устройств, которые должны быть легко адаптированы к вашему конкретному случаю и разметке разделов. У них также есть много готовых решений для популярных SOC) , Если вы не используете Yocto, вы можете заглянуть в этот пост, в котором объясняются, какие именно шаги вам нужно предпринять, чтобы использовать его с дистрибутивами не на основе Yocto.

swupdate также довольно крутой, но, похоже, это работа одного человека от парня из DENX (организации, стоящей за U-Boot). Это кажется довольно зрелым.

Есть также Ubuntu Snappy, с которым у меня нет никакого опыта, и я не могу компетентно прокомментировать это (может быть, кто-то добавит). Идея состоит в том, чтобы доставлять ваши приложения в виде «защелок». Из того, что я понял, это едва ли решение вашей проблемы, хотя и не в масштабе всей системы.

Я надеюсь, что вопрос ясен. Я понимаю, что это немного нечетко, но я на 100% уверен, что это проблема, которая была решена ранее и успешно решена, я просто не знаю, каковы текущие принятые стратегии.

На самом деле, похоже, что сегодня существует тенденция использовать Docker (даже во встроенных системах) и друзей через APT / YUM. Последнее может очень затруднить обеспечение последовательности.

staroselskii
источник
Это здорово, я не уверен, как я не заметил этот ответ. Скоро я предоставлю запрашиваемую информацию, этот проект был временно отложен и возобновлен вчера, так что я просто возвращаюсь на лошади, и этот конкретный вопрос сейчас является приоритетом № 1.
Джейсон С,