Применение python:slim для легковесных контейнеров Docker

Раздел: DevOps -> Контейнеризация

Выбор базового образа Python для Docker

При создании Docker контейнеров на Python размер образа влияет на скорость развертывания и затраты на хранение. Официальные образы Python имеют несколько вариантов: полный (latest), облегченный (slim) и минимальный (alpine). В этой статье рассматривается python:slim как сбалансированное решение для production сред, а также альтернативы с их особенностями.

Основное решение: python:slim

Как уменьшить итоговый размер образа Docker, сохранив совместимость с большинством библиотек?

Образ python:slim базируется на Debian, но содержит только минимально необходимые системные библиотеки и утилиты. Его размер около 45 МБ (против 100+ МБ у полного образа). Он подходит для большинства приложений, использующих чистый Python или пакеты с предварительно собранными колесами (wheels).

FROM python:3.12-slim
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY . .
CMD ["python", "app.py"]

Docker python slim (использование образа python:slim в docker)

Пояснение шагов:

  • FROM - указывается образ python:3.12-slim (версию рекомендуется фиксировать).
  • WORKDIR - создается рабочая директория /app.
  • COPY - сначала копируется только файл зависимостей, чтобы использовать кэш слоев Docker при пересборке.
  • RUN pip install - установка пакетов без сохранения кэша pip (флаг --no-cache-dir).
  • COPY . . - копирование остального кода.
  • CMD - команда запуска приложения.

Возможные проблемы и их решения

  • Некоторые пакеты (например, psycopg2, cryptography) требуют компиляции C расширений. В slim нет компилятора gcc. Решение: установить build-essential через apt, либо использовать образ с предустановленными инструментами (например, python:3.12-slim-buster с пакетами). Пример:
RUN apt-get update && apt-get install -y --no-install-recommends \
    gcc libc6-dev && rm -rf /var/lib/apt/lists/*

После установки зависимостей компилятор можно удалить, чтобы не увеличивать итоговый образ (лучше использовать multistage build).

Вариант 1: python:latest (полный образ)

Как обеспечить максимальную совместимость с любыми библиотеками без дополнительных настроек?

Полный образ Python основан на Debian и включает все системные пакеты, компиляторы, утилиты (gcc, make, curl и т.д.). Он идеален для разработки или случаев, когда размер не критичен. Однако размер может достигать 1 ГБ после установки зависимостей.

FROM python:3.12
# ...

Недостатки

  • Большой размер образа (базовый ~100 МБ, после установки пакетов может превышать 500 МБ).
  • Лишние пакеты увеличивают поверхность атаки и время развертывания.

Вариант 2: python:alpine (минимальный образ)

Как достичь минимального размера образа (около 15 МБ базовый)?

Образ на основе Alpine Linux использует musl libc вместо glibc. Это часто вызывает проблемы с установкой многих пакетов (например, numpy, pandas) из-за отсутствия glibc-совместимости. Требуется установка дополнительных инструментов (build-base).

FROM python:3.12-alpine
RUN apk add --no-cache gcc musl-dev
# ...

Типичные ошибки

  • Ошибка "Could not build wheels for psycopg2" - требуется libpq-dev. Для alpine пакет называется postgresql-dev.
  • Ошибка "missing libc.musl-x86_64.so.1" - приложение ожидает glibc. Решение: использовать python:alpine только для приложений, не использующих бинарные расширения.
  • Время установки увеличивается из-за компиляции из исходников, так как многие wheels не собраны для musl.

Вариант 3: Multistage build с финальным slim

Как разделить этапы сборки и выполнения, чтобы в итоговом образе остались только необходимые артефакты?

Многоэтапная сборка позволяет на первой стадии использовать полный образ для компиляции, а на второй - скопировать только установленные пакеты и код в slim образ.

# Stage 1: build
FROM python:3.12 AS builder
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt

# Stage 2: final
FROM python:3.12-slim
WORKDIR /app
COPY --from=builder /usr/local/lib/python3.12/site-packages /usr/local/lib/python3.12/site-packages
COPY --from=builder /usr/local/bin /usr/local/bin
COPY . .
CMD ["python", "app.py"]

Важно: копируются только каталоги site-packages и bin, без временных файлов компилятора.

Сложности

  • Необходимо точно указать пути, куда pip устанавливает пакеты. Иногда это /usr/local/lib/python3.12/site-packages. Можно использовать pip show для проверки.
  • Если приложение использует динамические библиотеки (.so), они должны быть скопированы из builder. Для этого можно скопировать только нужные файлы.

Расширенные примеры использования python:slim

1. Установка системных зависимостей для пакета psycopg2

Пакет psycopg2 требует библиотеку libpq. В slim-образе ее нет, поэтому необходимо установить через apt.

Пример
FROM python:3.12-slim
RUN apt-get update && apt-get install -y --no-install-recommends \
    libpq-dev gcc && rm -rf /var/lib/apt/lists/*
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY . .
CMD ["python", "app.py"]

Результат: образ собирается успешно, размер увеличивается примерно на 30 МБ из-за установки компилятора. Для минимизации можно использовать multistage build, где на финальном этапе устанавливается только libpq5 (runtime библиотека).

Пример
# Stage 1: build
FROM python:3.12-slim AS builder
RUN apt-get update && apt-get install -y --no-install-recommends gcc libpq-dev && rm -rf /var/lib/apt/lists/*
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt

# Stage 2: final
FROM python:3.12-slim
RUN apt-get update && apt-get install -y --no-install-recommends libpq5 && rm -rf /var/lib/apt/lists/*
WORKDIR /app
COPY --from=builder /usr/local/lib/python3.12/site-packages /usr/local/lib/python3.12/site-packages
COPY --from=builder /usr/local/bin /usr/local/bin
COPY . .
CMD ["python", "app.py"]

2. Использование .dockerignore для уменьшения контекста сборки

Файл .dockerignore исключает лишние файлы, что ускоряет сборку и уменьшает размер образа.

Пример
# .dockerignore
__pycache__
*.pyc
*.pyo
.git
.gitignore
.env
Dockerfile
README.md

3. Проверка размера образа

После сборки можно проверить размер с помощью docker images или docker history.

Пример
$ docker build -t myapp-slim .
$ docker images myapp-slim
REPOSITORY   TAG       IMAGE ID       CREATED        SIZE
myapp-slim   latest    abc123         2 minutes ago  156 MB

Более детальный анализ слоев:

Пример
$ docker history myapp-slim
IMAGE          CREATED              CREATED BY                                      SIZE
def456         2 minutes ago        CMD ["python", "app.py"]                       0B
...
123abc         3 minutes ago        RUN pip install ...                            95 MB
...

4. Пример для FastAPI с использованием slim

Пример
FROM python:3.12-slim
RUN apt-get update && apt-get install -y --no-install-recommends curl && rm -rf /var/lib/apt/lists/*
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY . .
EXPOSE 8000
CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8000"]

Запуск контейнера:

Пример
$ docker build -t fastapi-slim .
$ docker run -p 8000:8000 fastapi-slim

5. Оптимизация pip установки с помощью --only-binary

Для ускорения сборки и уменьшения зависимостей можно запретить исходные сборки и использовать только колеса.

Пример
RUN pip install --only-binary=:all: --no-cache-dir -r requirements.txt

Внимание:

Эта опция может привести к ошибке, если для какого-то пакета нет готового колеса. В таком случае стоит добавить исключения.

6. Копирование только необходимых файлов с помощью COPY с флагом --chown

Пример
FROM python:3.12-slim
RUN groupadd -r appuser && useradd -r -g appuser appuser
WORKDIR /app
COPY --chown=appuser:appuser requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY --chown=appuser:appuser . .
USER appuser
CMD ["python", "app.py"]

Это повышает безопасность, запуская контейнер от непривилегированного пользователя.

Использование образа python:slim в Docker - comments

En
Docker python slim (python)