В статье расскажу, как в Bercut реализовали интеграцию внутренней системы управления конфигурациями с популярной экосистемой Maven, что позволило обеспечить более удобное и стандартизированное распространение артефактов. Поделюсь проблемами, с которыми мы столкнулись при совмещении разных моделей идентификации артефактов и обеспечении корректности зависимостей, и расскажу о выбранном техническом решении на базе Jenkins, RabbitMQ и специального Consumer Service.

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

Система управления конфигурациями «Автосборка»

Мы используем внутреннюю корпоративную систему управления конфигурациями «Автосборка» — промышленный конвейер для разработки, сборки, выпуска и сопровождения программного обеспечения. Она реализует единый для всей компании процесс производства ПО: централизованно управляет конфигурациями компонентов и систем (наборов компонентов), их зависимостями, версиями и статусами готовности.

Система обеспечивает целостность и гарантированную воспроизводимость сборок, однозначную идентификацию версий и полную прослеживаемость изменений на всех стадиях жизненного цикла программного обеспечения. Также «Автосборка» поддерживает компонентный подход и возможность повторного использования, позволяет надёжно пересобирать и точечно исправлять любые версии компонентов.

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

Ряд продуктов нашей компании работает на единой программной платформе, написанной на Java и состоящей из нескольких платформенных систем. В основном эти продукты созданы на BPEL, однако часть из них разрабатывается на Java и зависит от платформенной системы RTSIB (Real Time Service Integration Bus).

В последние годы в Bercut наблюдается рост доли продуктов, разрабатываемых на Java: их количество увеличилось и уже достигло 25% от общего числа.

Некоторые команды стали использовать систему сборки Maven, и у нас появилась необходимость публиковать часть артефактов в Maven‑совместимый репозиторий и представлять зависимости в привычной для экосистемы Maven форме.

Так появилась задача интеграции двух систем хранения артефактов: «Автосборки» и Maven‑репозитория (в нашем случае — Sonatype Nexus).

Автоматическая загрузка артефактов в Maven-репозиторий

Первоначально мы собирались реализовать автоматическую загрузку артефактов системы RTSIB в Maven‑репозиторий. Например, для отмеченных артефактов «Автосборка» сама будет выполнять загрузку.

Однако возникли следующие проблемы:

  • Разные модели идентификации артефактов. Внутри «Автосборки» используются внутренние идентификаторы или наименования, а в Maven — координаты артефакта (groupId/artifactId/version). Поэтому требуется явно хранить и поддерживать маппинг между сущностями «Автосборки» и Maven‑координатами, чтобы однозначно связывать опубликованный артефакт с его источником и контекстом сборки.

  • Генерация информации о зависимостях артефактов в Maven‑формате. «Автосборка» может предоставить состав и граф зависимостей, но при поштучной публикации артефактов в Maven‑репозиторий нет гарантии, что на момент публикации конкретного артефакта все его зависимости уже доступны в репозитории. Это создаёт риск возникновения «битых» зависимостей при использовании. Поэтому публикацию следует трактовать как управляемый процесс (например, на уровне релиза системы), где можно проверить полноту и консистентность публикации.

  • «Автосборка» — критичный и нагруженный производственный контур, поэтому расширять её логикой, не относящейся напрямую к ядру сборки или выпуска, нужно осторожно. Чтобы не увеличивать связность и операционные риски, интеграционные функции (публикация во внешний формат или репозиторий, хранение маппингов, контроль консистентности публикации) разумно выносить в отдельный компонент, оставляя «Автосборку» источником истины по сборке и составу.

Поэтому мы приняли решение:

  • Вынести публикацию в Maven‑репозиторий в отдельный сервис.

  • Загрузку в Maven‑репозитории выполнять не для каждого отдельного артефакта, а для сборки платформенной системы RTSIB, как согласованного набора артефактов.

  • Кроме загрузки артефактов в Maven‑репозитории выполнять генерацию Parent POM‑файла (родительского файла конфигурации сборки Maven) для всех артефактов платформенной системы RTSIB.

  • Реализовать механизм передачи информацию о завершенной сборке от «Автосборки» в сервис публикации в Maven‑репозитории.

Конвейер Jenkins для загрузки в Maven-репозиторий

В качестве основы для такого сервиса мы выбрали Jenkins. Каналом обмена данными между «Автосборкой» и Jenkins стал RabbitMQ. Поскольку Jenkins не способен принимать сообщения по RabbitMQ, на языке Go был разработан RabbitMQ Consumer Service. На Jenkins мы создали конвейер для последовательной загрузки артефактов в Maven‑репозиторий и создания зависимостей системы для Maven‑сборки.

Конвейер работает следующим образом:

  • Разработчик заказывает сборку платформенной системы в «Автосборке».

  • «Автосборка» собирает дистрибутив платформенной системы RTSIB из артефактов, размещает его в файловом хранилище и после этого отправляет в RabbitMQ сообщение с информацией о сборке.

  • RabbitMQ Consumer Service преобразует сообщение из RabbitMQ в HTTP‑запрос к Jenkins. 

  • Jenkins получает запрос и по своим настройкам определяет, что для платформенной системы RTSIB необходимо запустить загрузку артефактов в Maven‑репозиторий и генерацию Parent POM‑файла:

    1. Загружает в Maven‑репозиторий все артефакты системы, используя имя jar файла в качестве имени артефакта.

    2. Перебирает все артефакты в дистрибутиве системы, создаёт один Parent POM‑файл с их перечислением и загружает его в Maven‑репозиторий.

После этого разработчики продуктов используют Parent POM‑файл системы в своих проектах для сборки в Maven.

Parent POM-файл

Отдельно хочу рассказать про Parent POM‑файл.

В первых версиях системы интеграции мы публиковали платформенные артефакты в Maven‑репозитории. А разработчики продуктов сами составляли POM-файлы для своих проектов. Список зависимостей составлял полтора десятка компонентов с указанием версий. Когда мы выпускали следующий релиз платформенной системы, разработчики продуктов руками меняли номера версий в своём POM-файле. Это был трудозатратный и полный ошибок процесс.

Поскольку я сам участвовал в этом процессе ручного изменения POM-файлов, мне было понятно, что требуется системное решение. И оно нашлось — это Parent POM‑файл.

Сначала сделал генерацию Parent POM‑файла с перечнем всех артефактов платформенной системы RTSIB. И его можно было применять для сборки некоторых продуктовых артефактов. Но большинство продуктовых артефактов разрабатывалось с использованием фреймворка Spring Boot, и уже имело свой Spring Boot Parent POM. А Maven не позволяет использовать два Parent POM‑файла.

Тогда я сделал генерацию Parent POM‑файла с перечнем всех артефактов платформенной системы RTSIB, в котором был указан свой родительский Spring Boot Parent POM. Т.е. создал иерархию файлов:

Spring Boot Parent POM
|‑-→ Bercut RTSIB Parent POM
|‑-→ Продуктовый POM

В разных командах у нас используют разные версии Spring Boot, и им нужны разные версии Bercut RTSIB Parent POM. Сейчас система создаёт 4 варианта этого файла:

  • Для сборки без Spring Boot

  • Для сборки с Spring Boot 2.35

  • Для сборки с Spring Boot 2.45

  • Для сборки с Spring Boot 2.70

Создание Parent POM‑файлов оказалось спасением для разработчиков продуктов, которые получили экономию времени на обновлении зависимостей. А для разработчиков платформенной RTSIB POM‑файлы гарантируют, что в продуктах будут использованы только совместимые между собой артефакты.

Результаты использования системы

Мы уже несколько лет используем описанную систему интеграции и с её разработали использованием больше десятка продуктов. Система зарекомендовала себя как надёжное решение.

У нее есть явные преимущества:

  • Разработчики продуктов используют только один Parent POM‑файл вместо списка зависимостей. Он содержит всю необходимую информацию для разработки и сборки с помощью Maven.

  • Создаётся несколько Parent POM‑файлов — для разных видов сервисов и для нескольких версий Spring Boot.

  • В рамках выбранного подхода «Автосборку» не перегружали дополнительными хранилищами и сложной интеграционной логикой. Был добавлен простой механизм: отправка события (нотификации) в RabbitMQ об успешной сборке системы. Далее внешние потребители события могут выполнять синхронизацию или публикацию и хранить служебные данные, не усложняя сам производственный контур «Автосборки».

Но есть и некоторые недостатки:

  • Потребовалось разработать, запустить и поддерживать RabbitMQ Consumer Service. 

  • Отдельные артефакты в Maven‑репозитории не содержат зависимостей от других артефактов.

  • Отсутствует хранилище информации о соответствии между сущностями «Автосборки» и Maven‑координатами. Связь «что опубликовано и чему это соответствует в исходной системе» остаётся неявной и может теряться.

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

Сейчас ставим перед собой новую цель — доработать «Автосборку», сделав ее единым источником данных для сборки и выпуска ПО. Так Maven‑репозиторий станет управляемым каналом публикации, а не обособленной внешней системой.