У меня есть проект среднего размера, который как раз близок к завершению фазы «неаккуратных кофеиновых прототипов для демонстраций клиентов» и переходит в фазу «думать о будущем». Проект состоит из устройств на базе Linux с программным обеспечением и встроенным программным обеспечением, а также центрального административного веб-сервера. В настоящее время существует 10 прототипов, производство которых ожидается на уровне порядка 1000-х.
Не будучи достаточно сведущим в искусстве автоматического обновления и не имея достаточного времени, я быстро разработал собственную стратегию развертывания / автоматического обновления программного обеспечения и, честно говоря, это отстой. В настоящее время он состоит из следующего:
- Хостинг-репозиторий Git (GitLab) с веткой производственного выпуска (обратите внимание, что источник веб-сервера также находится в этом же репо, как и некоторые другие вещи).
- Кнопка «развернуть обновление» в веб-интерфейсе, которая:
- Извлекает последнюю версию из ветки производственного выпуска в локальную область репозитория, а также копирует ее во временную область подготовки пакета.
- Запускает сценарий санации (хранящийся в репозитории) в промежуточной области для удаления несвязанных исходных файлов (например, источника сервера, источника встроенного ПО и т. Д.) И файлов .git.
- Записывает текущий git-хеш в файл в пакете обновления (цель станет ясна ниже).
- Если все прошло хорошо, он распаковывает его и готовит его для обслуживания, перезаписывая предыдущий пакет gzip файлом с тем же именем, а затем удаляет промежуточную область.
- Обратите внимание, что теперь на сервере имеется две копии текущего программного обеспечения устройства, которые, как ожидается, будут синхронизированы: полное локальное git-репо в последней производственной ветви и готовый к работе пакет gzipped, который теперь должен представлять, что та же версия
- Программное обеспечение на устройстве содержится в каталоге с именем
/opt/example/current
, который является символической ссылкой на текущую версию программного обеспечения. - Функция автоматического обновления на устройстве, которое при загрузке:
- Проверяет наличие
do_not_update
файла и не предпринимает никаких дальнейших действий, если он существует (для устройств dev см. Ниже). - Считывает текущий хеш коммита из вышеупомянутого текстового файла.
- Делает HTTP-запрос к серверу с этим хэшем в качестве параметра запроса. Сервер либо ответит 304 (хеш - текущая версия), либо предоставит пакет обновления gzipped.
- Устанавливает пакет обновления, если он был получен, в
/opt/example
:- Извлечение обновленной информации о программном обеспечении в папку с именем
stage
. - Запуск постинсталляционного скрипта из пакета обновлений, который делает такие вещи, как внесение необходимых локальных изменений для этого обновления и т. Д.
- Копирование текущей корневой папки программного обеспечения в
previous
(previous
сначала удаляет существующую , если она есть). - Копирование
stage
папки вlatest
(latest
сначала удаляет существующую , если она есть). - Обеспечение
current
символической ссылки для указанияlatest
. - Перезагрузка устройства (обновления прошивки, если они есть, применяются при перезагрузке).
- Извлечение обновленной информации о программном обеспечении в папку с именем
- Проверяет наличие
Существует также проблема первоначального развертывания на вновь созданных устройствах. Устройства в настоящее время основаны на SD-картах (здесь есть свои проблемы, выходящие за рамки), поэтому этот процесс состоит из:
- Существует образ SD, на котором есть стабильная более ранняя версия программного обеспечения.
- SD-карта создается из этого изображения.
- При первой загрузке происходит различная первоначальная инициализация, специфичная для устройства (на основе серийного номера), а затем автообновление захватывает и устанавливает последнюю производственную версию программного обеспечения, как обычно.
Кроме того, мне нужна была поддержка устройств разработки. Для девелоперских устройств:
- На устройстве поддерживается полный локальный репозиторий git.
current
Символическая указывает на каталог развития.- Существует локальный
do_not_update
файл, который предотвращает автоматическое обновление кода разработки с производственным обновлением.
Теперь процесс развертывания теоретически должен быть следующим:
- Как только код будет готов к развертыванию, отправьте его в ветку релиза.
- Нажмите кнопку «развернуть обновление» на сервере.
- Обновление активировано, и устройства будут автоматически обновляться при следующей проверке.
Тем не менее, есть тонны проблем на практике:
- Код веб-сервера находится в том же репо, что и код устройства, и на сервере есть локальное git-репо, из которого я выполняю. Последний код веб-сервера не находится в той же ветви, что и последний код устройства. Структура каталогов проблематична. Когда кнопка «развернуть обновление» извлекает последнюю версию из производственной ветви, она перетаскивает ее в подкаталог кода сервера. Это означает, что при развертывании на сервере с нуля я должен вручную «заполнить» этот подкаталог, вставив в него производственную ветвь устройства, потому что, вероятно, из-за ошибки пользователя git, если я не попытаюсь выполнить развертывание, извлеките код устройства из ветви веб-сервера родительского каталога . Я думаю, что это решаемо, если сделать промежуточную область не подкаталогом локального git-репо сервера.
- В настоящее время веб-сервер не поддерживает git-хэш программного обеспечения устройства. При запуске сервера он выполняет
git rev-parse HEAD
в своем локальном хранилище программного обеспечения устройства для получения текущего хэша. По причинам, которые я не могу обернуть вокруг себя, это также вызывает массу логических ошибок, которые я не буду здесь описывать. Достаточно сказать, что иногда перезапуск сервера портит работу, особенно если сервер новый и не работает филиал репо уже вытащен. Я бы с радостью поделился источником этой логики, если бы запросил, но этот пост становится длинным. - Если сценарий дезинфекции (на стороне сервера) по какой-то причине дает сбой, то на сервере остается обновленное хранилище, но с несинхронизированным / отсутствующим пакетом обновлений, таким образом,
git rev-parse HEAD
будет возвращен хеш, который не соответствует тому, что на самом деле происходит. обслуживаются устройства, и проблемы здесь должны быть исправлены вручную в командной строке сервера. Т.е. сервер не знает, что пакет обновлений не верен, он просто всегда так полагает. Это в сочетании с предыдущими пунктами делает сервер чрезвычайно хрупким на практике. - Одна из самых больших проблем : в настоящее время на устройстве не работает отдельный демон обновления. Из-за сложностей, ожидающих появления беспроводного доступа в Интернет и некоторых хакерских атак в последнюю минуту, именно основное программное обеспечение управления устройством само проверяет и обновляет устройство. Это означает, что если каким-то образом плохо протестированная версия попадает в производство, а управляющее программное обеспечение не может запуститься, все существующие устройства по существу блокируются, так как они больше не могут обновляться. Это был бы абсолютный кошмар в производстве. То же самое для одного устройства, если оно теряет мощность в неудачное время.
- Другая важная проблема : нет поддержки добавочных обновлений. Если устройство, скажем, какое-то время не включается, то при следующем обновлении оно пропускает несколько выпусков версий, оно должно иметь возможность выполнить прямое обновление с пропуском версий. Следствием этого является обновление развертывания - это кошмар, когда нужно убедиться, что любое данное обновление может быть применено поверх любой предыдущей версии. Кроме того, поскольку git-хэши используются для идентификации версий, а не номеров версий, лексикографическое сравнение версий для облегчения постепенных обновлений в настоящее время невозможно.
- Новое требование, которое я в настоящее время не поддерживаю, состоит в том, что будут существовать некоторые параметры конфигурации для каждого устройства (пары ключ / значение), которые должны быть настроены на стороне административного сервера. Я не возражаю против того, чтобы эти параметры для каждого устройства отправлялись обратно на устройство в том же HTTP-запросе, что и обновление программного обеспечения (возможно, я мог бы инкапсулировать его в HTTP-заголовки / файлы cookie), хотя я не слишком обеспокоен этим, поскольку могу всегда делайте это отдельным HTTP-запросом.
- Есть небольшое осложнение из-за того, что существуют две (и более в будущем) версии аппаратного обеспечения. Текущая версия аппаратного обеспечения фактически сохраняется как переменная среды в ее исходном образе SD (они не могут самоидентифицироваться), и все программное обеспечение совместимо со всеми версиями устройств. Обновления микропрограммного обеспечения выбираются на основе этой переменной среды, а пакет обновления содержит микропрограммное обеспечение для всех версий аппаратного обеспечения. Я могу жить с этим, хотя это немного неуклюже.
- В настоящее время не существует способа вручную загрузить обновление на устройство (короче говоря, эти устройства имеют два адаптера Wi-Fi, один для подключения к Интернету и один в режиме AP, который пользователь использует для настройки устройства; в будущем Я намерен добавить функцию «обновить программное обеспечение» в локальный веб-интерфейс устройства). Это не так уж важно, но влияет на способ установки обновлений.
- Куча других разочарований и общей небезопасности.
Итак ... это было долго. Но мой вопрос сводится к следующему:
Как мне сделать это правильно и безопасно? Могу ли я внести небольшие изменения в свой существующий процесс? Есть ли проверенная временем стратегия / существующая система, которую я могу использовать, чтобы мне не пришлось накатывать свою собственную дрянную систему обновлений ? Или, если мне все же придется свернуть свою собственную, что должно быть правдой, чтобы процесс развертывания / обновления был безопасным и успешным? Я также должен иметь возможность включать устройства разработки в смесь.
Я надеюсь, что вопрос ясен. Я понимаю, что это немного нечетко, но я на 100% уверен, что это проблема, которая была решена ранее и успешно решена, я просто не знаю, каковы в настоящее время принятые стратегии.
источник
Ответы:
Не могли бы вы предоставить больше информации о дистрибутиве Linux, загрузчике и архитектуре (x86, ARM, MIPS?), Которые используются?
Я постараюсь угадать в любом случае и, надеюсь, направить вас в правильном направлении.
Если это дистрибутив на основе Yocto с U-Boot, я бы порекомендовал взглянуть на mender.io или swupdate . Эти проекты, похоже, хорошо соответствуют критериям. Их основная цель - обеспечить атомарные обновления.
Mender предоставляет набор инструментов, включая демон (и набор сценариев systemd), написанный на Go, который снимет эту нагрузку с ваших плеч. Этот проект довольно прост в использовании с Yocto (они предоставляют мета-слой для многих устройств, которые должны быть легко адаптированы к вашему конкретному случаю и разметке разделов. У них также есть много готовых решений для популярных SOC) , Если вы не используете Yocto, вы можете заглянуть в этот пост, в котором объясняются, какие именно шаги вам нужно предпринять, чтобы использовать его с дистрибутивами не на основе Yocto.
swupdate также довольно крутой, но, похоже, это работа одного человека от парня из DENX (организации, стоящей за U-Boot). Это кажется довольно зрелым.
Есть также Ubuntu Snappy, с которым у меня нет никакого опыта, и я не могу компетентно прокомментировать это (может быть, кто-то добавит). Идея состоит в том, чтобы доставлять ваши приложения в виде «защелок». Из того, что я понял, это едва ли решение вашей проблемы, хотя и не в масштабе всей системы.
На самом деле, похоже, что сегодня существует тенденция использовать Docker (даже во встроенных системах) и друзей через APT / YUM. Последнее может очень затруднить обеспечение последовательности.
источник