Напишите production-готовый Dockerfile для Python-приложения. Объясните, как уменьшить образ и ускорить сборку за счёт кэша слоёв.
Короткий ответ
- Multi-stage: зависимости собираются отдельно от runtime-образа
- Сначала копировать файл зависимостей, потом код — ради кэша
- Базовый образ slim и фиксированная версия тега
- Запуск от непривилегированного пользователя
- Меньше слоёв с мусором: no-cache-dir, очистка apt-списков
- HEALTHCHECK и exec-форма CMD для корректных сигналов
Хороший Dockerfile — это multi-stage, правильный порядок слоёв под кэш, минимальный базовый образ и запуск не от root.
Как сказать вслух
пример ответаЯ делаю сборку в два этапа: в первом ставлю зависимости, во второй, чистый образ, копирую только результат. Файл с зависимостями копирую раньше кода, чтобы кэш слоёв не сбрасывался на каждый коммит. Беру slim-образ с зафиксированной версией, создаю отдельного пользователя и запускаю приложение не от root. В конце добавляю healthcheck и пишу CMD в exec-форме, чтобы процесс корректно получал сигналы остановки.
Подробный ответ
Основной ответ
Ключевые приёмы: multi-stage отделяет сборочные инструменты (компиляторы, dev-заголовки) от runtime; порядок инструкций использует кэш — зависимости меняются реже кода, поэтому COPY requirements и установка идут до COPY исходников; базовый образ python:slim с точным тегом вместо latest; непривилегированный пользователь (USER) снижает последствия компрометации; .dockerignore исключает .git, кэши и тесты; pip с --no-cache-dir не тащит кэш в слой. CMD в exec-форме делает процесс PID 1 и доставляет SIGTERM для graceful shutdown. Дальнейшее уменьшение — distroless-образы; проверка — сканеры уязвимостей (trivy) в CI.
Ключевые моменты
- Кэш слоёв. Инструкции упорядочены от редко меняющихся к частым; изменение кода не инвалидирует слой с зависимостями.
- Multi-stage. В финальный образ попадает только нужное для запуска — меньше размер и поверхность атаки.
- Не root. USER с выделенным uid — базовая гигиена; многие кластеры запрещают root через политики.
- Сигналы и PID 1. Exec-форма CMD без оболочки позволяет приложению получать SIGTERM и завершаться корректно.
Практический контекст
Типичное практическое задание: дают «плохой» Dockerfile и просят улучшить, либо написать свой с нуля. В работе от качества образа зависят скорость CI, время деплоя и результаты security-сканов. Интервьюер смотрит на порядок слоёв, multi-stage, пользователя и на то, может ли кандидат объяснить каждый приём, а не воспроизвести заготовку.
Пример кода
FROM python:3.12-slim AS builder
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir --prefix=/install -r requirements.txt
FROM python:3.12-slim
WORKDIR /app
RUN useradd --create-home --uid 10001 appuser
COPY --from=builder /install /usr/local
COPY src/ ./src
USER appuser
EXPOSE 8000
HEALTHCHECK --interval=30s --timeout=3s \
CMD python -c "import urllib.request;urllib.request.urlopen('http://127.0.0.1:8000/healthz')"
CMD ["gunicorn", "-b", "0.0.0.0:8000", "src.app:app"]Частые ошибки
- Копируют весь код до установки зависимостей, убивая кэш слоёв
- Оставляют запуск от root и тег latest у базового образа
- Используют shell-форму CMD, из-за чего приложение не получает SIGTERM