Как я исправил падающие сборки в GitHub Actions и уменьшил размер билда на 35%
Представим с вами ситуацию: одним прекрасным днём кто-то на проекте работает над крутой фичей, добавляет в Dockerfile новую зависимость, но вместо закрытой задачи и довольных клиентов вы получаете бесконечно падающие билды в работающем ранее без нареканий GitHub Actions. "Кэши" — скажет кто-то и будет прав, но лишь отчасти.
Сама по себе ошибка и правда крылась в кэшах, но даже их отчистка (да, их можно удалять на гитхабе, а вы и не знали?) не решила проблему, что и привело к дальнейшим изменениям и улучшениям.
Прежде всего, давайте взглянем на абстрактные исходные данные: самый обычный multistage (что абсолютно не важно, но агрессивно рекомендую) Dockerfile, какие-то переменные окружения, какие-то зависимости, назначение которых даже удобно прокомментировано:
FROM ubuntu:XX.YY AS base
ENV FOOBAR=42
...
RUN apt update
RUN apt upgrade -y
RUN apt install -y libffi-dev libheif-dev libde265-dev # HEIF/HEIC images
RUN apt install -y libwebp-dev # Webp images
RUN apt install -y fonts-wqy-zenhei # Chinese fonts
...
Чтобы решить проблему с кэшами, а заодно и заставить наш билд похудеть, мы воспользуемся комбинацией из нескольких best practices, которые по какой-то причине (ха, угадай) редко упоминаются в разного рода курсах и туториалах на сомнительного качества сайтах.
Прежде всего (и это самая важная информация на этой странице!), решаем проблему с кэшами реформатировав обновление репозиториев и установку пакетов в одну инструкцию. Т.к. каждая строка в Dockerfile в конечном итоге экспортируется в "слой", что после используется (в том числе) как кэш для ускорения сборок, мы заставляем docker обновлять репозитории и устанавливать пакеты каждый раз, как мы добавляем новую зависимость, в то время как старый вариант будет использовать слои для сборки ДО строки с новым пакетом и тем самым не станет обновлять репозитории, что в долгосрочной перспективе вызовет ошибки.
Более того, теперь все наши зависимости упакованы в один слой, что потенциально снижает трафик и/или размер кэша.
Вишенкой на торте нашего обновлённого Dockerfile'а станет удаление списков репозиториев, которые абсолютно нам не нужны в финальном билде, т.к. используются только для установки зависимостей.
Обратите внимание, чтобы после этого действия не осталось никаких команд по установке зависимостей, т.к. это может привести к ошибкам.
Базовые образы обновляются относительно часто, поэтому вам достаточно пулить их с некоторой периодичностью (как правило с этим отлично справляются системы сборки, как тот же GitHub Actions), поэтому можно смело опускать инструкцию apt upgrade.
Взглянем на результат:
FROM ubuntu:XX.YY AS base
ENV FOOBAR=42
...
RUN apt update \
&& apt install --no-install-recommends --assume-yes \
libffi-dev libheif-dev libde265-dev \
libwebp-dev \
fonts-wqy-zenhei \
... \
&& rm -rf /var/lib/apt/lists/*
...
Результаты оптимизации будут разниться от проекта к проекту и всё это очень индивидуально, однако я получил -35% размера билда, что определённо отличный результат, особенно учитывая, что изначально мы просто фиксили сборку.
Я пофиксил, а ты подпишись на Telegram, ведь там тоже бывает интересно.