Content-based tagging у збирачі werf: навіщо і як це працює?

Content-based tagging у збирачі werf: навіщо і як це працює?

werf — наша GitOps CLI-утиліта з відкритим кодом для збирання та доставки додатків у Kubernetes. У релізі v1.1 була представлена ​​нова можливість у збирачі образів: тегування образів за вмістом або content-based tagging. Досі типова схема тегування в werf передбачала тегування Docker-образів за Git-тегом, Git-гілкою або Git-коммітом. Але всі ці схеми мають недоліки, які повністю вирішуються новою стратегією тегування. Подробиці про неї і чим вона така гарна — під катом.

Викочування набору мікросервісів з одного Git-репозиторію

Часто зустрічається ситуація, коли програма розбита на безліч більш-менш незалежних сервісів. Релізи цих сервісів можуть відбуватися незалежно: за один раз може релізуватися один або кілька сервісів, решта при цьому має продовжувати працювати без будь-яких змін. Але з точки зору зберігання коду та управління проектом зручніше тримати такі послуги програми в єдиному репозиторії.

Бувають ситуації, коли послуги дійсно незалежні і не пов'язані з одним додатком. У такому разі вони будуть розташовані в окремих проектах та їх реліз здійснюватиметься через окремі процеси CI/CD у кожному з проектів.

Однак насправді розробники найчастіше розбивають єдиний додаток на кілька мікросервісів, але заводити для кожного окремий репозиторій та проект… — явний overkill. Саме про цю ситуацію і йтиметься далі: кілька таких мікросервісів лежать в єдиному репозиторії проекту і релізи відбуваються через єдиний процес у CI/CD.

Тегування по Git-Гілці та Git-тегу

Припустимо, використовується найпоширеніша стратегія тегування. tag-or-branch. Для Git-гілок образи тегуються назвою гілки, для однієї гілки одночасно існує тільки один опублікований образ на ім'я цієї гілки. Для Git-тегів образи тегуються відповідно до імені тега.

При створенні нового Git-тегу – наприклад, при виході нової версії – для всіх образів проекту в Docker Registry буде створено новий Docker-тег:

  • myregistry.org/myproject/frontend:v1.1.10
  • myregistry.org/myproject/myservice1:v1.1.10
  • myregistry.org/myproject/myservice2:v1.1.10
  • myregistry.org/myproject/myservice3:v1.1.10
  • myregistry.org/myproject/myservice4:v1.1.10
  • myregistry.org/myproject/myservice5:v1.1.10
  • myregistry.org/myproject/database:v1.1.10

Ці нові імена образів потрапляють через Helm-шаблони у конфігурацію Kubernetes. При запуску деплою командою werf deploy відбувається оновлення поля image в маніфестах ресурсів Kubernetes і перезапуск відповідних ресурсів через ім'я образу, що змінилося.

проблема: у випадку, коли реально з попереднього викату (Git-тега) не змінився вміст образу, а лише його Docker-тег, відбувається зайвий перезапуск цього додатка та, відповідно, можливий деякий простий. Хоча не було жодних реальних причин робити цей перезапуск.

Як наслідок, при поточній схемі тегування доводиться городити кілька окремих Git-репозиторіїв та постає проблема організації викочування цих кількох репозиторіїв. Загалом і загалом така схема виходить перевантаженою та складною. Краще об'єднувати багато сервісів у єдиний репозиторій та створювати такі Docker-теги, щоб зайвих перезапусків не було.

Тегування по Git-комміту

У werf також є стратегія тегування, пов'язана з Git-коммітами.

Git-commit є ідентифікатором вмісту Git-репозиторію і залежить від історії правок файлів у Git-репозиторії, тому здається логічним використовувати його для тегування образів у Docker Registry.

Однак тегування за Git-коммітом має ті ж недоліки, що і за Git-гілками або Git-тегами:

  • Міг бути створений порожній коміт, який не змінює файли, а Docker-тег образу буде змінено.
  • Міг бути створений merge-комміт, який не змінює файлів, а Docker-тег образу буде змінено.
  • Міг бути створений коміт, який змінює файли в Git, які не імпортуються в образ, а Docker-тег образу знову буде змінено.

Тегування на ім'я Git-гілки не відображає версію образу

Є ще одна проблема, пов'язана зі стратегією тегування по Git-гілкам.

Тегування на ім'я гілки працює до тих пір, поки коміти цієї гілки збирають послідовно в хронологічному порядку.

Якщо в поточній схемі користувач запустить перескладання старого комміта, пов'язаного з деякою гілкою, то werf перетрет образ за відповідним Docker-тегом знову зібраною версією образу для старого комміта. Deployment'и, що використовують цей тег, з цього моменту ризикують під час перезапуску pod'ів зробити pull іншої версії образу, в результаті чого наша програма втратить зв'язок з CI-системою, розсинхронізується.

Крім того, при послідовних push'ах в одну гілку з малим проміжком часу між ними старий коміт може зібратися пізніше, ніж новий: стара версія образу перетре нову по тегу Git-гілки. Такі проблеми може вирішувати CI/CD-система (наприклад, GitLab CI для серії коммітів запускається pipeline останнього). Однак це підтримують не всі системи і має бути надійніший спосіб запобігання такій фундаментальній проблемі.

Що таке content-based tagging?

Отже, що таке content-based tagging — тегування образів за вмістом.

Для створення Docker-тегів використовуються не примітити Git'а (Git-гілка, Git-тег…), а контрольна сума, пов'язана з:

  • вмістом образу. Ідентифікатор тег образу відображає його вміст. При збиранні нової версії цей ідентифікатор не зміниться, якщо файл не змінився в образі;
  • історією створення цього образу в Git. Образи, пов'язані з різними Git-гілками та різною історією складання через werf, будуть мати різні теги-ідентифікатори.

Як такий тег-ідентифікатор виступає так звана сигнатура стадій образу.

Кожен образ складається з набору стадій: from, before-install, git-archive, install, imports-after-install, before-setup, ... git-latest-patch і т.д. Кожна стадія має ідентифікатор, який відображає її вміст. сигнатура стадії (Stage signature).

Фінальний образ, що складається з цих стадій, тегується так званою сигнатурою набору цих стадій. stages signature, - Яка є узагальнюючою для всіх стадій образу.

У кожного образу конфігурації werf.yaml Загалом буде своя така сигнатура і, відповідно, Docker-тег.

Сигнатура стадій вирішує всі ці проблеми:

  • Стійка до порожніх Git-коммітів.
  • Стійка до Git-коммітів, які змінюють файли, що не є релевантними для образу.
  • Не призводить до проблеми з перетиранням актуальної версії образу під час перезапуску збірок для старих Git-коммітів гілки.

Тепер це рекомендована стратегія тегування і використовується за замовчуванням у werf для всіх систем CI.

Як включити та використовувати в werf

Відповідна опція з'явилась у команди werf publish: --tag-by-stages-signature=true|false

У CI-системі стратегія тегування задається командою werf ci-env. Раніше для неї визначався параметр werf ci-env --tagging-strategy=tag-or-branch. Тепер, якщо вказати werf ci-env --tagging-strategy=stages-signature або не вказувати цю опцію, werf за умовчанням використовуватиме стратегію тегування stages-signature. команда werf ci-env автоматично виставить потрібні прапори для команди werf build-and-publish (або werf publish), тому жодних додаткових опцій для цих команд вказувати не потрібно.

Наприклад, команда:

werf publish --stages-storage :local --images-repo registry.hello.com/web/core/system --tag-by-stages-signature

… може створити такі образи:

  • registry.hello.com/web/core/system/backend:4ef339f84ca22247f01fb335bb19f46c4434014d8daa3d5d6f0e386d
  • registry.hello.com/web/core/system/frontend:f44206457e0a4c8a54655543f749799d10a9fe945896dab1c16996c6

Тут 4ef339f84ca22247f01fb335bb19f46c4434014d8daa3d5d6f0e386d - це сигнатура стадій образу backend, а f44206457e0a4c8a54655543f749799d10a9fe945896dab1c16996c6 - сигнатура стадій образу frontend.

При використанні спеціальних функцій werf_container_image и werf_container_env у шаблонах Helm нічого змінювати не потрібно: ці функції автоматично генеруватимуть вірні імена образів.

Приклад конфігурації в CI-системі:

type multiwerf && source <(multiwerf use 1.1 beta)
type werf && source <(werf ci-env gitlab)
werf build-and-publish|deploy

Більше інформації про налаштування доступно в документації:

Разом

  • Нова опція werf publish --tag-by-stages-signature=true|false.
  • Нове значення опції werf ci-env --tagging-strategy=stages-signature|tag-or-branch (якщо не вказати, то за замовчуванням буде stages-signature).
  • Якщо до цього використовувалися опції тегування за Git-коммітами (WERF_TAG_GIT_COMMIT або опція werf publish --tag-git-commit COMMIT), то обов'язково перемикати на стратегію тегування stages-signature.
  • Нові проекти краще одразу перемикати на нову схему тегування.
  • Старі проекти при переведенні на werf 1.1 бажано перемикати на нову схему тегування, але стара tag-or-branch як і раніше, підтримується.

Content-based tagging вирішує всі висвітлені у статті проблеми:

  • Стійкість імені Docker-тегу до порожніх Git-коммітів.
  • Стійкість імені Docker-тегу до Git-коммітів, які змінюють нерелевантні для образу файли.
  • Не призводить до проблеми з перетиранням актуальної версії образу під час перезапуску збірок для старих Git-коммітів для Git-гілок.

Користуйтесь! І не забувайте заглядати до нас на GitHub, Щоб створити issue або знайти вже існуючий, поставити плюс, створити PR або просто спостерігати за розвитком проекту.

PS

Читайте також у нашому блозі:

Джерело: habr.com

Додати коментар або відгук