Контейнеризация ML-моделей давно перестала быть экзотикой. Однако в 2025 году настоящая индустриальная зрелость достигается только тогда, когда ML-продуктов становится десятки, inference идёт на GPU, обновления катятся в production ежедневно, а инфраструктура автосборки и логирования не ломается от одного слова “cuda”. Контейнеризация таких систем — это не просто docker build
и FROM pytorch/pytorch
. Это архитектурное ремесло.
Ниже — подробная, практическая архитектура контейнеризации ML-инференса для GPU, форматируемая через современный stack: CUDA-aware images, mulitstage Docker build, оптимизация размера образов, reproducible builds и runtime стратегии.
Ключевая задача: reproduce + run на GPU + обновляемо + быстро собирается
Цель — собрать стабильный, предсказуемый, лёгкий контейнер, поддерживающий:
- Воспроизводимость (на разных CI runner’ах)
- GPU-инференс (CUDA Runtime)
- Горячее обновление модели без перекомпиляции всего контейнера
- Минимальный размер финального образа
- Возможность хостинга в регистри без платной тарификации за jumbo images
Подход: трёхстадийная сборка образа
Стадия 1: Builder — Python deps + CUDA headers из исходников
# Builder stage — полный dev stack (CUDA toolkits, compilers, headers)
FROM nvidia/cuda:12.2.0-devel-ubuntu22.04 AS builder
ENV DEBIAN_FRONTEND=noninteractive
RUN apt-get update && apt-get install -y \
python3.10 python3.10-venv python3-pip build-essential \
git wget curl cmake \
&& rm -rf /var/lib/apt/lists/*
RUN python3.10 -m venv /opt/venv
ENV PATH="/opt/venv/bin:$PATH"
# pip с последним wheel
RUN pip install --upgrade pip wheel
# Копируем requirements.txt и устанавливаем в venv
COPY requirements.txt .
RUN pip install -r requirements.txt
Здесь собирается виртуальное окружение со всеми необходимыми зависимостями. Модули с C-расширениями (например, torchvision с компиляцией CUDA-расширений) важно собирать именно на этой стадии — они часто требуют заголовочных файлов cuda.h
, которые на runtime не нужны.
Стадия 2: Base runtime GPU — минимальный CUDA runtime + env
# Runtime stage — минимальный CUDA runtime + system deps
FROM nvidia/cuda:12.2.0-runtime-ubuntu22.04 AS base
ENV DEBIAN_FRONTEND=noninteractive
RUN apt-get update && apt-get install -y \
python3.10 libgl1 libglib2.0-0 \
&& rm -rf /var/lib/apt/lists/*
COPY --from=builder /opt/venv /opt/venv
ENV PATH="/opt/venv/bin:$PATH"
Контейнер на этой стадии содержит минимальный runtime — только CUDA runtime и нужные libs. Отдельно копируется виртуальное окружение Python из стадии builder. Это уменьшает final image size в 3-5 раз.
Стадия 3: Final (optional) — прокладка модели, API endpoint
FROM base AS final
WORKDIR /app
# entrypoint
COPY ./serve.py .
COPY ./custom_module.py .
# runtime model weights (горячее обновление возможно через volume mount)
COPY ./models /models
CMD ["python", "serve.py"]
В этой стадии добавляются бизнес-специфичные части: API-обёртка, логика inference, логеры и, при желании, модель. На практике модель можно монтировать как volume для избавления от rebuild при hot-swap проверках в staging-средах.
Выгоды от мультиярусной сборки
- Финальный образ ~400MB вместо типичных 3-4GB (в 2025 году это уже важно для edge inference)
- Повторное использование builder stage в CI. Можно собирать множество моделей против одного builder snapshot
- Компиляция зависимостей
torchvision
,xformers
,clip
etc. один раз, не при каждомdocker build context
Работа с GPU: запуск inference в контейнере с nvidia-container-runtime
Чтобы контейнер мог использовать GPU, требуется:
- Установленный NVIDIA Container Toolkit (
nvidia-container-runtime
) docker run
с параметром:
docker run --gpus=all --rm my-inference-container
Проверка GPU:
import torch
print(torch.cuda.is_available()) # True
print(torch.cuda.get_device_name(0)) # NVIDIA A100-SXM4-40GB
Если is_available()
возвращает False
, проблема почти всегда в mismatch между host NVIDIA driver version и CUDA runtime в контейнере. Пример:
- Host driver: 535.x
- Container CUDA runtime: 12.2
- Соответствие: OK (главное — driver >= runtime)
Под капотом: host драйвер обеспечивает реализацию ядра, контейнеру требуется лишь API runtime. Совпадения “точь в точь” не обязательно.
Хранение модели: монтировать или bake в слой?
Есть два подхода к размещению модельных весов:
-
Монтировать через Docker volume:
- размонтируемо (удобно в staging)
- минимальный размер образа
- гибко для A/B тестов
-
Bake внутрь образа:
- пригодно для offline deployment (например, edge на клиентской машине)
- проще “провести SDLC как артефакт”
- можно подписывать и кэшировать как immutable content
Обычно в prodоритет bake + версии через CI. В staging и early debug достаточно монтирования:
docker run -v $(pwd)/models:/models --gpus=all myimage:latest
Что ломается чаще всего
1. Ошибки при запуске: failed to initialize CUDA context
Причины:
-
Контейнер собран на CUDA 12.x, а драйвер на хосте старее (например, 515). Обновить драйвер.
-
Контейнер не запущен с
--gpus=all
. Требуется явное указание GPU-доступа.
2. Не работает torchvision с CUDA
torchvision
должен быть собран против конкретной сборки torch
и поддерживать ту же версию CUDA. Легче всего — ставить их из одного wheel source.
Плохо:
RUN pip install torch torchvision
Хорошо:
RUN pip install torch==2.2.0+cu122 torchvision==0.17.0+cu122 -f https://download.pytorch.org/whl/torch_stable.html
3. Слишком большой образ (3+ ГБ)
Обычно из-за:
- полных dev toolkits (gcc, компиляторы, cmake) в финальном образе
pip install
в runtime, не в builder- кэширования локальных моделей в
~/.cache/torch
,huggingface/transformers
Fix: использовать .dockerignore
, вынести pip
в фазу builder, удалять кэш перед final stage:
RUN rm -rf ~/.cache
Рекомендации для CI / CD
- Builder образ (stage 1) можно публиковать отдельно и переиспользовать с тегом
ci::torch-2.2-cu122-builder
- Final image публикуется с тегом версии модели
ml-service:resnet50-v3.12
- Используйте digest-based pull вместо latest
- Проверяйте хэш моделей перед bake (например, через SHA256 в
models/manifest.json
)
Выводы
Контейнеризация inference-сервисов для GPU — деталезависимая инженерная задача. Без избыточности правильное построение образов требует разумного разделения стадий (build/runtime), контроля над CUDA runtime совместимостью, и осознанного выбора: baked модель vs. mounted. Архитектура, приведённая выше, масштабируется, ускоряет CI/CD, и сокращает время rollout'а с часов до минут — с полной поддержкой GPU в продакшн-средах.
Сложное поведение читается проще, когда инженеры рассматривают не только технические зависимости, но и свойства жизненного цикла моделей.
Будьте тактичны с CUDA, и она отплатит вам снижением latency на миллисекунды.