werf vs. Helm: корректно ли их вообще сравнивать?

в 7:11, , рубрики: continuous delivery, devops, helm, kubernetes, open source, werf, Блог компании Флант, системное администрирование
werf vs. Helm: корректно ли их вообще сравнивать? - 1

Эта статья — развернутый ответ на вопрос, который нам периодически задают: чем werf отличается от Helm? На первый взгляд можно предположить, что задача у них примерно одинаковая: автоматизировать деплой приложений в Kubernetes. Но всё, конечно, немного сложнее…

Роль в CI/CD

Если упрощенно показать утилиты в рамках полного цикла CI/CD, то их функции значительно отличаются:

Helm

werf

Сборка приложения (в Docker-образ)

Публикация образов в container registry и их автоматическая очистка со временем

Деплой в Kubernetes

Деплой в Kubernetes (на базе Helm), расширенный трекингом ресурсов, интеграцией с образами, встроенной поддержкой Giterminism и другими фичами

Как видно, в рамках CI/CD-пайплайна werf делает гораздо больше, участвуя в полном жизненном цикле приложения, от сборки до выката в Kubernetes.

Helm — хороший инструмент, но довольно низкоуровневый. Для реального использования в CI/CD он требует надстроек, интеграции с другими инструментами… в общем, заметного усложнения инфраструктуры и процессов. 

Поэтому мы говорим, что werf — это следующий уровень доставки приложений в Kubernetes. Утилита использует Helm как один из компонентов и интегрирует его с другими стандартными инструментами: Git, Docker и Kubernetes. Благодаря этому werf выступает в роли «клея», который упрощает, унифицирует организацию CI/CD-пайплайнов на базе специализированных инструментов, уже ставших стандартом в индустрии, и выбранной вами CI-системы.

Что умеет werf (и не умеет Helm)

Мы упомянули, что werf — это утилита, которая не только про деплой. Но даже если посмотреть только на данный этап, то и здесь у werf есть ряд доработок:

Возможности

Helm

werf

Ожидание готовности ресурсов во время деплоя

+

+

Трекинг ресурсов и обнаружение ошибок

+

Fail-fast во время деплоя

+

Защита от параллельных запусков деплоя одного и того же релиза

+

Интеграция с собираемыми образами

+

Поддержка автодобавления аннотаций и лейблов во все ресурсы релиза

+ / −

+

Публикация файлов конфигурации и образов приложения в container registry (бандлы)

+ / −

+

Базовая поддержка секретных values

+

Поддержка Giterminism и GitOps

+ / −

+

Подробнее мы разберем каждый пункт таблицы ниже (см. «Детальное сравнение werf и Helm» ниже). Но сначала — об истоках, которые привели к таким следствиям. Расскажем о том, к чему мы стремились, создавая werf.

Четыре благородные истины werf

1. werf должна использоваться в CI/CD-пайплайне как единый инструмент

В утилите необходима поддержка работы в любой существующей CI/CD-системе и легкой интеграции. Сейчас werf работает «из коробки» с GitLab CI/CD и GitHub Actions. Другие CI-системы тоже поддерживаются — для интеграции достаточно написать скрипт, следуя инструкции.

2. werf должна оптимальным образом доставлять приложения в Kubernetes

В процессе доставки собираются только недостающие образы из container registry или недостающие слои для этих образов, а всё старое берется из прошлых сборок или кэша. Так экономится и время сборки, и место для хранения всех образов.

После доставки [обновлённого приложения] текущее состояние ресурсов в Kubernetes приводится к новому требуемому состоянию, которое определено в Git (мы назвали это словом «гитерминизм», от слов «Git» + «детерминизм») — и здесь снова речь идёт о том, что для такой синхронизации вычисляются изменения и применяются только они.

3. werf должна давать четкую обратную связь

Итоговый отчет werf в идеале должен быть достаточным для диагностики проблемы. Если что-то пошло не так в процессе доставки и развертывания, пользователю не нужно запускать kubectl и искать информацию в кластере. werf сразу показывает и постоянно обновляет актуальный статус процесса деплоя и даёт достаточно информации для решения проблемы без привлечения системного администратора.

4. werf должна поддерживать GitOps-подход

GitOps в общем виде — это подход, при котором для развертывания приложений в Kubernetes используется единственный источник правды — Git-репозиторий. Через декларативные действия в Git вы управляете реальным состоянием инфраструктуры, запущенной в Kubernetes. Этот паттерн «из коробки» работает в werf.

У нас есть собственный взгляд на то, как должен быть реализован GitOps для CI/CD (уже упомянутый Giterminism). GitOps в werf поддерживает не только хранение Kubernetes-манифестов в Git, но и версионирование собираемых образов, связанное с Git. Откат до предыдущей версии приложения выполняется без сборки выкатывавшихся ранее образов (при условии, что версия учитывается политиками очистки). Другие существующие реализации GitOps не дают этой гарантии. 

Зачем и как werf использует Helm

werf использует и расширяет Helm, чтобы следовать вышеприведенным принципам.

Helm — популярный и проверенный инструмент. У него есть собственный шаблонизатор, чарты и т.п. Мы не стали переизобретать то, что уже отлично работает. Вместо этого сфокусировались на фичах, которые помогают оптимизировать CI/CD.

С точки зрения совместимости важно, что кодовая база Helm вкомпилирована в werf. Обновления из upstream приходят регулярно, руками Helm обновлять не нужно. (Как, впрочем, и у самой werf, у которой есть встроенный version manager — multiwerf с 5 каналами стабильности и поддержкой автообновления.)

Мы даже стараемся по возможности участвовать в улучшении Helm через upstream. Один из примеров нашего вклада — аннотация helm.sh/hook-delete-policy=before-hook-creation («Удалить предыдущий ресурс перед запуском нового хука»), которая пришла в Helm из werf.

Плюсы и минусы Helm

У Helm есть ряд преимуществ:

  • Это стандартный package manager в K8s. Helm используют [практически] все, кто работает с Kubernetes; он стал стандартом индустрии.

  • Шаблонизация. Шаблоны Helm удобны для тиражирования манифестов при работе с несколькими окружениями. (Пусть и не все согласятся, что Go-templates — удобный шаблонизатор, но это уже другой вопрос...)

  • Удобное управление чартами. Общепринятый формат описания ресурсов и объединения их в чарты (charts — это «пакеты» для Helm).

  • Переиспользование чартов. Helm поддерживает публикацию переиспользуемых чартов в Chart Repository или container registry.

  • Удобное управление жизненным циклом релизов. В Helm легко управлять релизами и откатываться на нужные версии.

Что до минусов, то основной в том, что Helm — это маленькое звено в CI/CD-цепи, которое мало или совсем не связано с другими важными звеньями.

CI/CD приложения предполагает непрерывное слияние изменений в основную кодовую базу проекта и непрерывную доставку этих изменений до пользователя. Это включает периодическую сборку новых образов приложения с обновлениями, тестирование этих образов и выкат новой версии приложения. В CI/CD у нас обычно несколько окружений (production, staging, development и т. д.). И конфигурация приложения может отличаться для разных окружений.

Технически Helm позволяет конфигурировать описанный chart параметрами, через которые передаются образы приложения и выбранное окружение. Однако конкретный путь — как это делать — не стандартизован. Пользователю Helm приходится решать это самому.

Детальное сравнение werf и Helm

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

1. Трекинг ресурсов и обнаружение ошибок

И werf, и Helm умеют ждать готовности ресурсов в процессе развертывания. Но werf дает обратную связь и более проактивна. Три главных отличия:

1. В процессе деплоя werf выводит логи по выкатываемым ресурсам. Для отдельных ресурсов логирование отключается автоматически — по мере их перехода в состояние готовности. Можно отключить трекинг конкретных контейнеров или логировать вывод только по определенным контейнерам ресурса, отключив остальные; можно включать и выключать вывод сервисной информации Kubernetes. Всё это настраивается с помощью аннотаций, которые объявляются в шаблонах чарта.

2. Как только werf замечает проблему в одном из ресурсов, он завершается с ошибкой и ненулевым кодом выхода. werf следует принципу fail-fast. Он выдает наиболее полезную информацию по ошибке, чтобы пользователь сразу мог понять, в чём проблема, по выводу в CI/CD job.

Helm, в отличие от werf, в случае проблем с конфигурацией ждет истечения таймаута. А такие проблемы — распространенная ситуация в CI/CD. Выяснить, в чём их причина, можно только после подключения к кластеру через kubectl. Это неудобно:

  • нужно настраивать права доступа к kubectl;

  • kubectl требует знаний и умений: куда смотреть, что искать и как это интерпретировать.

В идеале разработчик по выводу werf сможет понять причину проблемы и пофиксить ее, не привлекая админа. После этого достаточно создать в Git новый коммит с исправлением.

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

2. Интеграция со сборкой образов

В Helm приходится явно передавать полные имена образов через values и заботиться об актуализации имен при изменениях.

werf решает эту проблему за пользователя: не нужно думать о способах передачи имен образов, т. к. они передаются через те же values автоматически. Более того, werf оптимальным образом решает проблему именования образов — так, чтобы в процессе деплоя менялись имена только у изменившихся образов.

К тому же, werf дает возможность откатиться на старую версию приложения со старыми образами без повторной пересборки этих образов. Часто при использовании Helm в CI/CD через values для упрощения передаются статические имена образов (вроде registry.example.com/myproject:production) — в этом случае имя образа ссылается только на последнюю собранную версию образа. При такой схеме тегирования приходиться пересобирать старый образ, чтобы откатиться до предыдущей версии. werf использует схему с content-based-тегами, которые связаны с историей Git. Помимо прочего, такая схема тегирования полностью решает вопрос с откатом на старую версию.

Что дает интеграция с собранными образами:

  • werf может использовать уже существующие Dockerfile'ы в своей конфигурации.

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

  • Нет лишних перевыкатов компонентов приложения в Kubernetes и, как следствие, лишнего простоя компонентов. Перевыкат происходит быстро и только для измененных компонентов, а не приложения целиком.

  • Со стороны конфигурации Helm остается только использовать имена образов, которые werf предоставляет через специальные values.

  • Можно откатиться на образы старой версии приложения.

3. Добавление аннотаций и лейблов в ресурсы релиза

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

werf добавляет во все ресурсы релиза автоматические аннотации вроде ссылки на CI/CD job, из которого ресурс был в последний раз выкачен, или ссылки на Git-коммит.

Также в werf через CLI-опции можно указать произвольные аннотации или лейблы, которые будут добавлены во все ресурсы релиза. Пример такой команды: 

werf converge --add-annotation pipeline-id=$CI_PIPELINE_ID --add-annotation git-branch=$CI_COMMIT_REF_NAME

Это удобно:

  • для интроспекции, когда надо понять, с каким CI/CD job связана версия ресурса;

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

4. Бандлы: публикация файлов конфигурации и образов приложения в container registry

Helm поддерживает публикацию чартов в OCI container registry либо Chart Repository, однако не отвечает за образы, которые нужны этому чарту для выката. Такие образы должны быть отдельно собраны и правильно протегированы, а опубликованный чарт должен быть настроен на использование образов по правильным именам. Эти проблемы пользователю Helm приходиться решать самостоятельно.

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

Пользователь не думает об именах образов, публикуемых вместе с чартом: werf делает это автоматически и оптимальным способом. Неизменные образы будут переиспользованы. Публикуются лишь те слои, которые требуются для текущего коммита. Пользователю werf достаточно выбрать версию бандла и обеспечить его публикацию в требуемом Git-коммите.

5. Встроенная базовая поддержка значений секретов

В  Helm поддержка секретов возможна через подключение сторонних плагинов. Это усложняет установку Helm на новые хосты.

werf из коробки дает возможность закодировать значения values через алгоритмы AES-128, AES-192 и AES-256. Ключи шифрования можно менять.

6. Поддержка Giterminism и GitOps

Helm не регулирует привязку используемых конфигурационных файлов к Git-коммитам. 

werf (с версии v1.2) форсирует использование конфигурации из текущего Git-коммита и реализует режим гитерминизма, в том числе и для конфигурации Helm.

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

Подытожим

Прямое противопоставление «werf vs Helm» не совсем корректно, потому что утилита werf реализует не только деплой, а нацелена на поддержку полного жизненного цикла доставки приложений. Сам по себе Helm — хороший инструмент для деплоя в Kubernetes, поэтому он (с некоторыми улучшениями) встроен в werf для решения этой задачи. Однако и в контексте деплоя у werf есть ряд преимуществ, общий смысл которых сводится к более полной и удобной интеграции с CI/CD-системами.

P.S.

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

Автор: Timofey Kirillov

Источник


* - обязательные к заполнению поля


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js