Pull to refresh

Comments 6

А потом в проекте появляется torch и transformers, и добро пожаловать в мир тридцатиминутных сборок и образов по 10 гигов 🙈

  1. Можно все-таки добавить билд исполняемого файла, чтобы не тянуть с собой пакеты ОС явно, а чисто 1 исполняемый файл

  2. Если файл исполняемый без внешних плагинов, то scratch/alpine достаточно

  3. Если раннер/билдер в единственном экземпляре и не stateless, то можно повесить кэш пакетов: --mount=type=cache в докере.

  4. Если доступа к диску в 3 пункте нет (а-ля какой-нибудь Kaniko), то лучше использовать зеркало пакетов максимально близкое к раннеру/билдеру, чтобы не качать пакеты из внешней сети. Это касается и питона, и ОС.

Забыл ещё момент.

Если захотите гонять много маленький файликов на раннере/билдере/рабочей ВМ, то однозначно рекомендую выбирать железку/ВМ на NVME. Проверено опытом, может кратно ускорить скачивание/распаковку пакетов из пакетного менеджера

C uv можно ещё быстрее :)

при правильном запуске сборки образа, все слой билдера (builder) будут переиспользоваться в будущем и все последующие обновления будут занимать значительно меньше времени, и будут даже быстрее чем, при одноэтапной сборке

Время сборки с нуля: 58
Время сборки после изменения кода: 56

Разница всего в 2 секунды явно говорит, что там что-то сделано не так. Скорее всего основной код копируется вместе с requirements.txt/pyproject.toml, и установка зависимостей на следующем шаге не использует кэш сборки. Это не про одноэтапнапную vs многоэтапную сборки, а криво написанный dockerfile для одноэтапной

Стало
COPY pyproject.toml poetry.lock /code/ # <- Тут копируется только список зависимостей
RUN poetry install # <- Установка зависимостей
COPY . /code/ # <- Тут копируется весь код
после изменения кода время билда после правок кода сократилось до 14 секунд

Собственно, о чём и речь. Такое разделение - стандарт для образов с приложениями на python. База, как сейчас модно говорить. Из описания к базовому образу:
https://hub.docker.com/_/python#how-to-use-this-image

Стоило только их разделить, как одноэтапная сборка после изменения кода стала хоть и немного, но быстрее многоэтапной

Вся эта чехарда со слоями хороша до тех пор, пока у вас билд не уезжает в гитлаб раннеры. Они чистятся, да и, как правило, их больше одного, так что вероятность даже просто запустить билд после изменения кода на том же раннере может быть заметно ниже 1. Так что можно считать, что мы начинаем всегда с нуля.

Я для себя эту проблему решил созданием для проекта базового образа, куда упихана установка нужных системных библиотек и тех пакетов из зависимостей, которые вряд ли будут обновлены (ну, застряли вы на старом marshmallow, например...)

Этот образ со своим докерфайлом и отдельной джобой сборки. Собранное пушится в хранилище и потом используется через FROM в образе приложения.

А остальные зависимости и сам код приложения как раз в отдельном докерфайле с минимумом слоев.

Немного громоздко, но куда деваться, если раннер чист? Да и пересобрать базовый образ пару-тройку раз в год несложно.

Sign up to leave a comment.

Articles