Всем привет! Меня зовут Марк Барзали, я Software Engineer. В этой статье я описал кейс из моей работы в Seznam.cz (Чешский аналог «Яндекса». — Прим. ред.). Вместе с командой мы занимались сохранением пользовательских файлов в хранилищах и следили за их версиями, перезаписями, дистрибуцией и прочим.
При использовании старых подходов в оркестрации мы столкнулись с рядом проблем, поэтому решили внедрить Kubernetes, но при миграции поняли, что нам нужен инструмент, который бы упростил сборку, публикацию и развёртывание приложений. И выбрали для этого werf — утилиту для эффективной и надёжной доставки приложений в Kubernetes.
Я поделюсь тем, как werf помог нам решить проблемы с кэшированием, ускорить сборку образов, а также наладить удобную интеграцию с Argo CD. Расскажу об опыте внедрения werf, первых шагах, трудностях и, конечно же, о тех преимуществах, которые эта утилита принесла нашей команде. Поехали!

Переезд с legacy
Когда я пришёл в компанию, команда использовала Salt (для тех, кто не знаком, — это что-то вроде Ansible) как главный оркестратор, а он был ну очень медленным. Приходилось собирать Python в Debian-пакеты, а развёртывание приложения иногда падало. Секреты хранились в локальной файловой системе, и развёртывание происходило с мастер-узла Salt вручную. Для этого нужно было предварительно написать в Mattermost, чтобы никто не переписал твой stage-релиз.
В то время компания уже потихоньку переносила сервисы в Kubernetes (треть сервисов уже была перенесена). Вот как всё выглядело для Kubernetes:
Папка с
.env
использовалась для деплоя в разные окружения (было много копипасты).Jinja вместо Helm. Jinja рендерился программой, написанной внутри компании нашими DevOps-инженерами. И финальный манифест можно было увидеть в основном во время деплоя, так как скрипт рендера был в образах контейнеров от DevOps, который внедрял в environment ещё и свои переменные.
Docker pull использовался как кэш — так скачивались старые собранные образы, надеясь на возможность переиспользования слоёв.
Версия образа определялась либо как
commit-hash
, либо как значение из файлаVERSION
, который редактировался вручную. В merge train была проверка на изменение этого файла.Harbor часто засорялся, и стабильно раз в пару дней приходилось чистить старые образы.
Таким образом, pipeline в GitLab выглядел примерно так:

Это упрощённая схема, так как было ещё много проблем с Debian, линтингом и прочим, что опустим в рамках этой статьи.
Интеграция werf в проеĸт
Каждую неделю-две у нас проходит техническое собрание, где команда представляет свои проекты, проводит демо, рассказывает про новые технологии. Я активно участвовал в этих мероприятиях: обычно открывал CNCF Landscape, выбирал что-нибудь и готовил демо, параллельно изучая что-то новое. Так однажды я представил и werf.
Первая итерация
После демо для команды мне не удалось получить разрешение пушить в продакшен с помощью werf. Но мне разрешили протестировать его в стейджинге. И здесь пришлось раздумывать над тем, как интегрировать werf в наш workflow, включая попутную адаптацию к нашим структурам проектов. В итоге оба стейджинга нового сервиса развёртывались с помощью werf converge для удобства и возможности развёртывания прямо с локального ПК или ноутбука.
Сам процесс сборки очень порадовал, особенно то, что билд практически пропускался, если были минимальные изменения в коде.
Конфигурация была минимальной, использовалась одна-две переменные среды, что числились в werf-giterminism.yaml
. Остальное было примитивным (принцип KISS, который отлично работает). К Stapel мы не перешли, так как привыкли к Dockerfile и были довольны им.
Позже на ретро всем всё понравилось, и мы решили продолжать использовать werf и внедрять его в другие проекты, попутно ускоряя сборку. Например, для нашего монолита (контейнерного образа из 2–3 слоёв) время сборки сократилось с 10 до 3–5 минут в зависимости от того, какой слой менялся.
Мы, конечно, сталкивались с проблемами при внедрении werf, но их удалось решить с помощью документации. В основном они касались наших сертификатов. Поэтому нам даже не понадобилось обращаться за помощью к разработчикам утилиты.
Вторая итерация
Наравне с werf у нас началась работа с Argo CD. И мы решили «склеить» инструменты. Финальная версия у нас выглядит сейчас так:
werf делает bundle и пушит в Harbor с тегом, соответствующим номеру пайплайна (
CI_PIPELINE_ID
).Argo CD использует как референс Harbor и смотрит на теги бандлов (а не Git, хотя была попытка использовать и такой подход).
Существует отдельная задача (Job), которая синхронизирует AppProject с
CI_PIPELINE_ID
.На этапе merge train задача пошлёт MR (PR) с изменением манифеста, содержащим референс на финальный
CI_PIPELINE_ID
, в репозиторий, на который смотрит Argo CD.
Третья итерация
Мы перевели все CI-задачи в компоненты GitLab и добавили периодическую команду werf cleanup
, что очень помогло нам с освобождением места в Docker-регистре. У нас были политики очистки на Harbor и раньше, но они не справлялись так эффективно, как теперь с поддержкой очистки werf.
Команда werf cleanup
учитывает образы, используемые в Kubernetes, и применяет Git-политики — werf знает, каким коммитам соответствуют теги образов, что позволяет безопасно удалять только неактуальные версии.
Вместо заключения
Мы не были сильно в восторге от использования trdl по причинам безопасности: что-то скачивается и обновляется на фоне. Кроме того, есть удобные инструменты, такие как go install
или brew
. Ещё нам не нравилась лишняя зависимость для бинарного файла, который сам по себе не требует зависимостей и сложной конфигурации. Поэтому мы строили всё из исходного кода.
Мне лично очень нравится подход UV к этому — когда бинарник попадает в образ или CI/CD через копирование:
COPY --from=ghcr.io/astral-sh/uv:0.6.14 /uv /bin/
Так мы создавали образы для нашего CI с нашими сертификатами, версиями Python, скриптами от команды DevOps для авторизации в Kubernetes и т. п.
Ещё нам нравилась команда converge
, которая позволяет мгновенно применять изменения в Kubernetes. Также werf умеет упорядочивать выкат любых ресурсов на основании их веса (приоритета) и имеет улучшенное кэширование при сборке, что делает его особенно ценным. Это его главная особенность. В общем, утилита удовлетворяла нашим потребностям и работает отлично.
P. S.
Читайте также в нашем блоге: