Всем привет! Меня зовут Марк Барзали, я 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 выглядел примерно так:

* Lock the environment — это был лок на стороне GitLab. На самом деле он даже не работал
* Lock the environment — это был лок на стороне 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.

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

Комментарии (0)