Как yarn v3 и философия Zero Installs помогли нам сократить длительность ci/cd пайплайна в 3 раза

Моя цель - предложение широкого ассортимента товаров и услуг на постоянно высоком качестве обслуживания по самым выгодным ценам.

Прежде чем перейти к статье, хочу вам представить, экономическую онлайн игру Brave Knights, в которой вы можете играть и зарабатывать. Регистируйтесь, играйте и зарабатывайте!

Всем привет, меня зовут Фёдор — я руководитель фронтенд-разработки на проекте Smartbot Pro в компании KTS

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

Недавно на проекте остро встал вопрос оптимизации наших ci/cd пайплайнов, потому что релиз определенной версии мог занимать до 18 минут.

Для нас очень важно сократить это время, потому что мы хотим быстрее доставлять пользователям две вещи:

  • Новый функционал

  • Исправления багов

В статье я расскажу, как мы решили эту проблему с помощью оптимизации сборки Docker-образа, оптимизации установки зависимостей и сокращения количества шагов пайплайна. 

Это может быть полезно тем, кто столкнулся с проблемой долгих пайплайнов.

Содержание:

  • Структура проекта:

    • Основной монорепозиторий

    • Shopback монорепозиторий

  • Анализ пайплайна

    • Base-build

    • Test

    • Build

    • Deploy Stages

  • Первая попытка оптимизации

    • Оптимизация base-build

    • Оптимизация build

    • Итоги первой попытки

  • Вторая попытка оптимизации

    • Yarn Zero-Installs

    • Миграция

      • Шаг 1

      • Шаг 2

      • Шаг 3

      • Шаг 4

      • Шаг 5

      • Шаг 6

    • Итоги второй попытки

  • Дальнейшие планы

Структура проекта

Пробежимся чуть подробнее по этой схеме для понимания источников проблемы.

Основной монорепозиторий

Основной репозиторий состоит из 3 пакетов:

  1. b2c — основная версия продукта

  2. b2b — версия для клиента, которая дополняет версию b2c специфичными функциями, которые подключаются к b2c версии с помощью плагинов.

  3. sa — приложение для администрирования. Которая используется утилиты/типы/модели из b2c версии.

Shopback монорепозиторий

Мы с командой часто тестируем разные продуктовые гипотезы. Shopback стал одной из таких гипотез — это конструктор магазинов внутри ботов в Telegram, который мы реализовали на базе Web App.

После успешного тестирования гипотезы было принято решение интегрировать этот конструктор в основной проект. Для интеграции потребовалось использовать UI-компоненты из shopback-репозитория для реализации превью магазина. Мы вынесли их в отдельную библиотеку @shopback/ui, которая используется внутри пакета app и внутри b2c пакета из основного репозитория.

настройка и превью того, как это реально выглядит в вашем сервисе
настройка и превью того, как это реально выглядит в вашем сервисе

Важно отметить, что задача заключалась в оптимизации пайплайнов именно основного репозитория. Пакет UI часто обновляется, потому что shopback сейчас активно разрабатывается. Из-за этого нам приходится часто изменять версию этого пакета в b2c. Поэтому нам важно, чтобы пайплайны длились как можно меньше именно при изменяющихся зависимостях.

Анализ пайплайна

Для начала необходимо было проанализировать наш пайплайн и понять, какие именно джобы (job) тормозят.

Пайплайн для ветки master
Пайплайн для ветки master

Base-build

В этой джобе собирается и пушится в registry Docker-образ со всеми установленными зависимостями монорепозитория. Мы приняли такое решение, чтобы не повторять установку зависимостей внутри каждой сборки наших пакетов (build stage).

Длительность base-build зависела от того, были ли изменены зависимости. 

  • Если зависимости не изменились: ~30 секунд, благодаря Docker cache

  • Если изменились: ~10 минут. Этот вариант стал большой проблемой

Test

Запускаются тесты внутри Docker-образа, собранного на предыдущем шаге. Длительность тестов составляла в среднем 30 секунд и не сильно влияла на общую длительность.

Build

В зависимости от пакета длительность составляла 2-7 минут. 

Для оптимизации мы будем рассматривать одну джобу: docker-b2c. Все они примерно одинаковые, проблема везде одна.

Длительность docker-b2c составляла 5-7 минут. 

Deploy stages

Все джобы внутри deploy-* стейджей деплоят собранные на предыдущем шаге образы в Kubernetes-кластер. Длительность каждой из этих ~10 секунд. 

Как и в случае с тестами, 10 секунд из 16-18 минут — это совсем небольшая часть. К тому же это стабильное время. Поэтому test и deploy стейджы в оптимизации не нуждаются.

Первая попытка оптимизации

После анализа стало очевидно:

  • Основная длительность сосредоточилась в base-build и build, эти джобы и нужно оптимизировать:

Пайплайн до оптимизации
Пайплайн до оптимизации
  • Длительность пайплайна зависит от того, есть ли измененные зависимости:

    • Если зависимости не изменились, длительность составляет 3-5 минут

    • Если изменились, длительность составляет 16-18 минут

Оптимизация base-build

В качестве пакетного менеджера мы использовали yarn v1.

Напомню, что длительность 10 минут соответствовала варианту с изменениями в зависимостях. 

Сначала мы попробовали добавить кэширование. Про Docker cache и лучшие практики применения очень хорошо написано в документации

После прочтения и анализа Docker-файла, стало понятно, что можно применить кэш для команды RUN:

RUN --mount=type=cache,target=.yarn/cache YARN_CACHE=.yarn/cache yarn install

Для использования данного кэша необходимо использовать BuildKit в качестве docker backend.

При таком вызове при следующей сборке будет использоваться yarn cache из директории .yarn/cache из предыдущего образа.

Оптимизация build

В качестве сборщика нашего проекта мы используем webpack v5.

Webpack по дефолту использует memory cache при development mode, но для production mode кэширование отключено — подробнее об этом можно прочесть в документации. Можно изменить это поведение, указав в настройках конфига cache:

{
  ...
  cache: isProd ? {
    type: 'filesystem',
    buildDependencies: {
      config: [__filename]
    }
  } : { type: 'memory' }
}

Теперь при production mode для кэширования будет использоваться файловая система.

По аналогии с добавлением кэширования при установке зависимостей добавим кэширование при сборке проекта:

RUN --mount=type=cache,target=.yarn/.cache/webpack yarn run build

Итоги первой попытки

После подключения кэширования в джобы были получены следующие цифры:

  • Если зависимости не изменились, длительность составляет 2,5-4,5 минуты.
    Было 4-6 минут

  • Если изменились, длительность составляет ~13-15 минут.
    Было 16-18 минут

Стало лучше. Но как я уже говорил, зависимости изменяются часто, и для нас 13-15 минут — плохой результат. 

Поэтому мы продолжили исследование.

Вторая попытка оптимизации

При повторном взгляде на base-build стало понятно, что на самом деле установка зависимостей занимает не так много времени относительно общей длительности этой джобы:

  • Длительность до первой оптимизации: ~140 секунд

  • Длительность после первой оптимизации: ~100 секунд

Напомню, что общая длительность составляет ~10 минут. 

Остальное время уходило на push образа в registry. Тут же вскрылась другая проблема: большой образ с зависимостями занимает 1 Gb и негативно влияет на размеры registry, скорость пуша и пула.

Очевидный вопрос: «Можно ли вообще избавиться от этого образа?». 

Первое, что пришло на ум — устанавливать зависимости не в отдельной джобе и хранить их в отдельном Docker-образе, а устанавливать в каждой docker-* джобе. Тогда при использовании multi-stage сборки размер будет включать только собранные файлы без зависимостей, но этот шаг будет повторяться в каждой из docker-* джоб. Это не лучший вариант, так как длительность установки зависимостей с ростом их числа тоже будет расти. К тому же установленные зависимости необходимы для прохождения тестов в джобе test.

Второй вариант — хранить зависимости внутри репозитория. Я предчувствую, что на этом месте половина читателей уже скроллит вниз, чтобы оставить плохой комментарий, но пожалуйста, подождите, я могу всё объяснить!

Источник: https://habr.com/ru/companies/kts/articles/735104/


Интересные статьи

Интересные статьи

Недавно я сделал игровую ретро-консоль для своих детей. И подумал, что было бы полезно поделиться своим опытом на тот случай, если другие захотят создать что-то похожее.
Привет, Хабр! На связи ЕВРАЗ, и сегодня мы хотим рассказать, как организовали и провели хакатон по Data Science и Computer Vision. Казалось бы, где металлургия и где хакатон — но нет. На самом деле, в...
Вызванный пандемией экономический кризис ударил по экономикам всего мира двумя путями: первым, более очевидным — это снижение и частичная остановка экономической активности из-за карантинных мер в раз...
Сюжет, в котором страны сражаются за различные ресурсы, не редкость для книг и фильмов. Собственно, это реальность сегодняшнего дня, а не просто художественный вымысел. Но если раньше п...
Как думаете, сколько заболеваний глаза (и его вспомогательного аппарата) существует по МКБ-10? Наверное, с сотню? Вот и нет — всего 59. Но даже одна и самая незначительная болячка будет е...