Привет, Хабр! На связи команда разработки App.Farm в РСХБ-Интех. Хотели бы представить вам следующую часть цикла статей об App.Farm CI, одной из подсистем нашего продукта — PaaS App.Farm.

App.Farm — платформа по типу PaaS для стандартизации процесса разработки бизнес-приложений: от хранения исходного кода до запуска сервисов. App.Farm CI — подсистема, обеспечивающая хранение кода, артефактов, автоматизацию сборки.

Предыдущие части:

Какие темы затронем в этой части:

  • Gitlab для процесса разработки

  • Структура компании

  • Базовые настройки проектов

  • Документация

  • CI как сервис

Что не затронем: технический процесс развертывания Gitlab, Gitlab CI и прочих инструментов.

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

Gitlab для процесса разработки

Ранее мы остановились на выборе Gitlab и Gitlab CI для стандартизации и автоматизации процесса разработки. Для избежания хаоса при предоставлении этого инструментария пользователям недостаточно просто установить выбранные инструменты. Необходимо их настроить, адаптировать под организационные процессы, придумать правила для работы с ними.

Рассмотрим для начала базовые возможности Gitlab, которые нам пригодились для его внедрения в процесс:

  1. Интеграции с SSO-провайдером. Благодаря этому удалось организовать аутентификацию пользователей с помощью доменных учетных записей.

  2. Ролевая модель. Благодаря ей мы выделили группу администраторов Gitlab, которые могут управлять всеми проектами. А пользователи уже адресно добавляются в свое пространство при онбординге.

  3. Группы проектов. Поддержка групп позволила сразу сформировать группы под существующие в банке системы и разместить там все проекты их микросервисов. При этом владелец системы получал полномочия администратора группы и далее уже самостоятельно управлял доступами внутри группы на конкретные проекты.

  4. Иерархия групп. Поддержка иерархии дала возможность заранее создать на верхнем уровне группы под различные команды разработки.

  5. Доска задач. В каждом проекте Gitlab есть возможность сделать issue-board. Мы не стали делать доску под каждый проект, а сделали отдельный проект — доска задач, в котором нет исходного кода, а используется только issues. Таких досок мы сделали несколько, например, для первой линии поддержки (пользовательские запросы), для разработки (запросы от саппорта), для безопасников и т.д. Доска позволяет организовать процесс работы по спринтам, а с помощью лейблов можно фиксировать проекты, производственные этапы, роли и т.д.

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

Это базовый набор функциональности, который позволил с нуля быстро организовать как собственный производственный процесс, так и быть отправной точкой для онбординга других команд разработки. Кроме размещения исходного кода требовалась автоматизация разработки начиная от проверок кода, заканчивая развертыванием в среде исполнения. В качестве CI-конвейера мы выбрали Gitlab CI, потому что это нативно интегрируемое решение в Gitlab.

Рассмотрим базовые возможности Gitlab CI, которые нам пригодились на старте для автоматизации разработки:

  1. Унаследование CI-переменных. Благодаря этой возможности мы смогли сделать различные настройки CI и разместить их на вышестоящих группах Gitlab, которые соответствовали компании. Доступ к этим группам имеют только администраторы Gitlab и могут видеть и менять настройки. А использовать эти настройки могут уже все нижележащие проекты по иерархии. Так, например, мы можем задавать различные базовые образы, их версии, прокси, различные хосты и т.д.

  2. Подключаемые CI-скрипты. Так как мы еще только придумывали, как будет выглядеть конвейер и из каких шагов состоять, нам нужно было предусмотреть возможность развивать и дорабатывать CI-скрипты без изменения пользовательских проектов. И другая сторона — нам не хотелось бы, чтобы несколько команд пользователей писали свой Java CI Flow, дублируя друг друга. Поэтому мы реализовали отдельный проект, где написали базовый CI, а пользователи ссылаются на него из своего файла .gitlab-ci.yml.

  3. Kubernetes executor. Для развертывания всего стека платформы мы по умолчанию принесли Kubernetes, поэтому наличие в Gitlab CI Kubernetes Executor позволило для раннеров использовать эту же среду исполнения.

По сути стек Gitlab + Gitlab CI + Kubernetes — это универсальный швейцарский нож, который нам позволил в кратчайшие сроки организовать мультиарендный процесс разработки. Конечно, впоследствии было много доработок и низкоуровневых оптимизаций, но все это неизбежно с ростом количества пользователей. Спустя 5 лет эта связка до сих пор успешно обслуживает наших пользователей и множество команд разработки.

Структура компании

Одно из наших нововведений в организацию производственного процесса — это отражение организационной структуры компании в инструментарии (в Gitlab, Nexus, Vault и т.д.).

Несколько причин, почему мы об этом задумались:

  1. В процессе работы мы встречали артефакты производства других команд, например, pom.xml или jar-файлы, в манифестах которых группа артефакта была: ru.company.backend. Это буквальный пример. А что, если таких бэкендов в компании тысячи, как их размещать и хранить в корпоративном maven-репозитории? Поэтому нам нужно решить проблему мультиарендности: в артефакте должна быть отсылка к компании, подразделению, продукту. И это касается не только JVM стека.

  2. В продолжение первого пункта: необходимость организации доступа таким образом, чтобы команды имели доступ только к своим проектам, артефактам, секретам.

  3. Унаследование доступа: мы предполагали, что будет удобно создать роль владельца подразделения с предоставлением прав на подразделение и эти права унаследуются на всю иерархию этого подразделения! Права владельца блока унаследуют доступ ко всем подразделениям в этом блоке и так далее по цепочке.

  4. Как администраторы мы хотели быстро ориентироваться в иерархии проектов и находить адресно все проекты нужного подразделения. Это бы облегчало саппорт.

  5. Желание навести порядок и структурировать все проекты в масштабах компании по продуктам/подразделениям/блокам/департаментам. Это казалось логичным, да и вообще было бы киллер-фичей.

Первым шагом мы нарезали структуру подразделений в Gitlab: сделали группы под компании, а внутри них воспроизвели иерархию из групп в соответствии с оргструктурой. А в конечные группы, в нашем случае это подразделения, разместили продукты — это уже бизнес-системы. Забегая вперед: для контроля манифестов мы реализовали утилиту статической верификации, которая выполняется первым шагом конвейера и для каждого стека проверяет манифест на соответствие иерархии проекта в Gitlab. Эта утилита позволила нам избежать игнорирования требований со стороны команд и соблюсти обязательность их исполнения.

Например, у нас есть продукт, в котором есть backend сервис на Java - importer. Продукт принадлежит отдельному Центру компетенций, а полная иерархия: Организация/Блок/Центр компетенций. Буквально в такой иерархии в Gitlab будет расположен проект importer. А в его манифесте должен быть указан такой groupId: ru.company.depart.product.

Ещё одним приятным эффектом этого решения стало упорядочивание артефактов в Nexus в соответствии с оргструктурой.

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

  1. В нашей компании очень часто меняется оргструктура: отделы переименовываются, упраздняются, масштабируются и делятся на части. Все это сотрудники саппорта должны отслеживать и подстраивать платформенный Gitlab. Особенно сложной задачей является отслеживание этих структурных изменений, потому что не существует API в отдел кадров, куда бы можно было подписаться на изменения. Что касается самой миграции проектов, её сложность зависит от размера продукта и количества ее микросервисов.

  2. Бывают случаи передачи продукта от одного подразделения другому или от компании-подрядчика во внутреннюю разработку. В этом случае предстоит сделать все те же действия по переносу проекта, как и при изменении оргструктуры.

  3. Не существует «владельцев подразделений». Ну точнее они есть, но это управленцы, которые не работают в Gitlab с проектами, поэтому отпадает необходимость в унаследовании прав на иерархию проектов. Практика показала, что владелец продукта — это самый высокий уровень доступа.

Для решения этих проблем сейчас мы рассматриваем переход к «плоской» структуре проектов, где вышестоящая группа иерархии — это продукт. А оргструктуру отражать в метаинформации к продукту. Но это лишь планы, пока живем так, потому как текущая структура проектов до сих пор кажется логичной, несмотря на эти проблемы.

Базовые настройки проектов

Напомним, что для нашей PaaS из существующих известных Git Workflow, которые повсеместно используются производственными командами (Git Flow, GitHub Flow, GitLab Flow) в качестве базовой методологии мы выбрали GitLab Flow.

Этот flow разработки предполагает определенные правила, например, наличие определенного набора базовых веток на проекте, например, master, rc, production — в соответствии с окружениями разработки. Правила именования веток под доработки, например, feature/*, bugfix/*, hotfix/*. А также семантические правила именования тегов.

Но, если мы просто объявим пользователям, что теперь необходимо использовать определенный flow разработки, то они это не прочитают, не увидят и не создадут этот обязательный набор веток на своем проекте, что приведет к сбою в работе CI-конвейера. Как и при введении требования на определенную структуру проектов в Gitlab, мы решили автоматизировать конфигурацию проектов в Gitlab. Для этого мы использовали паттерн реконсиляции: приведение действительного к желаемому. Реализовали мы это с помощью специальной утилиты, которая позволила декларативно задать конфигурацию проектов и желаемый набор базовых веток, а также настройки эти веток. Кроме того, инструмент позволил задекларировать набор технических CI-переменных на головных группах.

# Пример настройки веток в config.yaml
projects_and_groups:
  "*":
    project_settings:
      visibility: private
      public_jobs: false
      builds_access_level: private
      only_allow_merge_if_pipeline_succeeds: false
      only_allow_merge_if_all_discussions_are_resolved: true
    branches:
      "*":
        protected: true
        unprotect_access_level: 0
        push_access_level: 0
        merge_access_level: 0
      "master":
        protected: true
        unprotect_access_level: 0
        push_access_level: 0
        merge_access_level: 30
    tags:
      "*":
        protected: true
        create_access_level: 0
      "*.*.*":
        protected: true
        create_access_level: 40

Инструмент выполняет периодический обход всех проектов в Gitlab и приводит их состояние к указанной конфигурации. Здесь мы получаем дополнительное преимущество — откат несанкционированных пользовательских настроек на своем проекте. Например, пользователь на проекте включил разрешение коммитить напрямую в master, мы это откатим. Можно сказать, что это один из первых шагов в нашей концепции «всё есть код», теперь мы можем декларативно управлять настройками пользовательских проектов.

Недостатки этого инструмента:

  1. Периодический запуск. При создании нового проекта он приводится в желаемое состояние только после того, как отработает инструмент.

  2. Медленная работа при большом количестве проектов в условиях энтерпрайза.

  3. Отсутствие возможности расширения. При необходимости исправления или доработки требуется делать fork.

В последнее время мы все чаще задумываемся о замене этого инструмента, например, написании собственногоgitlab-operator.

Так мы подготовили конфигурацию всех проектов в соответствии с требованиями нашего gitflow и теперь можем опираться на это при написании CI-скриптов.

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

Документация

Любой организационный процесс должен сопровождаться документацией. В первую очередь мы задались вопросом: где размещать документацию? Мы не могли разместить ее непосредственно в Wiki Gitlab, потому что получали «курино-яичную» проблему: чтобы узнать как работать с Gitlab — нужно попасть в Gtilab. Другими словами: информация о получении доступа к Gtilab, корпоративные сертификаты, которые требуется установить, как сделать свое пространство в соответствии с оргструктурой — это все тоже документация.

На тот момент у нас уже была среда исполнения — Kubernetes, где были развернуты как инфраструктурные компоненты, так и некоторые наши прикладные сервисы. У нас также была возможность получить DNS имя и сделать доступным сервис в корпоративной сети банка. Все это навело нас на мысль сделать документацию как сайт, который разворачивается в кластере, и пользователи могут зайти на него и получить информацию.

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

  1. Не использовать проприетарные, закрытые, либо платные инструментов, например, Confluence.

  2. Текстовый формат, изменения которого можно было бы сравнивать в git diff.

  3. Наличие облегченной разметки для форматирования.

В качестве формата текста мы выбрали Markdown. Он решал все поставленные задачи, он абсолютно простой, не нужно обладать какими-то экстраординарными навыками, чтобы писать на нем текст. Существуют различные wysiwyg редакторы для Markdown. Кроме того, Markdown является базовым форматом в самом Giltab, он используется в описаниях задач или комментариях. Да и в README проектов тоже используется он.

После выбора формата текста нужен был инструмент для рендеринга документации из MD-формата в сайт. Мы рассматривали различные инструменты, но остановились на docsifyhttps://docsify.js.org/. Мы выделили следующие его преимущества:

  • Простой в установке и настройке.

  • Генерация страниц на лету, без статических html-файлов.

  • Большое количество плагинов.

  • Просто расширяется, если умеешь в JS.

  • Полнотекстовый поиск.

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

Параллельно с автоматизацией сборки и развертывания документации мы не забыли написать и саму документацию. Зафиксировали базовую информацию:

  • Дали определение платформы и ввели глоссарий, чтобы разговаривать с пользователями на одном языке.

  • Зафиксировали инструкции по получению доступов и настройки окружения.

  • Описали процесс сопровождения и привели контакты ответственных лиц.

  • Описали набор компонентов и UI, с которыми может работать пользователь.

  • Зафиксировали выбранный нами процесс разработки — git workflow, дали определение, привели схемы и примеры работы над проектами в команде.

  • Описали требования и правила по размещению проектов исходного кода.

Пример страницы нашей документации
Пример страницы нашей документации

Так мы продвинулись еще на один шаг к предоставлению пользователям сервиса App.Farm CI. При этом даже при написании документации сохранили свою концепцию «всё есть код»: разработка документации живет по тем же правилам, как и любой backend или frontend сервис!

CI как сервис

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

  • Создать проект и оформить его в соответствии с требованиями.

  • Разместить исходный код.

  • Создать в корне проекта .gitlab-ci.yml, в котором подключить один из готовых flow.

Такова была наша задумка. И дальше нам следовало определить, а какие flow необходимо реализовать? Мы провели research в организации, чтобы понять, на чем преимущественно здесь пишут команды, а также определили первых первопроходцев — команды, которые станут пилотными для использования нашего CI. Таким образом появился рейтинг технических стеков, в котором лидирующее место с большим отрывом занимал JVM (Java/Kotlin; maven/gradle).

В качестве первого flow был выбран jvm-maven. Теперь перед нами стояла задача придумать следующее:

  • Определить список шагов конвейера для этого flow.

  • Разработать базовый образ для сборки (далее как следствие еще и runtime).

  • Разработать скрипты для каждого stage.

  • Учесть при этом оптимизацию сборки: multistage building и кэширование слоев.

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

Пример JVM-пайплайна на feature-ветке
Пример JVM-пайплайна на feature-ветке

В следующих частях мы расскажем более подробно о том, как мы это делаем. В фокусе внимания будет то, как мы реализуем ci-flow под конкретную технологию, какие этапы предусматриваем и с какими проблемами сталкиваемся.

Комментарии (1)


  1. m_sinelnikov
    20.02.2025 15:31

    Спасибо за статью, вопрос "Какие проблемы возникли при отражении организационной структуры компании в инструментах разработки, и какие шаги команда рассматривает для улучшения структуры проектов в GitLab в будущем?"