Привет, Хабр! На связи команда разработки App.Farm — продукта, созданного в РСХБ‑Интех. Хотели бы представить вам цикл статей о нем.
App.Farm — продукт по типу PaaS, необходимый для стандартизации процесса разработки бизнес‑приложений: от хранения исходного кода до запуска сервисов. Основные подсистемы платформы включают хранилище исходного кода и CI, хранилище артефактов, среду исполнения приложений, SSO, интеграционную подсистему, observability и т. д.
Подробнее ознакомиться с компонентами можно в обзорной статье, ранее опубликованной на Хабре: Как мы создавали PaaS‑платформу App.Farm. Сейчас мы бы хотели углубиться в детали реализации и поделиться с вами проблемами, которые мы решали, и как пришли к текущей архитектуре. Первый цикл статей мы решили посвятить одной из подсистем нашей платформы — App.Farm CI.
К созданию единого механизма CI мы пришли из проблемы разрозненного подхода к разработке (и всему, что с ней связано) в компании и привлекаемых ей вендоров. Частью решения этой проблемы является введение единого механизма CI и сопутствующих ему вещей. В качестве инструмента решили использовать Gitlab. В этой статье расскажем о полном спектре решаемых проблем и выборе инструментов.
Какие темы затронем:
Проблема и пути решения.
Выбор инструментов.
Правила разработки и организационные процессы.
К чему пришли.
Дисклеймер: это история развития нашего продукта, наш опыт и грабли, на которые наступили, здесь не будет глубокого технического сравнения тех или иных технологий.
Проблемы и пути решения
Прежде, чем говорить о внедрении каких‑то инструментов, обозначим проблемы, которые собирались решать.
РСХБ‑Интех — крупная IT‑компания, которая по большей части занимается задачами автоматизации бизнеса банка. Специфика компании подразумевает наличие огромного количества команд внутренней разработки, а также наличие команд внедрения вендорских решений. В момент нашего прихода в компанию каждая из таких команд, особенно вендорских, имела свои стандарты, технический стек, процесс разработки. Отсюда выявились проблемы в глобальных масштабах:
нет единого процесса и правил разработки: каждая команда изобретает свои правила и процессы;
нет единой кодовой базы: кто-то разворачивает систему для хранения кода, кто-то обменивается исходниками в почте;
нет автоматизации разработки: кто-то делает полноценный CI, кто-то собирает вручную;
команды разработки вынуждены иметь в штате инфраструктурных инженеров, чтобы разрабатывать и сопровождать собственный инструментарий для автоматизации разработки;
отсутствие контроля качества кода: кто-то внедряет инструменты для контроля качества, кто-то нет;
отсутствие DevSecOps проверок: команды разработки не заинтересованы внедрять инструменты DevSecOps, а подразделение ИБ из-за разрозненности команд не может эффективно внедрить свои инструменты везде;
все бюрократические вопросы по предоставлению сетевых доступов, взаимодействию между контурами каждая команда решает своими силами;
дублирование решений: отсутствует единый реестр решений, где можно было найти и получить информацию о внедренных или реализованных приложениях, найти быстро их исходный код и владельца;
отсутствие единой службы сопровождения: каждая команда реализует свой саппорт;
при возникновении инцидентов сложно найти ответственных и прийти по адресу;
отсутствует аудит используемых инструментов и технологий: невозможно понять, кто использует или пытается внедрить нежелательные проприетарные решения;
долгий, непрогнозируемый выпуск приложений, плохой показатель time to market.
Это только часть проблем, решения которых будем рассматривать в контексте цикла статей App.Farm CI.
В то время мы еще не знали, что впоследствии у нас получится платформа для разработки, и пошли решать поставленные задачи. Начали с наведения порядка в процессе разработки и хранения кодовой базы. Вообще вся наша разработка и внедрение пройдет под лозунгом «наведение порядка» и избавления от хаоса.
Определили план работ:
придумать универсальный процесс разработки;
подобрать и внедрить систему для хранения кода;
подобрать и внедрить систему для хранения артефактов;
реализовать CI скрипты, необходимые командам разработки;
переместить разработку на новые инструменты.
Выбор инструментов
По правде говоря, у нас не было времени на глубокое исследование инструментов и анализ. Решение нужно было «уже вчера», как обычно и происходит. Поэтому мы переиспользовали стек технологий для построения CI исходя из опыта внедрения подобных систем на других проектах.
На момент реализации мы плохо понимали количество будущих пользователей и объем их данных, то есть прототип практически мгновенно перерос в продуктовую эксплуатацию. Так в режиме MVP мы просуществовали 3 года, накопили огромный бэклог того, что нам надо доработать или исправить. Но это уже совсем другая история.
С учетом специфики нашей компании мы сформулировали ряд требований, которые ожидали от системы хранения исходного кода:
бесплатная, open source;
возможность работы в мультиарендном режиме;
наличие аутентификации, авторизации, гибкой ролевой модели;
возможность создания иерархии групп проектов;
нативная поддержка CI или интеграция с CI-системами;
self-hosted установка;
возможность расширения.
До написания этих требований мы уже знали, что этим решением будет Gitlab, т.к. у нас уже был опыт внедрения и работы с Gitlab на других проектах. Поэтому требования выглядят так, как будто кроме Gitlab других альтернатив под них и не существует.
Кроме того, Gitlab был привлекательным решением, т.к. имеет собственный CI‑движок, который мы и решили использовать. Предоставляемая им возможность использовать в качестве runners Kubernetes Executer вписывалась в наши планы по дальнейшему внедрению Kubernetes в качестве среды исполнения бизнес‑приложений.
Спустя несколько лет использования можно сказать, что Gitlab хороший инструмент и заменять его мы не собираемся.
В качестве минусов можно отметить различные ограничения бесплатной версии, например, отсутствие возможности на MergeRequest установить несколько проверяющих или связывание MergeRequest между собой. Также не хватает возможности поиска по исходному коду всех проектов, которая отлично работает в Github. Но по мере развития Gitlab некоторые фичи перетекают из платной версии в бесплатную, например, Child Items в задачах для ведения эпиков. Так что, возможно, некоторые ограничения для бесплатной версии и снимутся в будущем.
Еще в качестве неудобства, с которым мы столкнулись, можно отметить REST API Gitlab. У нас есть несколько инструментов, которые интегрируются с Gitlab для решения тех или иных задач, например:
сбор статистики и формирование различных бизнес‑метрик по использованию Gitlab командами;
отображение информации о пользовательских проектах на нашем дашборде платформы.
У нас возникли следующие проблемы с API: производительность запросов, проблемы с поиском, отсутствие агрегационных запросов. Присутствует и еще одна неприятная особенность — UI Gitlab не обращается на свой API напрямую, а делает это через SSR, что не дает проинспектировать какие-то запросы в API непосредственно из UI. Это усложняет исследования работы его API и не дает просто скопировать URL для переиспользования части функциональности конкретной страницы.
Про CI составляющую в Gitlab можно сказать, что это отличный инструмент, который позволил нам гибко настроить автоматизацию производства.
При увеличении количества использующих Gitlab CI команд мы столкнулись со следующими проблемами:
долгое время выполнения конвейеров, например, задержки на так называемое «межjobьe» — время ожидания, пока создается Kubernetes Pod для выполнения очередной CI Job;
отсутствие нативных функций по кэшированию, из-за чего приходится изобретать собственные решения (ранее на базе kaniko, теперь на базе buildkit);
высокие трудозатраты на поддержку и развитие Gitlab CI-скриптов, когда их стало очень много;
падение мотивации у разработчиков и инженеров развивать CI-скрипты, никто не хочет быть YAML- программистом или постоянно ковыряться в bash-скриптах.
О том, как мы собираемся решать эти проблемы, мы расскажем в следующих частях.
Помимо хранения кода нам сразу нужно было задуматься о хранении артефактов, которые бы собирались в конвейерах.
Мы выбрали Sonatype Nexus, т.к. на тот момент это был универсальный комбайн, позволяющий работать с большим количеством видов артефактов, что покрывало наши запросы. Также выбор был обусловлен наличием опыта работы с инструментом.
Какие плюсы мы выделили у Nexus:
условно бесплатный;
написан на Java, поддается расширению;
огромное количество поддерживаемых Package Registry;
поддержка Container Registry;
поддержка proxy, hosted, group репозиториев;
интеграция с SSO, ролевая модель;
наличие политик очистки;
наличие контрольной суммы для артефактов.
Но сейчас с Nexus начали возникать проблемы, из-за которых в будущем у нас запланирован пересмотр его использования:
в бесплатной версии существуют «рекомендуемые производителем ограничения»: на количество компонентов 100k (мы превысили уже в 5 раз);
на количество запросов в день 20k (у нас более 4 миллионов);
перестаем справляться с нагрузкой;
недостаточно гибкие политики очистки, приходится реализовывать свои инструменты.
В отличие от платной версии с доступом к внешнему PostgreSQL в качестве БД, в бесплатной версии используется embedded БД OrientDB (с версии 3.71.0 будет H2). Из этого следуют еще три проблемы:
сложно найти компетенции в довольно экзотической OrientDB и каждый раз вспоминаем все как в первый;
нет возможности нативно горизонтально масштабировать Nexus;
нет возможности просто сделать катастрофоустойчивость Nexus.
Также мы сделали вывод, что единое хранилище для пользовательских артефактов и для OCI-образов — не лучшее решение. Например, когда пользователи задэддосят Nexus из своей IDE каким-нибудь некачественным плагином, пострадает слой развертывания, потому что Kubernetes начнет испытывать проблемы при выкачивании образов. Кроме того, сейчас столкнулись с проблемой, что работу с образами нужно сделать катастрофоустойчивой, а с артефактами это не обязательно. Однако вынуждены думать и о том, и о том сразу, т.к. для всего используется один Nexus. Из-за этого мы размышляем над разделением пользовательских артефактов и образов на разные инструменты.
Правила разработки и организационные процессы
Оказалось, что наличия инструментов для организации работы недостаточно. Нужно было еще провести аудит того, как сейчас работают команды и как должны работать согласно требованиям банка.
Нам было необходимо понять:
какие контуры (окружения) есть в банке и их характеристики;
какие процессы разработки используются в командах;
какие технологии (flow) используются в командах разработки;
какая автоматизация есть у текущих команд разработки;
как устроена работа сопровождения;
правила версионирования и подписи артефактов;
каким образом производится доставка артефактов и передача между контурами (окружениями);
какие предусмотрены процессы согласования и прочая бюрократия для возможности запуска приложений;
каналы взаимодействия между командами;
принятые трекеры ведения задач;
существующий процесс контроля качества и безопасности кода/артефактов.
Посмотрели мы на все это и выяснили, что единственная регламентная вещь, которая соблюдается всеми — это контуры (окружения), которые закреплены в банке, а также правила работы в этих контурах.
Все остальное аспекты каждая команда разработки делала по-своему, контроля и стандартизации в масштабах банка не существовало.
Изучив процесс разработки некоторых команд, а также переиспользуя свой опыт и методологии разработки, мы начали с выбора Git Workflow.
Из существующих известных Git Workflow, которые повсеместно используются производственными командами (Git Flow, GitHub Flow, GitLab Flow), в качестве базовой методологии Git Workflow мы выбрали GitLab Flow.
Такой выбор был сделан по ряду причин:
GitLab Flow является достаточно простым в сравнении с Git Flow, но все-таки более формализованный, чем GitHub Flow;
в качестве инструмента для хранения кода и конвейера CI/CD используется Gitlab;
в производственном процессе используются контуры (окружения), например, Разработка, Интеграционное тестирование, Продуктив, что перекликается с концепцией GitLab Flow (Environment).
Исходя из специфики разработки в нашей компании, а также для функционирования конвейера CI/CD мы дополнительно сформулировали процессы разработки (flow), которых необходимо придерживаться производственным командам. По итогу у нас получилось 3 процесса:
Основной — по сути тот же GitLab Flow, но с регламентированными названиями веток и их привязки к банковским окружениям, применяемы для написания и выпуска сервисов (приложений);
Расширенный — расширение основного процесса для высококритичных систем, где добавлены дополнительные окружения для регрессионного и нагрузочного тестирования;
Упрощенный — процесс, применяемый по большей части для библиотек, где в качестве основной ветки используется master и отсутствует привязка к окружениям, по сути это схоже с GitHub Flow.
Подробности этих процессов будут приведены в следующих частях.
В документации мы зафиксировали выбранный процесс и правила создания и настройки проектов, работы с ветками, окружениями, merge-реквестами, тегами.
Все это дало нам фундамент для будущей автоматизации и CI-конвейерам, которые опирались на эти правила.
На этот момент в банке уже были выбраны тестовые команды, которые будут использовать наши инструменты. Они в разработке использовали преимущественно Java. Как позже выяснилось, более 50% команд разработки в банке используют именно Java.
Это было прекрасно, потому что совпало с тем, что мы сами использовали для написания своих сервисов. А мы на тот момент использовали Kotlin и у нас уже были наработки для реализации JVM-flow, который бы покрывал одновременно и наши, и пользовательские запросы.
К слову, все внедряемые процессы или технологии мы в первую очередь опробовали на себе, соответственно, если видели проблемы и неудобства в процессах, то модернизировали их. Так, например, появился Упрощенный процесс, о котором говорили выше.
На основе своего опыта мы понимали, что командам для эффективной продуктовой разработки нужно сосредоточиться на бизнес-задачах, а не на инфраструктурных проблемах. Поэтому нам хотелось предоставить для пользователя готовое CI-решение, которое подключалось бы к проекту словно plugin. Таким образом мы решили инкапсулировать CI-скрипты, базовые образы, настройку раннеров и т.д. Для этого мы создали ci/base проект, в котором спрятали все скрипты, а пользователю выдали следующие строки для подключения в его .gitlab-ci.yaml:
# Это буквально все содержимое файла .gitlab-ci.yml!
include:
- project: 'rshbintech/integrations/ckpr/ci-cd/base'
file: '/flow/ci-cdp/svc/jvm.yml'
Этого удалось достичь благодаря функциональности Gitlab, позволяющей делать include скриптов. Благодаря этому:
можем исправить и доработать CI-скрипты в одном месте;
можем исправить и доработать базовые образы в CI, например, обновить версию Java;
стандартизирован подход для проверки, сборки, верификации всех Java-проектов;
можем внедрить в CI новые шаги, например, DevSecOps, которые применятся автоматически у всех команд;
внедрили правила версионирования артефактов;
реализовали публикацию артефактов;
упростили жизнь командам: нет необходимости держать собственного инфраструктурного инженера для задач CI;
упростили жизнь саппорту: появился предсказуемый жизненный цикл проектов, артефактов, версий базовых образов и технологий используемых в них.
Этот подход мы назвали «подключаемые flow», более подробно о его устройстве и проблемах мы поговорим в следующих частях.
Далее мы задумались о площадке для взаимодействия с пользователями, а также получения обратной связи и информации о дефектах.
Здесь нам потребовалось организовать первую линию саппорта, которая по совместительству выполняла задачи SRE и занималась работами по улучшению observability и надежности внедряемых нами инструментов. В качестве инструмента мы далеко ходить не стали и выбрали Gitlab Issues.
Нативная интеграция с Gitlab экономила время, здесь мы организовали:
пользовательскую доску саппорта, где команды заводили issue;
платформенную доску, где мы вели свой бэклог и куда саппорт заводил уже обработанные задачи от пользователей (баги или фича-реквесты);
создали шаблоны задач для пользователей по разным направлениям;
ввели процессы обработки фича-реквестов для возвращения обратной связи пользователям.
Таким образом, у нас появилась единая точка входа, связывающая команды разработки и команду App.Farm.
Помимо продумывания и реализации процесса разработки продуктовых команд нам нужно было параллельно организовывать и собственный процесс разработки, т.к. наш отдел только образовался. Поэтому решения, которые мы предлагали для пользователей, в первую очередь применяли на себе. Если вам интересна тема подробного устройства нашего внутреннего процесса разработки с менеджерскими подробностями, то можем это раскрыть в рамках отдельной статьи.
К чему пришли
По итогу мы реализовали и внедрили CI-систему, которая в будущем обрела название App.Farm CI. Она состоит из следующих компонентов:
Gitlab в качестве хранилища кода;
Gitlab CI в качестве движка CI;
Kubernetes в качестве runners executor;
Nexus в качестве хранилища артефактов;
процессы разработки на базе Gitlab Workflow;
базовый набор CI подключаемых CI-flow.
На тот момент нам удалось успешно перевести разработку тестовых команд на App.Farm CI, а сейчас все остальные команды планомерно переходят на использование App.Farm CI.
В следующих частях мы расскажем более подробно о собственных технических решениях, а также где пришлось выполнить доработки, чтобы ответить на вопрос: «Какая еще платформа, вы же просто установили Gitlab?!».