Москва

+7 495 741-65-70

Корзина пуста
Загрузка списка товаров из файла
SALE!-10% -20% -30% -40% -50% -60%
дефицитные компоненты
DevOps для встроенных систем (Часть 1)

DevOps для встроенных систем (Часть 1)

Вступление

Конечно, встроенные системы - это обширная область (как и DevOps), поэтому я начну с примитивного проекта, который просто создает код для небольшого микроконтроллера, а затем покажу как нужно действовать на примере небольшого проекта со встроенным Linux и Yocto. В каждом из этих случаев будут существенные различия в подходе и стратегиях, хотя сами инструменты практически одинаковы. При использовании DevOps вы можете достичь одного и того же результата, используя различные методологии и архитектуры, но в конечном итоге важно найти наиболее простое решение, которое и можно будет использовать повторно.

Эта статья не предназначена для разработчиков, уже использующих  DevOps, так как они уже знакомы с описанными здесь основными инструментами и архитектурой конвейеров CI/CD. Возможно, что статья будет интересна для тех специалистов DevOps, которые приходят в разработку встраиваемых систем из Web/Cloud/DB, так как описывает проблемы, с которыми сталкиваются инженеры по встраиваемым системам и инструменты, которые они используют.

Примечание. В качестве основной операционной системы в статье используется Ubuntu 18.04.3 LTS, и все приведенные в статье команды ориентируются на эту ОС, но они должны быть аналогичны для любой другой системы.

Что такое DevOps?

На самом деле, существует много споров вокруг DevOps и того, чем она является на самом деле. Я попробую описать этот термин своими словами. Слово является аббревиатурой и означает «Разработка/эксплуатация» и согласно википедии: «DevOps (акроним от англ. development и operations; по-русски обычно произносится как «дево?пс») — технология (методология) активного взаимодействия специалистов по разработке со специалистами по информационно-технологическому обслуживанию и взаимной интеграции их рабочих процессов друг в друга для обеспечения качества продукта. Предназначена для эффективной организации создания и обновления программных продуктов и услуг. Основана на идее тесной взаимозависимости разработки и эксплуатации программного обеспечения».

Другими словами, это все об автоматизации. Автоматизировать все. Автоматизируйте все этапы разработки (+ развертывание/доставка) и каждую деталь процедуры. В идеале, успешная архитектура DevOps позволяет достичь следующего результата: если здание сгорело дотла, вы просто покупаете новое оборудование, и ваша инфраструктура сама разворачивается в течение пары часов или нескольких минут. Автоматизация, в идеале, может начинаться с карт доступа и заканчиваться вашим готовым продуктом, который попадает в руки клиентов. Есть сторонники того, что и кофемашины в здании должны быть включены в процесс автоматизации, в принципе, если она имеет процессор и порт отладки, то и это выполнимо.

DevOps_1.png (11 KB)

Смысл в том, что DevOps предлагает простые и масштабируемые решения проблем, которые возникают, когда вы пытаетесь полностью автоматизировать свою инфраструктуру.

Почему DevOps?

Должно быть очевидно, что автоматизация всего не только экономит ваше время (например, на восстановление вашей инфраструктуры в случае, если она "умрет" в результате несчастного случая), но, если все сделано правильно, она позволяет упростить отслеживание проблемных изменений и быстро их восстанавливать или исправлять, что сводит к минимуму сбои и трудно обнаруживаемые проблемы.

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

Чем DevOps не является

DevOps это не набор инструментов! В DevOps так много инструментов, сред, платформ, языков, аббревиатур и сокращений, что вы точно потеряетесь. И что еще хуже, новые вещи выходят почти каждую неделю. Конечно, пытаться интегрировать все новые тенденции в вашу инфраструктуру автоматизации не имеет смысла, и это не должно быть вашей целью. Каждый из этих инструментов имеет свое применение, и вам нужно точно знать, что вам нужно. Если у вас есть время для исследований, то изучайте новое, но не заставляйте себя использовать что-то, потому что это модно и современно.

В DevOps и автоматизации в целом вы можете достичь одного и того же результата с помощью множества различных способов и инструментов. Нет единственного или «правильного» способа что-то сделать. Вам нужно хорошо понять свою проблему, знать хотя бы несколько доступных инструментов и выбрать самое простое решение. Всегда выбирайте самые простые решения. Это будет проще для понимания команды разработки, легче в тестировании, поиске неисправностей и отладке.

Так что забудьте о том, что вы должны использовать Kubernetes или Jenkins, или что-то еще, потому что другие делают. Вам нужно найти простейшее решение именно вашей проблемы.

DevOps и встроенное ПО

Обычно DevOps встречаются в облачных сервисах. Это означает, что большинство веб-серверов и баз данных в Интернете поддерживают практику DevOps. Все должно быть автоматизировано, и на самом деле есть специальная ветка DevOps, называемая Site Reliability Engineering (SRE ), которая занимается укреплением инфраструктуры вебсервисов, так, чтобы они могли выдержать даже катастрофические глобальные события.

Но что насчет встраиваемых решений? Как использовать DevOps в вашем рабочем процессе и инфраструктуре. Подумайте, как бы вы автоматизировали все, что вы делаете, так что, если завтра вы потеряете все (кроме кода, конечно), вы смогли вернуться к разработке и обслуживанию клиентов через несколько минут или часов. Например, все ваши разработчики должны получить новые ноутбуки и начать работу через несколько минут, и все инструменты, SDK, tool chain имеют одинаковые версии, а среда разработки одинакова для всех. Кроме того, ваши серверы сборки снова должны быть подключены, и вы начинаете непрерывную интеграцию и непрерывную доставку (CI/CD) в считанные минуты.

Звучит круто, верно?

Что ж, это, конечно, выполнимо, но нужно сначала сделать много небольших шагов, и, самое важное, понять, какие проблемы нужно решить, куда двигаться, каковы ваши возможности по времени, затратам и ресурсам, и как разработать решение простым и надежным способом. Нужно так же учесть, что во встраиваемом ПО продуктом является прошивка для контроллера, процессора или ПЛИС, которую нужно собрать, загрузить в целевую плату для запуска автоматических тестов и проверки правильности работы.

Как начать?

Итак, вам теперь должно быть совершенно ясно, что такое DevOps, и как он может помочь вашему проекту, и если вам интересно, то вы, вероятно, задаетесь вопросом, с чего начать?

Прежде чем углубляться в подробности, давайте посмотрим на очень простой проект, в рамках которого котором вам нужно разработать прошивку для небольшого MCU (например, STM32). В этом случае вам нужно написать код, скомпилировать его, загрузить в целевую плату, протестировать и если все работает как ожидается, слить изменения в ветку релиза. Хотя это очень простой проект, существует множество различных способов автоматизации всего конвейера, и некоторые из них могут быть лучше, чем другие, в зависимости от ситуации. Важно понимать, что каждый случай индивидуален, и задача состоит в том, чтобы найти самое простое и оптимальное решение для вашей конкретной проблемы и не перегружать ваше решение.

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

В большинстве сценариев, когда вам потребуется внедрить DevOps, вам придется столкнуться со следующими вопросами:

  • Общая среда разработки
  • Непрерывная интеграция
    • Использование распределенных систем контроля версий (например, git)
    • Автоматизированные сборки
    • Автоматизированные тесты
      • Тестирование системы
      • Пользовательское приемочное тестирование (UAT)
      • Функциональное тестирование
    • Развертывание

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

Наконец, это не исчерпывающий список вопросов, но это основной скелет для большинства проектов. Давайте теперь посмотрим на некоторые из вышеперечисленных пунктов.

Общая среда разработки (CDE)

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

Итак, что такое CDE (Common Development Enviroment)? Предполагалось, что мы собираемся разработать прошивку для STM32 MCU. В этом случае CDE - это все необходимое для компиляции вашего кода, включая конкретную версию ОС, системные библиотеки, набор инструментов, флаги компилятора и компоновщика и способ сборки прошивки. В данном случае не важно, какую IDE вы используете, поскольку разработчики могут использовать свой любимый редактор для написания кода (включая свои любимые плагины). Тем не менее, важно, чтобы каждая из этих IDE не загрязняла среду проекта ненужными пользовательскими настройками и файлами, поэтому и здесь необходимы меры предосторожности (хотя git легко позволяет бороться с этой проблемой).

Почему использование CDE важно? Вот реальный пример проекта, в котором разработчики отказались интегрировать CDE в свой рабочий процесс. Задачей было создание довольно сложного дистрибутива Yocto. Разработчики использовали свою собственную рабочую среду для создания дистрибутива Linux, что в итоге привело к хаотической ситуации, когда образ создавался не на всех рабочих станциях одинаково, а на некоторых рабочих станциях он даже не создавался. Как вы можете себе представить, процесс разработки стал сложным и хаотичным и требовалось слишком много времени для решения проблем, а не на собственно разработку. Наличие CDE гарантирует, что ваш проект будет создаваться одинаково везде, где он выполняется. Это единственная причина, которой на самом деле должно быть достаточно для каждого, чтобы рассмотреть возможность использования CDE в рабочем процессе разработки.

Так как вы можете это сделать? Ну, есть несколько способов, но важно иметь возможность автоматизировать воспроизведение CDE с версионированием. Обычно это делается с помощью виртуальной машины (или образа виртуальной ОС), которая включает все необходимые инструменты. Существует довольно много решений для виртуализации, таких как VMWare, VirtualBox, Docker, облачные VPS (Virtual Private Server), такие как AWS, Azure и т. д. Некоторые из них бесплатны, для использования других нужно купить лицензию. Какой из них лучше для вас, вам придется решить самостоятельно. Возможно, если у вас нет опыта, вам потребуется купить поддержку даже для бесплатного продукта, просто чтобы не рисковать.

В нашем примере проекта CDE будет реализована в виде виртуальной машины (или виртуальной ОС), которая включает в себя все необходимые инструменты и может быть установлена на компьютеры разработчиков, допуская даже использование разных ОС в качестве базовой системы на рабочей станции. Видите? Это очень полезно, потому что CDE не зависит от ОС.

Поскольку существует много технологий виртуализации, вам необходимо сначала решить, какая из них соответствует вашим потребностям. VMWare и VirtualBox являются решениями для виртуальных машин, а это означает, что они работают поверх вашей операционной системы. Это машина с песочницей, которая использует общие ресурсы вашего оборудования, поэтому вы можете назначить виртуальным машинам несколько ЦП, ОЗУ и хранилище из вашей хост-системы. Конечно, производительность такой системы не слишком велика, и вам может потребоваться больше ресурсов по сравнению с другими решениями.

Другое решение, такое как Docker с его контейнерами, использует все ваши аппаратные ресурсы без промежуточного слоя виртуальной машины. Он по-прежнему изолирован (но не на 100%), и, поскольку он находится в контейнере, он также может быть передан и использован повторно. Это решение имеет лучшую производительность по сравнению с виртуальными машинами, но реализация его технологии отличается в зависимости от ОС хост-машины; в случае хоста Linux, Docker работает отлично, а в случае хоста Windows он фактически реализован как виртуальная машина.

Кроме того, существуют такие решения, как работа с удаленными VPS, которые могут располагаться рядом с вами или в облаке. Эти серверы уже содержат CDE, который вы используете для сборки своего кода, поэтому вы редактируете свой код локально на вашей хост-ОС с помощью вашей любимой IDE, а затем собираете код удаленно на VPS. Преимущество этого решения заключается в том, что VPS может быть быстрее, чем ваш ноутбук/рабочая станция, и может использоваться другими разработчиками. Недостатком является то, что вам нужно сетевое соединение, и это может стать проблемой, если произведенные изменения, которые нужно протестировать, имеют большой размер, а сетевое соединение не быстрое.

Однако важно то, что любое решение, которое вы выберете, должно применять концепцию IaC (инфраструктура как код), что означает, что инфраструктура будет управляться с помощью скриптов, кода или файлов конфигурации.

Теперь давайте рассмотрим наш конкретный случай. Поскольку наша цель заключается в создании встроенной прошивки для STM32, очевидно, что нам не нужно много ресурсов и мощного компоновщика. Даже если в проекте будет много кода, сборка может занять 1-2 минуты, что вполне приемлемо. Если объем кода не такой большой, то сборка будет занимать всего несколько секунд. Конечно, если сборка выполняется на хосте, который может выполнять параллельную сборку и одновременно компилировать много исходных файлов, сборка также будет выполняться быстрее. Какое решение в этом случае является предпочтительным?

На самом деле, все решения, которые были упомянуты ранее, подходят для этого случая. Вы можете использовать VirtualBox и Docker (которые бесплатны) или использовать VPS, такой как AWS, который не является бесплатным, но стоит недорого (особенно для компании или организации), а также экономит ваше время, деньги и силы, не заставляя вас поддерживать инфраструктуру 24/7, поскольку вы платите только за время работы вашего образа.

Поскольку все эти решения кажутся подходящими, возникает другой вопрос. Как оценить все это и принять решение? И, если возникнет необходимость перехода с одного решения на другое, как не потратить слишком много времени и денег? Для этих целей есть специальные инструменты, такие как Packer или Vagrant, позволяющие упростить процесс создания разных типов образов с одинаковой конфигурацией. Хотя эти два инструмента кажутся похожими, на самом деле они немного отличаются, и стоит изучить их документацию перед использованием. Вы можете выбрать и то, и другое для создания CDE, но Packer лучше подходит для создания образов (которые можно использовать также из Vagrant), и он позволяет все сделать с помощью одного файла конфигурации, который создает образы для разных поставщиков одновременно. (Поставщиками в этом контексте являются Docker, Vagrant, AWS и т. д.) Кроме того, оба инструмента поддерживают приложения конфигурации, которые позволяют настраивать образ.

Существует множество приложений для конфигурации, таких как Puppet, Chef, Ansible. Кроме того, вы можете использовать shell скрипты для этих целей. Возможно, вам покажется, что проще всего использовать для конфигурации shell, на самом деле это не так. В этой статье я буду использовать Ansible .

Почему Ansible? Потому, что он более гибкий, облегчает работу в более сложных случаях и поставляется с кучей готовых к использованию примеров, модулей и ролей (роли - это просто предопределенные и протестированные сценарии для популярных пакетов). Это существенно облегчает работу по сравнению с использованием shell скриптов. Если сравнивать с Puppet и Chef, Ansible «лучше», потому что он позволяет подключаться через SSH к целевому компьютеру и настраивать его удаленно.

Одна из проблем использования shell скриптов заключается в том, что их сложность катастрофически нарастает, если вы хотите предусмотреть все случаи. Например, давайте предположим, что вы хотите создать каталог в целевом образе, для чего используете команду mkdir. Но если этот каталог уже существует, скрипт завершится ошибкой, если вы забудете предварительно проверить, существовал ли каталог ранее. Ansible делает именно эту работу, сначала проверяет необходимость изменения, и применяет их только в том случае, если они еще не сделаны.

Предположим, мы решили создать образ Docker и AWS EC2 AMI , которые имеют почти одинаковую конфигурацию. В этом случае Packer кажется наиболее подходящим решением. Вы также можете использовать Vagrant, но давайте пока остановимся на Packer. В любом случае вам потребуется учетная запись Docker и AWS, чтобы выполнить примеры, поскольку вам нужно будет отправить эти образы на удаленный сервер. Если вы не планируете использовать облачные сервисы, вы все равно можете использовать Packer для создания образа Docker, который можно будет отправить в локальный репозиторий на вашем компьютере или в сети, но в этом случае, возможно, имеет смысл использовать Vagrant, как более удобный способ создания, запуска и использования образа, без необходимости иметь дело с интерфейсом Docker.

Я не буду вдаваться в подробности создания учетной записи AWS. Вы можете найти эту информацию в документации AWS EC2. Будем предполагать, что у вас уже есть как минимум бесплатная учетная запись AWS и необходимые для доступа ключи.

В этой статье мы будем использовать два репозитория:

В первом из них вы найдете файлы, которые Packer будет использовать для создания образа, а во втором репозитории - всего лишь небольшой пример шаблона кода для STM32, который я использую каждый раз, когда начинаю новый проект для STM32F103.

Прежде чем продолжить подготовку образа, давайте посмотрим на среду, которая нам будет нужна внутри него. Как минимум, нам потребуется CMake и toolchain ARM GCC. Следовательно, образ должен включать их, и мы подготовим этот образ, используя Ansible во время сборки образа. Опять же, я не буду вдаваться в подробности того, как использовать Ansible, вы можете найти подробную информацию в онлайн-документации.

Установка Packer

Чтобы установить Packer, загрузите предварительно скомпилированный бинарный файл для вашей операционной системы. Затем скопируйте полученный файл в /usr/bin (используя sudo), или в папку ~/.local/bin, если вы единственный пользователь, который будет использовать Packer.

Чтобы проверить, все ли в порядке, выполните команду:

packer -v

которая должна вернуть текущую версию программы.

Установка Ansible

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


lisudo apt-add-repository ppa:ansible/ansible
sudo apt-get update
sudo apt-get install ansible -y

 Это официальный репозиторий ppa, так что можете не волноваться. Если вы никому не доверяете, вы можете использовать и другие способы установки (вы также можете просто использовать git repo).

Чтобы проверить, что все в порядке, запустите:


ansible --version

В моем случае версия 2.9.1. Имейте в виду, что, поскольку эти инструменты находятся в постоянной разработке, они регулярно обновляются, и вам всегда нужно использовать документацию, соответствующую вашей версии.

Регистрация в Docker и создание хранилища

В этом примере мы используем docker хаб, создадим репозиторий для полученного образа и сохраним его там. Это даст доступ к образу из любой точки мира, и любой узел докера будет иметь к нему доступ. Конечно, если вы не хотите публиковать свой образ, вы можете просто создать локальный репозиторий на рабочей станции или в локальной сети, в котором будут храниться образы докеров.

В этой статье я буду использовать докер-хаб. Вам нужно создать учетную запись и репозиторий с именем stm32-cde-image. В этом случае ваш образ будет сохраняться в хранилище под именем <username>/stm32-cde-image.

Теперь, когда у вас есть репозиторий, вам нужно перейти к настройкам учетной записи пользователя и создать токен, чтобы можно было отправлять файлы в ваши репозитории. Для этого вам нужно пройти по пути Account Settings -> Security -> Access Tokens и, следуя приведенным там инструкциям, создать токен, запустить командную оболочку и использовать имя пользователя и токен для задания учетных данных локально. После этого вы сможете легко отправлять образы в свой репозиторий, и Packer также сможет сделать это для вас.

Наконец, в файлах stm32-cde-docker.json и stm32-cde-images.json измените свойство repository в соответствии с вашим именем репозитория.

Создание образа CDE

Теперь, когда у вас подготовлены все необходимые инструменты, откройте репозитории, которые вы клонировали ранее и посмотрите на файлы stm32-cde-images.json, stm32-cde-aws.json и stm32-cde-docker.json. Эти файлы конфигурации, которые Packer будет использовать для создания образов. Файл stm32-cde-images.json содержит настройки и для docker и для AWS EC2 образов, а другие два файла содержат индивидуальные настройки для каждой из систем.

В этой статье я буду использовать только Docker, пока забудем обо всем, что связано с AWS. В файлах stm32-cde-images.json и stm32-cde-docker.json есть параметр builders для Docker. В моем случае я выбрал значение ubuntu:16.04 в качестве базового образа из официального хранилища docker. Базовый образ клиент в вашей системе извлекает из хаба и использует в качестве основы для создания нужного образа поверх этого. Этот официальный образ представляет собой упрощенный образ Ubuntu, в который вы можете добавить любые необходимые пакеты, инструменты и библиотеки (естественно, чем больше вы добавляете, тем больше размер образа). Базовый образ Ubuntu: 16.04 весит всего 123 МБ, но вес готового образа может легко увеличиться до ГБ и даже больше.

Как уже отмечалось ранее, хотя вы можете использовать Packer для создания образов системы из различных типов сборщиков, это не означает, что все базовые образы из всех источников будут одинаковыми, и между ними могут быть незначительные различия. Тем не менее, использование инструмента, такого как Ansible, позволит избежать любых проблем, поскольку он будет применять только те изменения, которых еще нет в образе, и по этой причине Ansible лучше shell, поскольку shell скрипт может завершиться ошибкой из-за разницы в образах. Конечно, если вы используете команды shell напрямую в вашей конфигурации Ansible, то вам самому нужно контролировать их выполнение.

Для создания образа Docker вам необходимо клонировать этот репозиторий:

https://bitbucket.org/dimtass/stm32-cde-template/src

и использовать Packer, чтобы создать файл stm32-cde-docker.json следующим образом:


git clone https://dimtass@bitbucket.org/dimtass/stm32-cde-template.git
cd stm32-cde-template
packer build stm32-cde-docker.json

 Примечание: перед запуском команды сборки вам нужно изменить переменную docker_repo и поместить в нее репозиторий Docker, который вы создали, а также создать токен для Docker, чтобы иметь возможность отправить образ.

Если все сделано правильно, вы увидите на экране примерно такую картину:


==> docker: Creating a temporary directory for sharing data...
==> docker: Pulling Docker image: ubuntu:04
...
==> docker: Using docker communicator to connect: 17.0.2
==> docker: Provisioning with shell script: /tmp/packer-shell909138989
...
==> docker: Provisioning with Ansible...
...
==> docker: Committing the container
...
==> docker: Killing the container: 84a9c7842a090521f8dc9bd70c39bd99b6ce46b0d409da3cdf68b05404484b0f
==> docker: Running post-processor: docker-tag
docker(docker-tag): Tagging image: sha256:0126157afb61d7b4dff8124fcc06a776425ac6c08eeb1866322a63fa1c3d3921
docker(docker-tag): Repository: dimtass/stm32-cde-image:1
==> docker: Running post-processor: docker-push
...
Build 'docker'

 Это означает, что образ был собран без ошибок, а также отправлен в хранилище Docker Hub.

Использование образа Docker CDE

Хорошо, теперь у нас есть образ, что нам с ним делать?

Конечно, этот образ можно использовать различными способами. Например, разработчики могут использовать образ для сборки своего кода локально на рабочей станции или удаленно, но этот же образ можно использовать для сборки кода в процессе непрерывной интеграции. Этот же образ можно использовать для тестирования, хотя в этом случае, вам потребуется добавить в него больше инструментов и пакетов. Следовательно, этот образ является базовым, который мы можем использовать во всем своем рабочем процессе.

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

В зависимости от типа образа, который вы создаете, есть несколько способов его использования. Например, если у вас есть VirtualBox VM, вам нужно запустить VBox и затем запустить образ, а при использовании Vagrant вы можете запустить образ используя «vagrant up» и «vagrant ssh». Если вы используете docker, вам нужно запустить новый контейнер из образа, подключиться к нему, выполнить свою работу, а затем остановить/уничтожить контейнер (или оставить его остановленным и использовать его позже). При использовании AWS AMI все немного по другому. Инструменты и способ, который вы использовали для создания образа, определяют, как его можно использовать. То, что происходит в фоновом режиме, в большинстве случаев почти одинаково (например, в случае с Docker, не имеет значения, используете ли вы Vagrant или командную строку Docker (cli).

В этой статье рассказывается только про использование образа Docker, а про Vagrant и AWS EC2 AMI будет отдельная статья.

Итак, начнем с образа Docker. Я не буду вдаваться в подробное описание команд cli, а просто перечислю их и объясню что они делают. Для получения более подробной информации вам необходимо обратиться к справочному руководству docker cli .

На предыдущих шагах мы создали образ Docker и поместили его в репозиторий на docker.io. В примерах дальше в статье будет использоваться имя моего репозитория, вам, конечно, следует изменить его на свой. Тестовый репозиторий, который мы будем использовать для создания кода, находится по следующему адресу:

https://bitbucket.org/dimtass/stm32f103-cmake-template/src/master/

Наша цель состоит в том, чтобы создать контейнер Docker из образа, а затем клонировать репозиторий кода и встроить его в контейнер Docker.


cd ~
docker run -it dimtass/stm32-cde-image:0.1 -c "/bin/bash"

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


cd /root
git clone https://dimtass@bitbucket.org/dimtass/stm32f103-cmake-template.git
cd stm32f103-cmake-template
TOOLCHAIN_DIR=/opt/toolchains/gcc-arm-none-eabi-9-2019-q4-major CLEANBUILD=true USE_STDPERIPH_DRIVER=ON SRC=src_stdperiph ./build.sh

Конечно, перечисленные выше команды должны выполняться внутри контейнера Docker. Итак, сначала вы переходите в каталог/root затем клонируете и создаете шаблон репозитория. В команде сборки (последней) вам необходимо указать путь к toolchain, который был определен с помощью Packer и Ansible как /opt/toolchains/gcc-arm-none-eabi-9- 2019-q4-major. Кроме того, вам нужно передать еще несколько параметров, описанных в документации к репозиторию stm32f103-cmake-template, которые зададут подключаемые библиотеки.

Результатом выполнения команды build будет примерно такой вывод:


[ 95%] Linking C executable stm32-cmake-template.elf
text       data        bss        dec        hex    filename
14924        856       1144      16924       421c    stm32-cmake-template.elf
[ 95%] Built target stm32-cmake-template.elf
Scanning dependencies of target stm32-cmake-template.bin
Scanning dependencies of target stm32-cmake-template.hex
[100%] Generating stm32-cmake-template.hex
[100%] Generating stm32-cmake-template.bin
[100%] Built target stm32-cmake-template.bin
[100%] Built target stm32-cmake-template.hex

Проект был собран и теперь у нас есть шестнадцатиричный файл, который мы можем использовать для прошивки. Но как можно проверить эту прошивку? Ну, существует много вариантов. Чтобы прошить контроллер из контейнера, вы можете открыть USB программатор st-link для докера, используя --device опцию при создании контейнера с помощью docker run -it команды. Например, как описано по этой ссылке. Мы вернемся к этой теме чуть позже.

Но как разработчик может использовать IDE для внесения изменений в код, а затем для сборки прошивки? Для этого нужно предоставить доступ к вашему диску из контейнера, используя тома. Таким образом, у вас будет папка, которая монтируется в контейнер, а затем вы можете клонировать репо внутри этой папки. В результате репозиторий будет клонирован в вашу локальную папку, поэтому вы можете использовать IDE в своей ОС для открытия кода и внесения изменений. А затем собрать прошивку внутри контейнера.

Для этого давайте сначала очистим предыдущий контейнер, выполнив следующую команду в своей ОС (не в контейнере Docker), которая выведет список текущих остановленных или запущенных контейнеров.


docker ps -a

Результат этой команды выглядит примерно следующим образом:


CONTAINER ID IMAGE                           COMMAND                  CREATED             STATUS                     PORTS               NAMES
166f2ef0ff7d dimtass/stm32-cde-image:1     "/bin/sh -c /bin/bash"   2 minutes ago       Up 2 minutes                                   admiring_jepsen

Это означает, что существует контейнер, использующий образ dimtass/stm32-cde-image. Его нужно остановить, а потом удалить с помощью следующих команд:


docker stop 166f2ef0ff7d
docker rm 166f2ef0ff7d

Обратите внимание, что 166f2ef0ff7d - это уникальный идентификатор контейнера, и вы можете использовать docker cli для выполнения операций с этим конкретным контейнером. Приведенные выше команды просто остановят и затем удалят контейнер.

Теперь вам нужно запустить новый контейнер и на этот раз смонтировать локальную папку из вашей файловой системы в контейнер докера. Для этого выполните следующие команды:


cd ~/Downloads
mkdir -p testing-docker-image/shared
cd testing-docker-image
docker run -v $(pwd)/shared:/home/stm32/shared -it dimtass/stm32-cde-image:1 -c "/bin/bash"

Теперь из контейнера нужно повторить клонирование репозитория git и сборку прошивки.


cd /home/stm32
su stm32
git clone --recursive https://dimtass@bitbucket.org/dimtass/stm32f103-cmake-template.git
cd stm32f103-cmake-template
TOOLCHAIN_DIR=/opt/toolchains/gcc-arm-none-eabi-9-2019-q4-major CLEANBUILD=true USE_STDPERIPH_DRIVER=ON SRC=src_stdperiph ./build.sh

Здесь есть ловушка! Чтобы это работало, пользователь вашей ОС должен иметь тот же uid и gid, что и пользователь stm32 в контейнере (или наоборот). Поэтому, если ваш пользователь имеет uid/gid = 1000/1000, то те же самые идентификаторы должны быть для пользователя stm32 в контейнере. Если это не так, то вам нужно создать пользователя в контейнере с таким же uid/gid. Если они не совпадают, то разрешения будут другими, и вы не сможете создавать/редактировать файлы.

Например, если ваш uid/gid в операционной системы равен 1001, а у пользователя stm32 в контейнере - 1000, то во время работы контейнера выполните эту команду в терминале внутри контейнера:


usermod -u 1001 stm32

Приведенная выше команда изменит uid/gid пользователя stm32 внутри контейнера с 1000 на 1001.

Теперь давайте предположим, что разрешения в порядке, и uid/gid одинаковы для пользователей вашей ОС и контейнера. Это означает, что вы можете использовать вашу любимую среду разработки и открыть проект git, внести изменения и одновременно запустить докер-контейнер в фоновом режиме и создать исходный код, когда захотите. Помните, что сборка будет ручной процедурой, и вы не сможете нажать кнопку в своей IDE, чтобы собрать исходный код в контейнере. Конечно, вы можете достичь и этой функциональности, если захотите, но давайте не будем сейчас заострять на этом внимание.

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

Прошивка контроллера

Для загрузки собранной в docker прошивки в контроллер можно использовать несколько способов. Первый из них - прошивка из под своей операционной системы, но в этом случае вам потребуется установки st-link. У разных разработчиков могут быть различные версии ОС, различные архитектуры компьютеров, и им понадобятся различные версии драйверов и другого ПО. Следовательно, добавление в образ docker st-flash поможет унифицировать процесс загрузки прошивки у различных разработчиков. В этом случае можно использовать роль Ansible, которая добавляет st-flash в образ, и вы сможете использовать программу для прошивки.

Как уже упоминалось ранее, образ docker должен иметь доступ к USB устройству st-link. Это не слишком сложно сделать. Для начала найдем путь к USB-устройству. Просто запустим команду lsusb на терминале и получим список устройств:


Bus 001 Device 006: ID 0483:3748 STMicroelectronics ST-LINK/V2

Эта строка означает, что устройство ST-LINK V2 было перечислено на шине 001, а его идентификатор 006. Для Linux и докера это означает, что если вы смонтируете путь / dev/bus/usb/001 / в образе докера, то вы сможете использовать устройство st-link. Но обратите внимание на один момент: этот прием всегда предполагает, что программатор будет подключен к одному и тому же порту. Обычно это не проблема для компьютера разработчика, но в случае, если вы хотите обобщить решение и смонтируете /dev/bus/usb, Docker сможет получить доступ к любому зарегистрированному USB-устройству. Это плохая практика, для разработки всегда используйте один и тот же USB-порт.

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


docker run -v $(pwd)/shared:/home/stm32/shared -v /dev/bus/usb/001:/dev/bus/usb/001 \
--privileged -it dimtass/stm32-cde-image:0.1 -c "/bin/bash"

 Отключите и подключите программатор. Затем, чтобы убедиться, что все работает правильно, выполните следующую команду:


st-info --probe

Она должна вернуть примерно следующее:


root@6ebcda502dd5:/# st-info --probe
Found 1 stlink programmers
serial: 513f6e06493f55564009213f
openocd: "\x51\x3f\x6e\x06\x49\x3f\x55\x56\x40\x09\x21\x3f"
flash: 65536 (pagesize: 1024)
sram: 20480
chipid: 0x0410
descr: F1 Medium-density device

Отлично, устройство работает из контейнера. Теперь давайте снова клонируем репозиторий, соберем и загрузим прошивку. Прежде чем перейти к этому шагу, убедитесь, что контроллер stm32f103 (я использую blue-pill) подключен к st-link, а st-link определяется в контейнере. Затем выполните следующие команды для сборки:


cd /home/stm32
su stm32
git clone --recursive https://dimtass@bitbucket.org/dimtass/stm32f103-cmake-template.git
cd stm32f103-cmake-template
TOOLCHAIN_DIR=/opt/toolchains/gcc-arm-none-eabi-9-2019-q4-major CLEANBUILD=true USE_STDPERIPH_DRIVER=ON SRC=src_stdperiph ./build.sh

 и прошивки:


st-flash --reset write build-stm32/src_stdperiph/stm32-cmake-template.bin 0x8000000

 На экране вы должны увидеть примерно следующее:


stm32@6ebcda502dd5:~/stm32f103-cmake-template$ st-flash --reset write build-stm32/src_stdperiph/stm32-cmake-template.bin 0x8000000
st-flash 1.5.1
2019-12-02T21:18:04 INFO common.c: Loading device parameters....
2019-12-02T21:18:04 INFO common.c: Device connected is: F1 Medium-density device, id 0x20036410
2019-12-02T21:18:04 INFO common.c: SRAM size: 0x5000 bytes (20 KiB), Flash: 0x10000 bytes (64 KiB) in pages of 1024 bytes
2019-12-02T21:18:04 INFO common.c: Attempting to write 15780 (0x3da4) bytes to stm32 address: 134217728 (0x8000000)
Flash page at addr: 0x08003c00 erased
2019-12-02T21:18:04 INFO common.c: Finished erasing 16 pages of 1024 (0x400) bytes
2019-12-02T21:18:04 INFO common.c: Starting Flash write for VL/F0/F3/F1_XL core id
2019-12-02T21:18:04 INFO flash_loader.c: Successfully loaded flash loader in sram
16/16 pages written
2019-12-02T21:18:05 INFO common.c: Starting verification of write complete
2019-12-02T21:18:05 INFO common.c: Flash written and verified! jolly good!

Итак,  вы запустили Docker-контейнер на своем компьютере, на котором, в принципе, может не быть никаких инструментов, необходимых для сборки и прошивки контроллера, и смогли собрать прошивки и загрузить ее. Таким же образом любой другой разработчик в команде в офисе или дома может создать ту же прошивку таким же образом, получить тот же двоичный файл и прошить его с той же версией программатора. Это означает, что вы только что исключили большую часть цепочки разработки, которая может вызвать непредсказуемые проблемы и, следовательно, задержать ваш процесс разработки.

Теперь каждый разработчик, имеющий доступ к этому образу, может создавать и прошивать одну и ту же прошивку одинаково.

Возможно, некоторые разработчики посчитают этот процесс сложным, но это не совсем так. Если задуматься, для разработчика единственным дополнительным шагом является создание контейнера один раз (для команды) и запуск этого контейнера при каждой загрузке. Вся остальная разработка может идти как и раньше. Если вы раньше нажимали кнопку в IDE для сборки и прошивки, то вы можете делать то же самое и с контейнером - обычно IDE поддерживают создание пользовательских команд и кнопок или запуска сценариев, так что вы можете использовать их для достижения той же функциональности.

Использование gitlab-ci для CI/CD

До сих пор я объяснял, как вы можете использовать образ CDE для создания исходного кода в качестве разработчика, но во встроенных проектах также важно иметь конвейер CI/CD, который создает код и автоматически выполняет некоторые сценарии или тесты. Например, в вашем конвейере вы также можете запустить инструмент форматирования стилей (например, clang-format), затем запустить тесты и, если все пройдет нормально, получить успешную сборку.

Существует множество сервисов CI/CD, таких как Jenkins, Travis-CI, gitlab-ci, buildbot, bamboo и многие другие. У каждого из них есть свои плюсы и минусы. Я не использовал их все, поэтому я не могу прокомментировать их различия. Я использовал только gitab-ci, jenkins, buildbot и bamboo, но опять же не очень много. Bamboo в основном используется в компаниях вместе с Atlassian, этот сервис с закрытым исходным кодом и требует покупки лицензии. Jenkins самый известный из всех, он бесплатный, с открытым исходным кодом и имеет множество плагинов. Buildbot часто используется для проектов с Yocto. Gitlab-CI используется в платформе gitlab и также бесплатен.

Из всего вышесказанного я лично предпочитаю gitlab-ci, поскольку это самый простой и понятный сервис для создания конвейеров, легкий и без особых сложностей. Например, я считаю пайплайны Jenkins очень громоздкими и сложными в обслуживании; такое ощущение, что конвейерный интерфейс не очень хорошо вписывается в остальную часть платформы. В любом случае, здесь нет правильного или неправильного выбора, все упирается в вашу текущую инфраструктуру, потребности и опыт.

Чтобы использовать gitlab-ci, вам нужно разместить свой проект в gitlab или установить gitlab на свой собственный сервер. В этом примере я размещаю проект stm32f103-cmake-template одновременно в bitbucket, github и gitlab, и все эти репозитории зеркалят друг друга ( это можно сделать, например, с помощью сценария, описанного здесь).

Чтобы использовать gitlab-ci, вам нужен только файл шаблона в вашем репозитории, который называется .gitlab-ci.yml. Это простой файл YAML, который настраивает CI/CD для вашего проекта. (Подробная документация по использованию этого файла приведена  на сайте). В этом примере я буду использовать только базовые возможности, но вы сможете реализовать и гораздо более сложные вещи.

Для примера мы будем использовать следующий репозиторий:

https://gitlab.com/dimtass/stm32f103-cmake-template

Я добавил в него файл.gitlab-ci.yml со следующим содержанием:


---
image:
name: dimtass/stm32-cde-image:0.1
entrypoint: [""]
 
variables:
GIT_SUBMODULE_STRATEGY: recursive
 
stages:
- build
- test
 
build:
stage: build
script: TOOLCHAIN_DIR=/opt/toolchains/gcc-arm-none-eabi-9-2019-q4-major CLEANBUILD=true USE_STDPERIPH_DRIVER=ON SRC=src_stdperiph ./build.sh
 
test:
stage: test
script: echo "Dummy tests are done..."

Первое, что вы видите, это определение образа. Эти строки указывают на docker образ, который был создан с помощью packer и отправлен в docker-hub. Эта запись заставит программу загрузить образ, а затем запустить контейнер, и внутри этого контейнера будут выполняться указанные далее этапы. Кроме того, поле entrypoint помогает решить проблему, которая возникает при работе с некоторыми образами, созданными с помощью Packer - контейнер не может найти путь /bin/sh. (Подробнее смотри здесь)..

Наконец, определяются два этапа, сборка и тестирование. Этап тестирования ничего не делает в данном случае, но этап сборки будет запускать команды сборки, и если какой-либо шаг этих этапов завершится неудачей, конвейер завершится неудачей.

После коммита и пуша файла .gitlab-ci.yml в репозиторий сервис CI/CD автоматически запустит агента (runner) и конвейер. После получения задания агент начнет выполнять шаги из yaml файла. Посмотрите, как создать этот файл. Это важное отличие gitlab-ci по сравнению с другими сервисами CI/CD.

Запуск агента, процесс получения образа dimtass/stm32-cde-image и запуска модулей git

Рис.1. Запуск агента, процесс получения образа dimtass/stm32-cde-image и запуска модулей git

Процесс сборки прошивки

Рис.2. Процесс сборки прошивки

Процесс успешного завершение сборки и тестирования

Рс.3. Процесс успешного завершение сборки и тестирования

На первом скриншоте вы видите запуск агента, процесс получения образа dimtass/stm32-cde-image и запуска модулей git. На втором виден успешный процесс сборки прошивки, на третьем успешное завершение сборки и тестирования.

Обратите внимание на следующее. В результате работы конвейера не остается никаких произведенных артефактов, т.е. прошивка собирается, а затем удаляется. Если вам нужно сохранить прошивку, нужно немного изменить файл конфигурации.gitlab-ci.yml:


---
image:
name: dimtass/stm32-cde-image:1
entrypoint: [""]
 
variables:
GIT_SUBMODULE_STRATEGY: recursive
 
stages:
- build
- test
 
build:
stage: build
script: TOOLCHAIN_DIR=/opt/toolchains/gcc-arm-none-eabi-9-2019-q4-major CLEANBUILD=true USE_STDPERIPH_DRIVER=ON SRC=src_stdperiph ./build.sh
artifacts:
paths:
- build-stm32/src_stdperiph/stm32-cmake-template.bin
expire_in: 1 week
 
test:
stage: test
script: echo "Dummy tests are done..."

Приведенный выше скрипт yml теперь также создаст файл прошивки, который вы можете самостоятельно загрузить, прошить и протестировать. Чтобы получить прошивку, щелкните по меню CI/CD -> Pipelines в вашем проекте, выберите раскрывающееся меню в последней сборке и загрузите ZIP-файл, содержащий артефакт, как показано на рисунке 4.

Интерфейс процесса загрузки ZIP-файла

Рис. 4. Интерфейс процесса загрузки ZIP-файла

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

Нужное поведение можно реализовать с помощью cache. Это позволяет при выполнении этапа кэшировать определенную папку (набор папок) или файл/файлы и пометить их ключом. Затем на другом этапе вы можете использовать ключ кеша и получить доступ к этим файлам/папкам и выполнять любые действия, которые вам понадобятся. Давайте изменим файл.gitlab-ci.yml для реализации этого подхода:


---
image:
name: dimtass/stm32-cde-image:1
entrypoint: [""]
 
variables:
GIT_SUBMODULE_STRATEGY: recursive
 
stages:
- build
- test
 
build:
stage: build
script: TOOLCHAIN_DIR=/opt/toolchains/gcc-arm-none-eabi-9-2019-q4-major CLEANBUILD=true USE_STDPERIPH_DRIVER=ON SRC=src_stdperiph ./build.sh
cache:
key: build-cache
paths:
- build-stm32/src_stdperiph
artifacts:
paths:
- build-stm32/src_stdperiph/stm32-cmake-template.bin
expire_in: 1 week
 
test:
stage: test
script: file build-stm32/src_stdperiph/stm32-cmake-template.bin
cache:
key: build-cache

 Эта конфигурация позволяет закэшировать папку build-stm32/src_stdperiph на этапе сборки, а затем на этапе тестирования использует файл, который будет извлечен из кэша. На экране это будет выглядеть примерно следующим образом:


Downloading artifacts for build (368146185)...
Downloading artifacts from coordinator... ok id=368146185 responseStatus=200 OK token=jeC2trFF
$ file build-stm32/src_stdperiph/stm32-cmake-template.bin
build-stm32/src_stdperiph/stm32-cmake-template.bin: data
Job succeeded

Задача выполнена, прошивка автоматически собирается и тестируется.

Заключение

В этой статье о применении подхода DevOps для разработки встроенного ПО я рассказал о создании образа Docker, который может использоваться разработчиками в качестве CDE (общая среда разработки) и сервисов CI/CD для организации конвейера. Для создания образа использовался Packer и Ansible. Packer полезен, потому что вы можете использовать его для создания похожих образов с одним и тем же файлом конфигурации для разных провайдеров, таких как docker, AWS и т. д. Ansible удобен тем, что он поддерживает также много разных провайдеров, а также позволяет настраивать образ удаленно.

Далее был протестировал образ CDE для локального создания прошивки, а также прошить микроконтроллер с помощью программы st-flash, входящей в состав образа.

Затем с помощью gitlab-ci был создан конвейер CI/CD, который выполняет сборку и тестирование. В следующей статье будет рассказано об использовании AWS EC2 AMI (и других инструментов AWS), чтобы реализовать  этот же функционал.

Источник: https://www.stupid-projects.com

Автор: dimtass Перевод: Китаин Сергей, г. Москва

Разделы: Программаторы

Опубликовано: 31.01.2020

Товары

ST-LINK/V2 ST-LINK/V2 (ST)
0 шт.
нет в наличии
добавить к сравнению
ST-LINK/V2 [mini] хит ST-LINK/V2 [mini] (WAVESHAR)
38 шт.
нет в наличии
добавить к сравнению
ST-LINK/V2-ISOL ST-LINK/V2-ISOL (ST)
0 шт.
нет в наличии
добавить к сравнению
STLINK-V3MINI STLINK-V3MINI (ST)
0 шт.
нет в наличии
добавить к сравнению
STLINK-V3SET STLINK-V3SET (ST)
0 шт.
нет в наличии
добавить к сравнению
  • Москва
  • Санкт-Петербург
  • Мурманск
  • Ульяновск
  • Новосибирск
  • Екатеринбург
  • Краснодар
  • Нижний Новгород
  • Воронеж
  • Уфа
  • Челябинск
  • Самара
  • Красноярск
  • Казань
  • Ростов-на-Дону
  • Саратов
  • Пермь
  • Томск
  • Иркутск
  • Омск
  • Тюмень

Актуальность предложений на товары в корзине истекла, данные были удалены 29.11.2022 в 00:00:00 (Мск.) Список позиций из корзины сохранен в Списке товаров
Актуальность предложений на товары в корзине истекла, данные были удалены 29.11.2022 в 00:00:00 (Мск.) Зарегистрируйтесь или авторизуйтесь на сайте, если регистрировались ранее, чтобы сохранять список товаров из корзины

Данный товар получен от клиентов, которые купили его для целей производства, но он оказался не востребован. Возможно отсутствие ГТД и страны происхождения.