company_banner

Поддержка monorepo и multirepo в werf и при чём здесь Docker Registry



    Тема монорепозитория обсуждалась уже не раз и, как правило, вызывает весьма активные споры. Создавая 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 — возможные варианты таковы:

    1. Хранить образы в отдельных вложенных репозиториях:

    2. Хранить всё в одном репозитории, а имя образа учитывать в теге, к примеру, следующим образом:


    NB: Вообще-то, есть ещё вариант с сохранением в различных репозиториях, PROJECT-frontend и PROJECT-backend, но его мы не будем рассматривать из-за сложности поддержки, организации и распределения прав между пользователями.

    Поддержка в werf


    Изначально werf ограничился вложенными репозиториями — благо, большинство реестров поддерживают такую возможность. Начиная с версии v1.0.4-alpha.3, добавлена работа с реестрами, в которых не поддерживается вложенность, и Docker Hub — в их числе. С этого момента у пользователя появился выбор, как хранить образы приложения.

    Реализация доступна в рамках опции --images-repo-mode=multirepo|monorepo (по умолчанию multirepo, т.е. хранение во вложенных репозиториях). Она определяет шаблоны, по которым образы хранятся в реестре. Достаточно выбрать нужный режим при использовании основных команд, а всё остальное останется неизменным.

    Поскольку большинство опций werf можно задавать переменными окружения, в CI/CD-системах режим хранения, как правило, легко задать глобально для всего проекта. Например, в случае с GitLab достаточно добавить переменную окружения в настройках проекта: Settings -> CI / CD -> Variables: WERF_IMAGES_REPO_MODE: multirepo|monorepo.

    Если говорить о публикации образов и выкате приложений (об этих процессах можно подробно прочитать в соответствующих статьях документации: Publish process и Deploy process), то режим исключительно определяет шаблон, по которому можно работать с образом.

    Дьявол в деталях


    Отличие и основная сложность при добавлении нового способа хранения — в процессе очистки registry (возможности чистки, поддерживаемые в werf, см. в Cleaning process).

    При очистке werf учитывает используемые в кластерах Kubernetes образы, а также политики, настраиваемые пользователем. В основе политик лежит разделение тегов на стратегии. Стратегии, поддерживаемые в настоящий момент:

    1. 3 стратегии, связанные Git-примитивами, такими как тег, ветка и коммит;
    2. 1 стратегия для произвольных пользовательских тегов.

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

    При сохранении в одном репозитории (monorepo), в теге образа, помимо метатега также может храниться имя образа: PROJECT:frontend-META-TAG. Для их разделения мы не стали вводить какой-то специфичный разделитель, а просто добавили необходимое значение в лейбл конечного образа при публикации.

    NB: Если интересно посмотреть на всё описанное в исходном коде werf, то отправной точкой может служить PR 1684.

    В этой статье мы не будем уделять больше внимания проблематике и обоснованию нашего подхода: про стратегии тегирования, хранения данных в лейблах и процесс публикации в целом —обо всём этом подробно рассказано в недавнем докладе Дмитрия Столярова: «werf — наш инструмент для CI/CD в Kubernetes».

    Резюмируя


    Отсутствие поддержки реестров без вложенности не являлось блокирующим фактором для нас или известных нам пользователей werf — ведь всегда можно поднять отдельный реестр образов (или перейти на условный Container Registry в Google Cloud)… Однако снятие такого ограничения выглядело логичным для того, чтобы инструмент был удобен более широкому DevOps-сообществу. Реализуя его, мы столкнулись с главной сложностью в переработке механизма очистки реестра контейнеров. Теперь, когда всё готово, приятно осознавать, что кому-то стало легче, а у нас (как главных разработчиков проекта) заметных сложностей в дальнейшей поддержке этой фичи не предвидится.

    Оставайтесь с нами и совсем скоро мы расскажем о других нововведениях в werf!

    P.S.


    Читайте также в нашем блоге:

    • +31
    • 2,3k
    • 3
    Флант
    840,93
    Специалисты по DevOps и Kubernetes
    Поделиться публикацией

    Комментарии 3

      +1
      контейнезируются

      Э, чо-чо? :-)


      Когда приложение представлено в виде нескольких компонентов, микросервисов, то требуется выбрать определённый подход

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


      Хранить образы в отдельных вложенных репозиториях:

      Дайте определение вложенного репозитория. Во-вторых, вы тут вносите терминологическую путаницу, которая идёт от самого докера:


      1. Репозиторий — это repository (как git repository)
      2. "Репозиторий" докера — это реестр или регистр. Не знаю как лучше. По-английски — [docker] registry.
      3. Не существует такой вещи как "вложенные репозитории". По сути докер образ определяется двумя ключевыми строковыми значениями: image name и tag. Ссылка на регистри является неотъемлемой частью image name. Либо опускается, если речь идёт о докерхабе. И как пример в том же гитлабе — в один "типа реестр" на проект можно напихать сколько угодно по-разному поименованных образов (создавая "типо иерархическую подструктуру" под головным доменом registry.gitlab.com)
      4. Что хуже — в разной документации TAG'ом называется либо хвостик названия образа (:latest, :v0.1.0, :master), либо все название образа целиком

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

        +2
        Во-первых, строго говоря разбивка приложения на несколько контейнеров ничего общего с микросервисной архитектурой не имеет. Ну, разбили монолит на два контейнера и разбили. И получили распределённый монолит.

        Когда приложение представлено в виде нескольких компонентов, к примеру, микросервисов…

        Дайте определение вложенного репозитория

        • Repository — коллекция образов, сгруппированная по имени.
        • Registry — сервис для хранения репозиториев (Docker Hub, GCR, ...).

        Большинство registry поддерживает многоуровневую иерархию репозиториев. Термин вложенный репозиторий используется, чтобы подчеркнуть изоляцию группы репозиториев, а не просто возможность хранения образов с многоуровневым именем (REGISTRY/webdev/app/backend).

        В GCR используется термин nested repository.

        В Azure nested namespaces, но по сути тоже самое.

        В случае с Docker Hub многоуровневое именнование образов не поддерживается (и не планируется), поэтому и соответствующего термина нет.
          0

          Спасибо за разъяснение.
          Ещё один довод в пользу неиспользования Докер Хаба в продакшен.
          Предыдущее, что насторожило — когда у них угнали базу паролей.

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

      Самое читаемое