DockerMiddleExperience
Какие ошибки в Dockerfile чаще всего приводят к большим, небезопасным или невоспроизводимым образам?
Большие образы — из-за отсутствия multi-stage, полных base images и node_modules в runtime. Небезопасные — запуск от root, секреты в слоях, устаревший base image. Невоспроизводимые — нефиксированные теги, COPY . . в начале Dockerfile.
Ошибки, приводящие к большим образам
- Отсутствие multi-stage build: копирование всего исходника вместе с компилятором, dev-зависимостями и тестовыми библиотеками в runtime image. Go-бинарник на базе
golang:1.23весит ~1 ГБ; наdistroless/static— ~10 МБ. - apt-get update и install в разных RUN: update кэшируется отдельно, install может использовать устаревший index или создавать лишние слои. Правило:
update && install && rm -rf /var/lib/apt/lists/*в одной инструкции. - Копирование node_modules в image: вместо
COPY . .передnpm ci --omit=devнужно сначала копироватьpackage*.json, устанавливать зависимости, а потом исходники — для оптимального кэша и минимального размера. - Использование полных base images там, где достаточно slim:
python:3.13vspython:3.13-slim— разница ~400 МБ.ubuntu:24.04vsdebian:bookworm-slim— ~150 МБ.
Ошибки, приводящие к небезопасным образам
- Запуск от root: если не задать
USER nonroot, процесс получает UID 0 внутри контейнера. При escape из namespace это даёт root на хосте. Всегда создавайте непривилегированного пользователя. - Секреты в ARG/ENV или в слоях: даже если
ENV SECRET=...удалить в следующем слое — он останется в истории образа. Используйте BuildKit secrets:RUN --mount=type=secret,id=npmrc .... - Устаревший base image без обновления:
FROM ubuntu:20.04безapt-get upgradeсодержит CVE из 2020 года. Подключите автоматическое обновление base image через Dependabot или Renovate. - Установка curl, wget, nc в production image: эти инструменты расширяют attack surface при компрометации контейнера. В distroless образах их нет по умолчанию.
Ошибки, приводящие к невоспроизводимым образам
- Нефиксированные теги:
FROM node:latestилиapt-get install python3без версии — разные сборки в разное время дают разный результат. Используйте digest:FROM node:22-slim@sha256:.... - Зависимость от внешних ресурсов во время build: curl-скрипт установки, pip install из git-репозитория без pinned commit, wget бинарника без проверки хэша — всё это невоспроизводимо и небезопасно.
- COPY . . в начале Dockerfile: любое изменение исходника сбрасывает весь кэш слоёв. Правильный порядок: сначала скопировать lockfiles, поставить зависимости, потом скопировать исходники.
Пример правильного Dockerfile для Python
FROM python:3.13-slim AS base
WORKDIR /app
RUN addgroup --system app && adduser --system --group app
COPY requirements.txt ./
RUN pip install --no-cache-dir -r requirements.txt
COPY src/ ./src/
USER app
CMD ["python", "-m", "src.main"]
Подводные камни
- Удаление файла в следующем слое не уменьшает предыдущий слой — нужно удалять в том же
RUN. - Объединение всех команд в один гигантский
RUNухудшает читаемость и убивает cache — объединяйте только логически связанные операции. - hadolint (
docker run --rm -i hadolint/hadolint < Dockerfile) ловит большинство типичных ошибок автоматически — стоит добавить в CI. - Multi-stage build не помогает, если финальный stage всё равно основан на
ubuntu:22.04с полным toolchain.
What hurts your answer
- Перечислять ошибки без объяснения причин
- Не отличать beginner mistakes от production failure modes
- Не предлагать процесс, который предотвращает повторение ошибок
What they're listening for
- Знает типичные ошибки при работе с Docker
- Понимает причины ошибок
- Предлагает практики prevention и early detection