Слои и кэш Docker: .dockerignore, no-cache и BuildKit cache mounts
Автор: Казачкин Даниил Михайлович · Обновлено
Кэш сборки отвечает на вопрос: можно ли повторно использовать результат шага с теми же входными данными? Он ускоряет работу, но не является обещанием, что внешние репозитории…
Кэш сборки отвечает на вопрос: можно ли повторно использовать результат шага с теми же входными данными? Он ускоряет работу, но не является обещанием, что внешние репозитории пакетов не изменились. Различайте кэш слоёв, кэш менеджера пакетов и локально сохранённые базовые образы.
Эксперимент с порядком COPY
В отдельную папку скопируйте app.py из предыдущего урока и requirements.txt со строкой packaging==25.0. Создайте Dockerfile:
# syntax=docker/dockerfile:1
FROM python:3.13-slim
WORKDIR /app
COPY requirements.txt ./requirements.txt
RUN --mount=type=cache,target=/root/.cache/pip pip install -r requirements.txt
COPY app.py ./app.py
USER 10001:10001
CMD ["python", "app.py"]Соберите дважды:
docker build --progress=plain -t dk-cache:1 .
docker build --progress=plain -t dk-cache:1 .Во второй сборке ищите CACHED у подходящих шагов. Теперь измените только текст в app.py и повторите build. Шаг установки зависимостей должен остаться пригодным для повторного использования; шаг COPY приложения изменится. Если вместо раздельных COPY сначала написать COPY . ., изменение любого переданного исходника может сделать следующий pip install необходимым заново. Порядок шагов и кэш.
Cache mount хранит скачанные pip-артефакты у builder между сборками. Он полезен, когда инструкция RUN всё же выполняется заново. В этом варианте намеренно нет pip --no-cache-dir: иначе мы отключили бы именно тот кэш pip, который хотим использовать. Содержимое cache mount не становится обычным каталогом конечного образа.
Уменьшим контекст
Создайте .dockerignore в корне контекста:
.git
.venv
__pycache__
*.pyc
.env
outputНе передавайте в сборщик окружение хоста, логи и локальные секреты. .dockerignore работает для build context; .gitignore сам по себе не заменяет его. При другом Dockerfile может применяться отдельный файл исключений с соответствующим именем. Перед оптимизацией проверьте в логе размер переданного контекста. Правила .dockerignore.
Что меняет --no-cache
docker build --no-cache --progress=plain -t dk-cache:fresh .
docker build --pull --no-cache -t dk-cache:refreshed .Первая команда запрещает повторно использовать кэш инструкций для этой сборки. Вторая дополнительно проверяет актуальность базового образа по тегу. --no-cache не означает «обнови тег FROM», не удаляет все хранилища Docker и не превращает плавающие зависимости в закреплённые. Cache mounts — отдельный механизм; их содержимое может использоваться и при повторном выполнении RUN. Правила инвалидации.
Почему старый пакет может остаться
Для обычного RUN сборщик не проверяет удалённый apt-репозиторий только потому, что прошло время. Если инструкция и предшествующее состояние не изменились, шаг может быть взят из кэша. Для Debian-пакетов объединяйте обновление индекса и установку в одну инструкцию, например RUN apt-get update && apt-get install -y --no-install-recommends curl && rm -rf /var/lib/apt/lists/*. Так индекс и установка не расходятся по разным кэшированным шагам.
Обновление зависимостей должно быть отдельным воспроизводимым изменением проекта. Постоянный no-cache как универсальное «лечение» скрывает причины и замедляет CI. Сначала проверьте, что вы собираете правильный контекст и запускаете именно новый тег; после build уже работающий контейнер сам не обновляется.
Упражнение с наблюдаемым результатом
Сравните три сборки: без изменений; после изменения app.py; после изменения requirements.txt на packaging==24.2. Для последней версии подтвердите результат командой docker run --rm dk-cache:1 python -c "import packaging; print(packaging.__version__)". Запишите, какие шаги выполнились повторно и почему. Не требуйте от времени сборки точных миллисекунд: сеть и состояние builder влияют на замер. Критерий понимания — предсказать повторное выполнение по входам, а не запомнить порядок цветных строк.