Тема монорепозитория обсуждалась уже не раз и, как правило, вызывает весьма активные споры. Создавая werf как Open Source-инструмент, призванный улучшить процессы сборки кода приложений из Git в Docker-образы (и их последующей доставки в Kubernetes), мы мало размышляем на тему того, какой выбор лучше. Для нас первично обеспечить всё необходимое для сторонников разных мнений (если это не противоречит здравому смыслу, конечно).
Появившаяся недавно поддержка mono-repo в werf — хороший тому пример. Но для начала давайте разберёмся, как эта поддержка вообще связана с использованием werf и при чём здесь Docker Registry…
Проблематика
Представим такую ситуацию. В компании имеется множество команд разработчиков, занимающихся независимыми проектами. Большинство приложений функционируют в Kubernetes, а соответственно — контейнезируются. Для хранения контейнеров, образов, необходим реестр (registry). В качестве такого реестра в компании используется Docker Hub с единственным аккаунтом
COMPANY
. По аналогии с большинством систем хранения исходного кода, Docker Hub не позволяет создавать вложенную иерархию репозиториев, такую как COMPANY/PROJECT/IMAGE
. В таком случае… как же с этим ограничением хранить в реестре немонолитные приложения, не создавая отдельный аккаунт под каждый проект? Возможно, описанная ситуация кому-то не понаслышке знакома, но давайте рассмотрим вопрос организации хранения приложений в общем, т.е. без привязки к вышеописанному примеру и Docker Hub.
Пути решения
Если приложение монолитно, поставляется в одном образе, то вопросов не возникает и мы просто сохраняем образы в реестр контейнеров проекта.
Когда приложение представлено в виде нескольких компонентов, микросервисов, то требуется выбрать определённый подход. На примере типового web-приложения, состоящего из двух образов:
frontend
и backend
— возможные варианты таковы:- Хранить образы в отдельных вложенных репозиториях:
- Хранить всё в одном репозитории, а имя образа учитывать в теге, к примеру, следующим образом:
NB: Вообще-то, есть ещё вариант с сохранением в различных репозиториях,
PROJECT-frontend
и PROJECT-backend
, но его мы не будем рассматривать из-за сложности поддержки, организации и распределения прав между пользователями.Поддержка в werf
Изначально werf ограничился вложенными репозиториями — благо, большинство реестров поддерживают такую возможность. Начиная с версии v1.0.4-alpha.3, добавлена работа с реестрами, в которых не поддерживается вложенность, и Docker Hub — в их числе. С этого момента у пользователя появился выбор, как хранить образы приложения.
Реализация доступна в рамках опции
--images-repo-mode=multirep|monorep
(по умолчанию multirep
, т.е. хранение во вложенных репозиториях). Она определяет шаблоны, по которым образы хранятся в реестре. Достаточно выбрать нужный режим при использовании основных команд, а всё остальное останется неизменным.Поскольку большинство опций werf можно задавать переменными окружения, в CI/CD-системах режим хранения, как правило, легко задать глобально для всего проекта. Например, в случае с GitLab достаточно добавить переменную окружения в настройках проекта: Settings -> CI / CD -> Variables:
WERF_IMAGES_REPO_MODE: multirep|monorep
.Если говорить о публикации образов и выкате приложений (об этих процессах можно подробно прочитать в соответствующих статьях документации: Publish process и Deploy process), то режим исключительно определяет шаблон, по которому можно работать с образом.
Дьявол в деталях
Отличие и основная сложность при добавлении нового способа хранения — в процессе очистки registry (возможности чистки, поддерживаемые в werf, см. в Cleaning process).
При очистке werf учитывает используемые в кластерах Kubernetes образы, а также политики, настраиваемые пользователем. В основе политик лежит разделение тегов на стратегии. Стратегии, поддерживаемые в настоящий момент:
- 3 стратегии, связанные Git-примитивами, такими как тег, ветка и коммит;
- 1 стратегия для произвольных пользовательских тегов.
Информацию о стратегии тега мы сохраняем при публикации образа в лейблах конечного образа. Само значение — так называемый метатег — необходим для применения части политик. Например, при удалении ветки или тега из Git-репозитория логично удалять и связанные неиспользуемые образы из реестра, что и покрывается частью наших политик.
При сохранении в одном репозитории (
monorep
), в теге образа, помимо метатега также может храниться имя образа: PROJECT:frontend-META-TAG
. Для их разделения мы не стали вводить какой-то специфичный разделитель, а просто добавили необходимое значение в лейбл конечного образа при публикации.NB: Если интересно посмотреть на всё описанное в исходном коде werf, то отправной точкой может служить PR 1684.
В этой статье мы не будем уделять больше внимания проблематике и обоснованию нашего подхода: про стратегии тегирования, хранения данных в лейблах и процесс публикации в целом —обо всём этом подробно рассказано в недавнем докладе Дмитрия Столярова: «werf — наш инструмент для CI/CD в Kubernetes».
Резюмируя
Отсутствие поддержки реестров без вложенности не являлось блокирующим фактором для нас или известных нам пользователей werf — ведь всегда можно поднять отдельный реестр образов (или перейти на условный Container Registry в Google Cloud)… Однако снятие такого ограничения выглядело логичным для того, чтобы инструмент был удобен более широкому DevOps-сообществу. Реализуя его, мы столкнулись с главной сложностью в переработке механизма очистки реестра контейнеров. Теперь, когда всё готово, приятно осознавать, что кому-то стало легче, а у нас (как главных разработчиков проекта) заметных сложностей в дальнейшей поддержке этой фичи не предвидится.
Оставайтесь с нами и совсем скоро мы расскажем о других нововведениях в werf!
P.S.
Читайте также в нашем блоге:
- «Собирать Docker-образы в werf теперь можно и по обычному Dockerfile»;
- «werf — наш инструмент для CI/CD в Kubernetes (обзор и видео доклада)».