Best practice Dockerfile for Python with uv
Ниже приведён пример Dockerfile
, который мы используем и рекомендуем при создании образов Docker для приложений Python, использующих uv
в качестве менеджера пакетов.
FROM python:3.12-slim-bookworm AS base
FROM base AS builder
COPY --from=ghcr.io/astral-sh/uv:0.4.9 /uv /bin/uv
ENV UV_COMPILE_BYTECODE=1 UV_LINK_MODE=copy
WORKDIR /app
COPY uv.lock pyproject.toml /app/
RUN --mount=type=cache,target=/root/.cache/uv \
uv sync --frozen --no-install-project --no-dev
COPY . /app
RUN --mount=type=cache,target=/root/.cache/uv \
uv sync --frozen --no-dev
FROM base
COPY --from=builder /app /app
ENV PATH="/app/.venv/bin:$PATH"
EXPOSE 8000
CMD ["uvicorn", "uv_docker_example:app", "--host", "0.0.0.0", "--port", "8000"]
Объяснение Dockerfile
Используя многоэтапную сборку, мы можем отделить сборку от развертывания и в полной мере воспользоваться преимуществами кэширования слоев Docker для ускорения сборки и получения итогового образа меньшего размера.
Этап 1: FROM python:3.12-slim-bookworm AS base
FROM python:3.12-slim-bookworm AS base
Для оптимального кэширования мы используем один и тот же базовый образ для всех этапов. Это обеспечивает совместимость между этапами сборки и развёртывания и позволяет нам использовать преимущества кэширования слоёв Docker, чтобы создавать меньше слоёв при сборке. Образ -alpine
также можно использовать для создания ещё более компактного итогового образа, но в некоторых проектах может потребоваться установка дополнительных зависимостей.
COPY --from=ghcr.io/astral-sh/uv:0.4.9 /uv /bin/uv
ENV UV_COMPILE_BYTECODE=1 UV_LINK_MODE=copy
В конструкторе мы копируем двоичный файл uv
из официального UV-изображения с тегом конкретной версии.
UV_COMPILE_BYTECODE=1
указывает uv
скомпилировать файлы Python в .pyc
файлы байт-кода. Установка этого занимает немного больше времени (часть процесса сборки), но часто ускоряет время запуска приложения в контейнере.
UV_LINK_MODE=copy
команда uv
копирует файлы Python в контейнер из кэша, устраняя любые проблемы, связанные с символическими ссылками.
WORKDIR /app
COPY uv.lock pyproject.toml /app/
RUN --mount=type=cache,target=/root/.cache/uv \
uv sync --frozen --no-install-project --no-dev
После настройки рабочего каталога мы копируем только файлы uv.lock
и pyproject.toml
в каталог /app
и запускаем uv sync
для установки зависимостей. Это позволяет нам использовать кэширование уровней Docker для кэширования зависимостей, которые меняются реже, перед копированием остального кода приложения.
COPY . /app
RUN --mount=type=cache,target=/root/.cache/uv \
uv sync --frozen --no-dev
После установки зависимостей и кэширования слоя мы копируем остальную часть кода приложения и снова запускаем uv sync
без флага --no-install-project
, чтобы установить приложение. Если исходный код приложения изменится, этот слой станет недействительным, но зависимости не нужно будет устанавливать заново.
Этап 3: FROM base
(заключительный этап)
FROM base
COPY --from=builder /app /app
ENV PATH="/app/.venv/bin:$PATH"
Заключительный этап начинается с создания минимального базового образа и копирования данных из этапа сборки в каталог /app
. В этом случае мы задаём переменную среды PATH
, включающую каталог bin
виртуальной среды, чтобы можно было запускать приложение без указания полного пути к исполняемому файлу uvicorn
.
EXPOSE 8000
CMD ["uvicorn", "uv_docker_example:app", "--host", "0.0.0.0", "--port", "8000"]
После копирования в ваше приложение вы можете открыть любой порт, который прослушивает ваше приложение, и установить команду по умолчанию для запуска вашего приложения. В данном случае мы запускаем приложение uvicorn
на порту 8000
.