Эта статья — развернутый ответ на вопрос, который нам периодически задают: чем 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.
Читайте также в нашем блоге:
-
«Пакетный менеджер для Kubernetes — Helm: прошлое, настоящее, будущее»;
-
«werf — наш инструмент для CI/CD в Kubernetes» (обзор и видео доклада).
Автор: Timofey Kirillov