Использование Docker и docker image — способ запаковать Python приложение со всеми необходимыми зависимостями, а затем запустить в изолированной среде. Есть разные способы это сделать, Docker наиболее распространен среди DevOps-практик.

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

Чем меньше образ, тем быстрее его закачивать/скачивать/запускать. Поэтому если есть дешевый способ сократить размер на 25%+ - то стоит это сделать.

Как настроить multi-stage build

Multi-stage builds — это процесс, когда внутри одного Dockerfile описываем несколько образов, которые переиспользуют друг друга.

Такой подход позволяет динамически создать образ (выполнить десятки команд), сжать его до 1 слоя, и всю подготовку в виде 1 слоя скопировать, а не десятки отдельных слоев.

1. Выбери базовый образ и установи основные настройки

# Первый образ: для компиляции
FROM python:3.13-slim-bullseye AS python

ENV PYTHONUNBUFFERED=1 \
    PYTHONDONTWRITEBYTECODE=1 \
    PIP_NO_CACHE_DIR=off \
    PIP_DISABLE_PIP_VERSION_CHECK=on \
    PIP_DEFAULT_TIMEOUT=30 \
    POETRY_NO_INTERACTION=1 \
    POETRY_VIRTUALENVS_IN_PROJECT=true \
    POETRY_HOME="/opt/poetry" \
    PYSETUP_PATH="/app" \
    VENV_PATH="/app/.venv" \
    POETRY_VERSION=2.1.3 \
    PIP_VERSION=25.1.1

ENV PATH="$POETRY_HOME/bin:$VENV_PATH/bin:$PATH"

RUN pip install -U "pip==$PIP_VERSION" "poetry==$POETRY_VERSION"

Реже обновляемые данные стоит помещать в самый первый образ.

2. Установи системные зависимости

# Подготовка сборки и установка зависимостей
FROM python AS python-build-stage

WORKDIR $PYSETUP_PATH

# Устанавливаем системные зависимости
RUN apt-get update && apt-get install --no-install-recommends -y --fix-missing \
  build-essential \
  libpq-dev \
  libffi-dev \
  libpcre3 \
  libpcre3-dev \
  gettext \
  htop \
  git \
  vim \
  python3-all-dev

# Копируем файлы зависимостей
COPY pyproject.toml poetry.lock ./

# Устанавливаем зависимости
RUN poetry install --without=dev --no-ansi

На этом этапе мы устанавливаем во временную директорию все необходимые зависимости. Часто, при установке создаются множество промежуточных файлов, которые занимают место. Нам они не нужны.

3. Собираем финальный образ

# Копируем файлы приложения, собираем, чистим кэш
FROM python AS python-run-stage

# Устанавливаем только runtime зависимости, если требуются
RUN apt-get update \
    && apt-get install -y --no-install-recommends \
        libpq5 \
        libpq-dev \
  && apt-get purge -y --auto-remove -o APT::AutoRemove::RecommendsImportant=false \
  && rm -rf /var/lib/apt/lists/*

# Копируем зависимости из этапа сборки
COPY --from=python-build-stage $PYSETUP_PATH $PYSETUP_PATH

WORKDIR $PYSETUP_PATH

# Копируем код приложения
COPY ./ ${PYSETUP_PATH}

RUN python --version

4. Проверяем сборку

docker build -t myapp .

Начни тренироваться с реальными инцидентами

Хочешь быстро научиться диагностировать проблемы в продакшене? Incidenta — это тренажер IT-инцидентов, где ты можешь прожить 50+ различных сбоев в безопасной среде.

Попробуй демо-сценарий прямо сейчас и почувствуй, что такое "база данных недоступна" или "нехватка памяти в контейнере".

FAQ

Когда НЕ стоит использовать multi-stage builds?

Не стоит использовать multi-stage builds если: твой проект не требует компиляции (только чистый Python без C-расширений), размер образа не критичен, у тебя простой проект без внешних зависимостей. В таких случаях single-stage build будет проще и быстрее.

Как измерить эффективность multi-stage builds?

Сравни размеры образов до и после: docker images | grep myapp. Засеки время полной сборки и время пересборки при изменении кода: time docker build -t myapp .. Хорошие результаты: размер уменьшился в 2-3 раза, время пересборки сократилось в 2-5 раз.

Как отладить проблемы в multi-stage builds?

Собирай промежуточные образы для отладки: docker build --target python-build-stage -t myapp-debug ., затем запускай контейнер: docker run -it myapp-debug bash. Проверяй, что файлы копируются правильно и PATH настроен корректно в runtime-stage.