С какими вызовами сталкиваются разработчики UMI
«Если юзеру для начала работы с продуктом нужно нажать больше одной кнопки, он не захочет этого делать»
Друзья, мы всегда стремились и стремимся создавать такой продукт, который будет максимально технологичным, понятным, безопасным и удобным для пользователя любого уровня. В сегодняшней статье мы хотим поделиться с вами проблемами, с которыми мы — разработчики UMI — сталкиваемся, а также решениями этих проблем. Ведь вы видите только конечный продукт — приложение, с которым удобно взаимодействовать на смартфоне или компьютере. Но «под капотом» этого приложения создается сложная инфраструктура со множеством различных решений.
Сегодня мы расскажем, какой путь проходит готовый программный код перед тем, как его начнут использовать наши юзеры. Ведь очевидно, что для создания удобного продукта просто написать красивый и работающий код недостаточно. Нужно собрать и упаковать программу таким образом, чтобы пользователю было удобно с ней работать.
Разные операционные системы и архитектурные платформы
Существует огромное количество операционных систем на разных архитектурных платформах, однако пытаться создавать решения сразу для всех не имеет смысла. Логичнее будет при создании приложения ограничиться самыми популярными системами.
Исторически так сложилось, что на данный момент существуют три популярные операционные системы: Windows, Linux и macOS. Однако они могут запускаться на разном оборудовании, из-за чего появляется новая переменная — архитектура процессора.
Раньше основными были i386 (IA-32, 32-битная) и AMD64 (x86-64, 64-битная), однако вместе с популяризацией различных мобильных устройств началось активное развитие платформы на архитектуре ARM, которая тоже прошла путь совершенствования от 32-битной до 64-битной версии.
Разница между 32-битной и 64-битной версиями для простого пользователя одна — последняя дает возможность использовать больше 4 GB оперативной памяти без особых сложностей. А поскольку мы знали, что сеть UMI будет обрабатывать огромный объем транзакций, то понимали, что и потребление памяти нодами в сети будет большим. Соответственно, запуск ноды с малым объемом адресуемой памяти не имеет смысла в долгосрочной перспективе.
Опираясь на вышеприведенные вводные, мы решили ограничиться поддержкой только 64-битных архитектур. На практике это означает поддержку четырех вариантов операционных систем и архитектур процессора для каждой из них:
- ОС Windows и архитектура процессора AMD64;
- ОС Linux и архитектура процессора ARM64;
- ОС Linux и AMD64;
- А также два варианта для устройств Apple, которые можно объединить в один универсальный: операционная система Darwin и архитектура процессора ARM64 (для процессоров M1) или AMD64 (для процессоров Intel).
Нам удалось избавиться от внешних зависимостей, благодаря чему сборка программного ядра происходит легко — для этого достаточно компилятора Go (golang).
Однако все сложности еще впереди, потому что нужно упаковать приложение в пакет. Рассмотрим решение данной задачи на примере ОС Linux.
Вариантов дистрибутивов Linux существует огромное количество. Все они различаются между собой в основном набором библиотек, включенных в дистрибутив. Именно на этом этапе и возникают сложности. Например, если собрать приложение с зависимостями для CentOS 5, оно может не работать на более свежей версии операционной системы из-за другой версии библиотек, от которых зависит приложение.
В связи с этим мы собираем приложение UMI без динамических зависимостей, то есть оно вообще никак не зависит от окружения, в котором будет запущено.
Может показаться, что благодаря этому проблема решилась — нужно только скачать или собрать из исходников бинарный файл UMI и запустить его. Но на самом деле все немного сложнее. Дело в том, что изначально вся наша архитектура строилась на основе открытого программного обеспечения Kubernetes. Соответственно, в качестве средства упаковки мы использовали Docker. То есть мы собирали образы Docker под платформу AMD64, и все прекрасно работало. Также Docker было удобно использовать в локальном окружении.
Однако недавно появилась версия Docker под новую платформу Apple M1, в связи с чем нам пришлось собирать две версии пакета — под AMD64 и ARM64. Поначалу сборка проходила без проблем благодаря экспериментальной функции Docker под названием buildx. Но из-за перехода на бинарный блокчейн нам пришлось отказаться от Kubernetes, потому что появились новые требования к скорости работы дисковой подсистемы. Поскольку для запуска виртуальных машин в облаке Amazon мы используем Amazon Linux 2, основанный на дистрибутиве RHEL, было принято решение попробовать использовать штатный менеджер пакетов advanced packaging tool (apt) и сборку в RPM.
Вначале проблем со сборкой RPM-пакетов с использованием Docker под платформу AMD64 не было. Позже, работая над оптимизацией расходов на серверную архитектуру, мы попробовали использовать инстансы AWS на платформе ARM64. Для этого нужно было сделать сборку RPM-пакетов для архитектуры ARM64. Buildx прекрасно справился с этой задачей.
Параллельно сообщество UMI попросило добавить поддержку пакетного менеджера Yum, который используется в Debian и Ubuntu. Мы это сделали. В итоге у нас появился свой репозиторий пакетов —
https://pkg.umi.top/.
Соответственно, подготавливая каждый релиз, мы собирали для Linux три варианта пакетов — каждый под две архитектуры. Итого шесть стадий разработки, если не считать пайплайны для обновления и синхронизации репозиториев.
Позже мы столкнулись с проблемами:
- После каждого обновления macOS (которая используется нашими разработчиками в качестве основной ОС) используемые нами программы начинали работать все хуже и хуже.
- В эмуляторе Android после очередного обновления Android Studio перестала работать сеть;
- Появились проблемы с работой сети в VirtualBox, который мы активно использовали для тестирования сетевого взаимодействия.
Работать в таких условиях становилось все сложнее — все больше времени уходило на починку инструментов для работы, чем на саму работу. Когда мы говорим об инструментах, то имеем в виду компиляторы (упаковщики), необходимые для подготовки продуктов к релизу.
Мы решили попробовать вариант с тестовым окружением на основе Google Cloud и создали для этого новый аккаунт. Но на практике оказалось, что это не дает больших преимуществ, поэтому мы отказались от этой идеи.
Постепенно мы пришли к варианту с гипервизором ESXi, запущенном на локальном компьютере. Этот вариант нам понравился больше всего, и мы остановились на нем. Однако осталась проблема сборки пакетов под разные архитектуры. Самым логичным вариантом было бы запустить отдельные сервера для сборки под целевую архитектуру. В таком случае мы бы получили максимальную эффективность при относительно небольших увеличениях расходов на инфраструктуру.
Но нестабильность в мире, наблюдающаяся в последнее время, заставила пересмотреть идею использования «чужих» серверов под критически важные сервисы. Поэтому мы начали искать решение, которое позволило бы в рамках одного локального сервера решить проблемы со сборкой всех необходимых пакетов и обновлением репозиториев. Это же решение должно быть легко воспроизводимым в том случае, если потребуется перенести все компоненты в другое окружение.
В итоге выбор пал на GoReleaser. Первые впечатления оказались вполне хорошими. Сейчас мы постепенно переводим сборочные конвейеры на этот инструмент. В ходе работы мы тщательно все изучаем и тестируем, а также сталкиваемся со множеством нюансов. Это занимает много времени, но пока такой подход кажется нам наиболее удачным.
Друзья, как видите, разработка сложных блокчейн-продуктов — это трудоемкий процесс, который не виден большинству пользователей. Мы постоянно сталкиваемся с новыми вызовами и иногда тратим много ресурсов и творческой энергии на решение возникающих проблем. Однако это того стоит, ведь мы стремимся создать наилучший продукт на рынке, который будет доступен максимальному количеству пользователей.
В дальнейшем мы будем продолжать держать с вами тесную связь и публиковать вот такие технические статьи, в которых делимся своим опытом. Надеемся, вам было интересно.
С любовью, команда UMI!