Использование 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.