App.Farm CI. Часть IV. Реализуем flow — шагаем к автоматизации разработки

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

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

79741f044b2ea722d83b17ab6c358361.jpeg

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

  • Разновидности flow

  • Базовый набор job

  • Генерация Dockerfile

  • Проверка качества кода

  • Сборка и публикация артефактов

  • Развертывание

  • Демонстрационные примеры

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

Разновидности flow

В контексте App.Farm CI »flow» — это набор Gitlab CI конфигураций, стадий и заданий для автоматизации разработки под определенный технологический стек.

Например, у нас есть команда разработки, которая создает микросервисы на Java используя пакетный менеджера maven. Этой команде необходимо выполнять сборку, публикацию и развертывание артефакта. Для такой команды мы реализуем набор скриптов, обеспечивающих всю требуемую бизнес-логику автоматизации, и упаковываем в подключаемый файл — ci-cdp/svc/jvm.yml. Это и есть flow в нашей терминологии.

В нашем случае flow как минимум разделяются на технологические стеки. Сейчас для команд внутренней разработки мы предоставляем следующий перечень flow:

  • Java/Kotlin

  • C#/.NET

  • Angular

  • React

  • Python

  • NodeJS

  • Golang

  • 1С: Предприятие

Для вендорских приложений, которые вендор тиражирует не в виде исходного кода, а в виде артефактов, доступны flow:

  • JVM исполняемые артефакты

  • Python исполняемые артефакты

  • .NET исполняемые артефакты

  • Frontend исполняемые артефакты

  • OCI-образ

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

fa1981d1022006374a4257e3cff4c1f8.png

Дополнительно flow могут иметь несколько разновидностей в рамках одной технологии. Мы реализовали следующие разрезы:

  • Пакетный менеджер. Для каждого сборщика мы готовим разный flow, т.к. они различаются базовыми образами, Dockerfile’ом, правилами верификации, требованиями к оформлению проекта и манифеста. Например, для JVM поддерживается maven и gradle в качестве пакетного менеджера. Для каждого пакетного менеджера мы готовим отдельный подключаемый файл с flow.

  • Доставка/развертывание. В зависимости от типа проекта или бизнес-потребности пользователю нужно либо доставить собранный артефакт в хранилище, либо выполнить развертывание артефакта в среде исполнения. Подобные flow мы разделяем на CDL и CDP: Delivery и Deploy соответственно. В первом случае мы собираем артефакт и публикуем в Nexus, на этом работа конвейера заканчивается. Во втором случае артефакт вообще может не публиковаться, он будет упакован в OCI-образ, опубликован в registry и развернут в среде исполнения.

  • Библиотека/Сервис. В зависимости от способа организации исходного кода у каждой команды могут быть проекты — библиотеки и сервисы. Библиотеки используются как зависимости в исполняемых сервисах. Для библиотеки конвейер предполагает сборку и публикацию артефакта в хранилище артефактов. Ключевое отличие этих flow в том, что для библиотеки используется упрощенный git workflow, где не предполагаются ветки под окружения разработки. Для сервиса же используется полноценный git workflow, где выполняется жизненный цикл релиза с перетеканием из окружений разработки в продуктив. Отсюда также возникают различные требования к оформлению проектов.

  • Тип сервиса. Актуально для проектов с исполняемыми сервисами. Мы разделяем сервисы на следующие типы:

    • простой сервис — один собираемый артефакт, который и является исполняемым backend/frontend;

    • многомодульный сервис — несколько собираемых артефактов, где один исполняемый backend/frontend и несколько библиотек;

    • SSR — сервис, выполняющий Server-Side Rendering, по сути объединяет роли backend и frontend.

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

    • документация — flow для сборки документации. Мы поддерживаем сборку и развертывание сайта с документаций, а так же рендеринг документации из Markdown-разметки в классические-бюрократические форматы формата .docx, для согласования документов внутри компании;

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

    • все остальное — все прочие назначения, когда мы уже не погружаемся в бизнес-смысл проекта.

  • Платформенный/Пользовательский. Мы явно разделяем flow, которые используем для разработки самой платформы, и которые предоставляем пользователям. Для платформенных flow могут быть упрощения или допущения в более низкоуровневую инфраструктуру, что недопустимо для пользовательских flow. Также отличаются окружения, в которых используются эти flow, например, для пользовательских flow повышенные требования к проверкам безопасности и используемым компонентам.

Иерархия CI-конфигураций
Иерархия CI-конфигураций

Если вы попытаетесь скомбинировать описанное количество разрезов с количеством поддерживаемых технологий, то сможете представить количество flow, которые мы поддерживаем. На самом деле мы не сразу делаем весь набор перечисленных выше разрезов под каждую технологию, а действуем по запросу. Например, завели фича-реквест, что нужен e2e для .NET-flow — делаем.

А чтобы представить было легче, мы назовем, сколько же у нас на данный момент различных flow. 68 — столько flow на текущий момент реализовано в App.Farm CI.

03168a475f02f9b6924bcb44ce746506.png

При реализации каждого нового flow мы всё время думаем: как было бы круто, если бы в компании приняли в качестве стандарта одну или ограниченный ряд технологий, чтобы не распыляться на все в мире. Наращивать экспертизу и компетенции в выбранных стеках, обеспечить наивысшее качество производства приложений. Например, все пишут на Golang и всё тут. Такой опыт есть в других IT-компаниях, разрабатывающих подобные платформы. Однако это все не наш случай, потому что специфика нашей компании — наличие множества подрядчиков, помимо внутренней разработки, которых не подведешь под одну технологию.

Базовый набор job

Каждый flow имеет скелет — набор CI Job, из которых строится конвейер. В зависимости от разновидности flow набор job может меняться, например, для библиотек отсутствует job — deploy (развертывание в кластере). Но всё же есть набор базовых заданий, которые используются повсеместно в конвейерах. Рассмотрим их подробнее:

  • Статическая верификация. Шаг, который всегда идет первым, это статическая верификация проекта на соответствие требованиям этого flow. Подробно об этом инструменте мы рассказывали в предыдущей части. По сути это фейс-контроль, который допускает пользовательский проект к использованию конвейера.

  • Генерация Dockerfile. На этом шаге мы генерируем Dockerfile, это необходимо, чтобы взять под контроль Dockerfile, инструкции в котором маппятся под логику в CI-конфигурации и дальнейшие шаги в конвейере. Создание собственного Dockerfile в пользовательских проектах запрещено, подробно об этом мы рассказывали в предыдущей части.

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

  • Проверка информационной безопасности. Проверки ИБ внедряет в конвейер отдел DevSecOps, который по сути контрибьютит в разработку App.Farm CI. Здесь применяются различные проверки как исходного кода, так и артефактов, в том числе OCI-образов.

  • Тестирование. В шаге тестирования у нас поддерживаются unit-тесты и e2e-тесты. Вызов Unit-тестов мы предоставлем в обязательном порядке, а шаг с e2e-тестами мы реализуем уже по запросу команд разработки, если в этом есть потребность.

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

  • Публикация артефакта. На этом шаге выполняем публикацию артефакта. Для Delivery-flow это значит положить собранный артефакт в хранилище артефактов, в нашем случае Nexus. Например, в качестве артефакта может быть jar-файл. В случае с Deploy-flow это публикация итогового OCI-образа с исполняемым артефактом в registry, в нашем случае это тоже Nexus.

  • Развертывание. Шаг, выполняющий развертывание приложения в среде исполнения.

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

Пример конвейера для Kotlin-приложения
Пример конвейера для Kotlin-приложения

Генерация Dockerfile

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

  • Управление базовыми образами: каждый Dockerfile основывается на базовом образе, который мы тщательно готовим и проверяем через отдел ИБ. В том числе мы можем централизованно обновлять базовые образы при необходимости.

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

  • Использование оптимизаций и лучших практик: например, использовать Multi-stage build.

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

1f0c3a6c49d32a7f77b9a390f34041c9.png

Для генерации Dockerfile мы не стали искать готовых решений, потому что написание собственной утилиты выглядело дешевле, чем ресерчить и адаптировать что-то готовое. Мы написали CLI-утилиту, которую затем вызываем в шаге конвейера dockerfilegen непосредственно перед сборкой. Эту утилиту реализовали на Golang, а ее архитектура была выстроена вокруг шаблонизации Dockerfile.

Мы взяли готовые Dockerfile, которые были у нас в распоряжении, и конвертировали их в gotmpl-шаблоны. А также внедрили в эти шаблоны логику на базе CI-переменных, которые формируются в зависимости от flow и прокидываются в шаг dockerfilegen.

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

Помимо рендеринга утилита дополнительно проверяет наличие Dockerfile в проекте, а если находит его, то выдает ошибку, т.к. у нас запрещены пользовательские Dcokerfile. Кроме Dockerfile утилита генерирует еще и .dockerignore, его генерация выполняется на базе .gitignore, наличие которого уже обязательно в проекте и пользователь должен его описать.

Вот так выглядит подключение генерации Dockerfile в CI:

# Базовое расширение, подключаемое в каждой технологии
.dockerfilegen:
  image:
    name: $IMAGE_NAME__DOCKERFILEGEN:$IMAGE_TAG__DOCKERFILEGEN
  artifacts:
    paths:
      - Dockerfile
      - .dockerignore
    expire_in: 1 week
  script:
    - /dockerfilegen

---

# Подключение шага для JVM-стека
dockerfilegen:
  extends:
    - .dockerfilegen
  stage: dockerfilegen
  variables:
    DOCKERFILE_GEN_STACK: jvm-image

Выбор шаблона осуществляется указанием CI-переменной DOCKERFILE_GEN_STACK. В зависимости от ее значения CLI-утилита выбирает нужный gotmpl-шаблон и выполняет рендеринг: создает файлы Dockerfile и .dockerignore, которые переиспользуют следующие шаги конвейера.

Код генерации умещается в пару строк:

func (g *Generator) generateDockerfile(stack string, templateVars TemplateVariables) error {
	dockerfileTemplate := g.buildPathToEmbedTemplate(stack)

	template, err := NewTemplate(stack, dockerfileTemplate)
	if err != nil {
		return err
	}

	data, err := template.Render(templateVars)
	if err != nil {
		return err
	}

	return g.save(g.config.Output, data)
}

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

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

Артефакты задания dockerfilegen
Артефакты задания dockerfilegen

Проверка качества кода

Еще один из шагов конвейера, на котором хотелось бы остановиться подробнее, это проверка качества кода. Так как мы работаем в крупной финтех-компании, у нас есть повышенные обязательства к качеству итоговых продуктов и сервисов. Один из первоначальных шагов по обеспечению этого качества — наведение порядка в кодовой базе. Сюда мы относим следование Code Conventions для конкретной технологии, Best Practices, базовым стандартам безопасности, а также борьбу с потенциальными уязвимостями и Code Smells.

d346554616c8172ddd7a230e65f7c784.png

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

Какие преимущества использования SonarQube мы выявили:

  • Улучшение качества кода за счет встроенных проверок SonarQube.

  • Уменьшение уязвимостей в коде за счет встроенных проверок в SonarQube.

  • Единый стандарт к написанию кода: если подрядчик использует на своем производстве SonarQube, то его код без проблем залетает в наш CI.

Несмотря на все эти преимущества, есть и минусы: в связи с ростом числа пользователей на платформе мы столкнулись с ограничением Community-версии на масштабирование SonarQube. Текущий инстанс уже не способен переварить все проекты, за счет чего образуется очередь и, как следствие, подвисает конвейер. Также периодически возникает проблема, что один проект может утилизировать все ресурсы SonarQube. Было несколько инцидентов, когда сборка какого-нибудь фронтенда с огромной кодовой базой и несколькими запущенными в параллель конвейерами полностью забивала очередь сканирования SonarQube.

Мы рассматриваем осуществить шардирование SonarQube, выполнив развертывание нескольких инстансов под какой-то критерий. В качестве критерия может быть подразделение, команда, продукт. Либо просто выполнить развертывание нескольких независимых инстансов с установкой прикладного балансировщика перед ними.

В результате выполнения сканирования пользователю формируется ссылка на SonarQube, где он может посмотреть отчет о выполнении проверки. В случае, если сканирование выполнено неудачно, например, не пройдет QualityGate или обнаружены issues, по уровню критичности не позволяющие продолжать выполнение, то в таком случае конвейер прерывается.

d68fc92d01daee5d6990a560af8b7a56.png

Помимо SonarQube мы используем также линтеры, которые приняты как стандарт в комьюнити той или иной технологии. Например, для Golang-flow мы используем golangci-lint — https://github.com/golangci/golangci-lint. Этот линтер подразумевает включение пресетов — наборов проверок, которые будут выполняться. Но в инструменте нет возможности гибкой настройки, например, если не прошел один пресет — сломай конвейер, а если не прошел другой пресет — покажи ворнинг.

Мы разделили выполнение линтера на 2 задания: с обязательными пресетами и рекомендуемыми. И при внедрении нового пресета мы сначала выполняем его в «рекомендательной» джобе, уведомляем пользователей, о наличии периода на исправление, а по его окончанию переводим этот пресет из «рекомендательных» в «обязательные» . Суть в том, что «обязательные» проверки ломают конвейер в случае не прохождения, а «рекомендательные» лишь выдают предупреждение.

Конвейер для приложения на Golang, в котором не выполнились рекомендательные проверки линтера
Конвейер для приложения на Golang, в котором не выполнились рекомендательные проверки линтера

С точки зрения конфигурации CI это обеспечивается механизмом allow_failure, в случае «рекомендательных» проверок допустимо окончание заданием с ненулевым кодом. При этом «рекомендательная» джоба появляется в конвейере только тогда, когда у нас заданы такие опциональные проверки:

.rules_short_base:
  rules: &rules_short_base
    - if: '($GOLANG_OPTIONAL_LINT_PRESETS != null && $GOLANG_OPTIONAL_LINT_PRESETS != "") && $CI_JOB_NAME == "lint_optional" && $CI_JOB_STAGE == "lint"'

Заполнение GOLANG_OPTIONAL_LINT_PRESETS регулируется в настройках CI-переменных на головной группе проектов в Gitlab. Таким образом без пересборки проектов мы можем регулировать поведение линтера.

Сборка и публикация артефактов

В каждой технологии сборка осуществляется пакетным менеджером, который принят как стандарт и является официально рекомендованным. В случае, если существует несколько пакетных менеджеров для одной технологии, то мы разводим их по разным flow, чтобы не смешивать логику их работы. Сборку мы делаем на базе Dockerfile с помощью Buildkit —https://github.com/moby/buildkit. Подробно об архитектуре сборки в целом мы описывали в предыдущей части.

65c51a6c4ef92584547c7ebb7ccdb75f.png

В первую очередь для сборки мы готовим базовый образ с тулчейном и пакетным менеджером, который будем применять. За основу обычно берется рекомендованный базовый образ в комьюнити, например, для сборки JVM на maven мы берем за основу образ https://hub.docker.com/_/maven. Обычной практикой у нас считается предварительно обернуть базовый образ: мы делаем промежуточный проект, пишем Dockerfile, где используем публичный базовый образ. А во flow в качестве базы уже используем нашу обертку.

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

После того как базовый образ подготовлен, на его основе пишется Dockerfile:

FROM ${IMAGE_NAME__MAVEN}:${IMAGE_TAG__MAVEN} as deps
WORKDIR /app
COPY pom.xml .
RUN mvn	dependency:resolve -DskipTests

FROM deps as build
COPY src src
RUN mvn clean compile -DskipTests

FROM build as package
RUN mvn package -DskipTests

Для сборки мы обычно делаем стадии deps и build, в первой скачиваем зависимости, во второй на их основе выполняем сборку. Благодаря кэшированию в Buildkit не выполняем повторное скачивание зависимостей, а переиспользуем их. В зависимости от технологического стека может быть только стадия build или build + package как в данном случае. В JVM в build мы компилируем классы, а сам артефакт собираем в package. Это разделение сделано умышленно, чтобы внедрить сканирование SonarQube в конвейере между заданиями build и package, потому что для более качественного анализа SonarQube помимо исходного кода сканирует в том числе скомпилированные классы.

Для случаем, когда flow подразумевает публикацию собранного артефакта в хранилище артефактов у нас добавляется стадия publish:

FROM package as publish
RUN mvn deploy -DskipTests

В качестве хранилища артефактов мы используем Nexus. Отличный условно бесплатный комбайн, который поддерживает все возможные виды артефактов, а если не поддерживает явно какой-то тип репозитория, то для этого существуют generic или RAW репозитории. В предыдущей части мы подробно рассматривали преимущества и недостатки Nexus.

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

На этом этапе мы уже собрали артефакт и опубликовали его в хранилище.

Развертывание

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

По аналогии с базовым build-образом мы также опираемся на общепринятые рекомендации в сообществе, а именно какой runtime-образ следует использовать. Например, для JVM — это https://hub.docker.com/_/eclipse-temurin. Дополнительно к этому стараемся максимально облегчить рантайм, чтобы в образе не было ничего лишнего, тем самым минимизируем риски по эксплуатации различных уязвимостей.

С точки зрения Dockerfile добавляется слой runtime, где мы копируем собранный артефакт из сборки. Только артефакт, без исходников, бойлерплейта сборки и прочего. В случае с JVM — это исполняемый uber-jar.

FROM ${IMAGE_NAME__JRE}:${IMAGE_TAG__JRE} as runtime
COPY --from=package /app/target/demo-backend.jar /app
USER app
CMD exec java -jar demo-backend.jar

Обращая внимание на этот Dockerfile можно подметить несколько особенностей, которые выражаются у нас в требованиях к проекту:

  • Запускается только один исполняемый артефакт. Придерживаемся правила: один Gitlab-проект = один исполняемый артефакт = один процесс в рантайм-контейнере.

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

a176d0eacd6f7182e4673df7f05321ec.png

Развертывание выполняет шаг deploy конвейера, в рамках которого создаются манифесты в Kubernetes, где скачивание и развертывание собранного образа делегируется контроллеру Kubernetes.

Что касается самой процедуры развертывания: для нее мы используем наш deploy-toolchain — это набор различных компонент, обеспечивающих развертывание в среду исполнения Kubernetes. Тулчейн состоит из helm-чартов, gotmpl-шаблонов, values с конфигурациями и множеством bash-скриптов, склеивающих это между собой.

Это базовый набор инструментов обычно использующихся для целей развертывания, но с ростом количества flow стало сложно развивать весь этот набор и управлять им. Сейчас мы задумывается над апгрейдом этого инструментария в пользу https://fluxcd.io/. Подробнее о нашем инструментарии развертывания, его преимуществах и недостатках мы расскажем в будущем цикле статей.

Демонстрационные примеры

Разработка любого flow сопровождается его непосредственным прототипированием и отладкой. Для этого у нас на верхнем уровне в Gitlab заведена специальная группа проектов»Демонстрационные примеры», где созданы группы для каждой из технологий. Во время разработки flow мы создаем тестовый приватный проект, на котором обкатываем новый flow. В качестве примеров мы стараемся использовать не просто стабы, а написать какую-то логику, чтобы на ней продемонстрировать работу сервиса.

Например, при реализации JVM примера Spring-Boot приложения сделали TODO-List, и на его примере продемонстрировали как правильно описывать application.yml, как описывать management-эндпоинты, как формировать OAS-спеку и выставлять Swagger UI, как делать правильный конфиг логирования, продемонстрировали описание REST API, репозиториев и т.д.

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

После реализации демо-примера и отладки flow на нем, мы открываем доступ к нему, чтобы каждый сотрудник мог воспользоваться им как эталоном. В пользовательской документации мы выполняем описание flow и используем отсылки к демо-примеру, чтобы показать реальные кейсы использования. Стараемся примеры делать так, чтобы их можно было использовать как quick-start, как каркас для своего будущего приложения: клонируешь демо-проект, пишешь свою бизнес-логику в коде, коммитишь, выполняется конвейер и PROFIT!

Группа с демонстрационными проектами
Группа с демонстрационными проектами

Таким образом мы прошли наш стандартный путь от создания flow до его публикации. Пользователю остается лишь склонировать готовый пример или подключить flow в своем проекте:

# Пример содержимого .gitlab-ci.yml для подключение JVM-flow
include:
  - project: 'rshbintech/integrations/ckpr/ci-cd/base'
    file: '/flow/ci-cdp/svc/jvm.yml'

На этом шаге наш flow готов! Мы реализовали CI-конфигурацию, разработали OCI-образы, Dockerfile, описали требования и инструкции, написали демо-пример и передали flow пользователям в эксплуатацию.

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

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

  • Как мы создавали PaaS‑платформу App.Farm

  • App.Farm CI. Часть I. Проблемы и выбор решений

  • App.Farm CI. Часть II. Конвейер — швейцарский нож: особенности запуска мультиарендного процесса разработки

  • App.Farm CI. Часть III. Подготовка к реализации flow — быть вахтером или не быть?

© Habrahabr.ru