Я не знаю человека, который любит долго ждать. Но ожидание и его последствия бывают разные.
Для нашего облака Amvera ожидание сборки проекта обратно коррелирует с количеством деплоев, которые пользователи могут сделать при отладке кода. Но если для обычного деплоя не всегда нужно много итераций (в идеале одна), то при вайбкодинге итераций нужно гигантское количество. И когда мы стали делать своего ИИ-помощника, который пишет/правит код и запускает его на сервере, поняли, что сборка должна быть быстрой. Очень быстрой.
Контекст: Amvera – это PaaS-облако, где для запуска любого кода на сервере достаточно просто привязать репозиторий. И не нужно настраивать и администрировать сервер.
В этой статье я очень кратко расскажу, что мы сделали, чтобы ускорить сборку и какие архитектурные приёмы использовали для этого.
Что замедляет сборку
Узкое место при сборке приложений на удаленном сервере, это скачивание и установка зависимостей.
Конкретно для Python
-
PyPi имеет склонность резать скорость, если загрузок много (а у нас их очень много).
-
pip – сам по себе не быстрый инструмент.
-
Операции по установки зависимостей занимают львиное время от сборки.
-
Еще требуется установка Python, но у нас он уже нативно поддержан, и его устанавливать не нужно.
Именно эти узкие места мы и решили оптимизировать. Разумеется, сборка состоит не только из установки зависимостей. В нашем случае есть еще архивирование, сохранение и скачивание архива, и время, когда Kubernetes планирует поды на ноды. Но всегда лучше начать с самого долгого этапа, где понятно, что делать для его сокращения.
Избавляемся от PyPI
Первое, что мы сделали в Amvera, это собственное зеркало с образами Python. Принцип очень простой: если пакета в нем нет, скачиваем зависимость из PyPI. Если есть, берем из нашего зеркала.
Однажды наполнив зеркало, мы ходим в PyPI редко. И пакеты скачиваем по внутренней сети (не через полусломанный интернет), что даёт надёжность и скорость.
Про надёжность я говорю не просто так. Если обычная скорость загрузки PyPI около 100 мб/сек., то при превышении внутреннего лимита PyPI по количеству загрузок в единицу времени, скорость падает до 20—50 кб, и зависимости могут скачиваться десятки минут, что неприемлемо. А с ростом мы стали часто попадать под эти ограничения. Зеркало же даёт стабильные 300—400 мб/сек.
Переходим на UV
Даже если не брать чистый UV, а использовать совместимый с pip, UV PIP, прирост в скорости установки зависимостей на наших тестах составил 7 раз.
Если раньше в Amvera поддерживался только pip, то теперь мы добавили и UV, и UV pip.
UV написан на Rust, делает параллельную загрузку зависимостей и использует специальный кэш.
Скорость обеспечивается благодаря тому, что UV устанавливает зависимости не последовательно, а параллельно.
А как бонус, он хорошо решает еще и конфликты в зависимостях.
И практика подтверждает теорию, UV действительно намного быстрее.
Кешируем зависимости на ноде
Зеркало, это, конечно, хорошо, но если говорить честно, есть набор зависимостей, которые встречаются очень часто. Гонять их по сети и ждать чтения с дисков/S3 для них нецелесообразно.
Для таких зависимостей мы сделали горячий кэш прямо на ноде сборки. Эффект такой, как если бы вы проводили операцию сборки локально.
Выделяем ноды с большим количеством CPU
Сборка – это вычислительно ёмкая операция. А нам нужно одновременно производить 10–15 сборок, с пиками до нескольких десятков (на днях фиксировали 70 одновременных сборок). Если мощности нод не хватает, все начинает безжалостно тормозить.
Но, к счастью, в этом случае помогает просто не жадничать и выделить несколько нод с большим количеством CPU.
В начале работ мы заметили, что текущие ноды не справляются в часы пик и просто добавили их количество и ресурсы процессора и ОЗУ.
Компромиссы
Но есть и компромиссы, на которые пришлось пойти.
Наша система стала сложнее, что иногда приводит к ошибкам.
Из недавних примеров могу вспомнить, когда мы не уследили за тем, что диск для зеркала закончился и некоторые зависимости не могли установиться.
Другим примером была неверная настройка брокера сообщений, когда мы не могли понять несколько часов, почему неожиданно новые сборки не могли стартовать.
Но это разовые инциденты, которые нужно было один раз отладить. Хотя я и не исключаю, что что-то еще может проявиться в будущем. Повышение сложности системы всегда требует большего администрирования.
Потенциал для улучшения
Сейчас самым долгим этапом стал этап архивации и действий с архивом сборки. Плюс, есть время, когда оркестратору нужно запланировать под сборки, а когда сборка произведена, выполнить запуск. Сейчас эти этапы уже занимают больше времени, чем установка зависимостей. И именно их мы оптимизируем следующим этапом.
Что получилось в итоге
У меня есть тестовый проект, использующий Streamlit и ряд других зависимостей. Если раньше он собирался 18 минут, то сейчас время сборки составляет 50 секунд. Разница около 20 раз. Но это тяжелый проект. Для простых ботов ускорение не столь существенно, но они и раньше собирались за пару минут, а сейчас еще быстрее.
Как итог, мы пришли к тому, что сам запуск кода в поде стал в среднем занимать дольше времени, чем сборка. И это следующая наша цель для оптимизации.
Мы хотим, чтобы в Amvera проекты собирались и запускались также быстро, как на локальной машине.
ссылка на оригинал статьи https://habr.com/ru/articles/1084160/