«У меня всё работает локально» — фраза, которая похоронила десятки AI-проектов на этапе деплоя. Разные версии CUDA, конфликты Python-зависимостей, несовместимость драйверов — всё это превращает перенос модели с ноутбука разработчика на сервер в квест. Docker решает эту проблему: вы упаковываете модель, зависимости, рантайм и GPU-конфигурацию в один контейнер, который идентично работает где угодно.

В этом руководстве — от базовой контейнеризации AI-проекта до продвинутых техник: GPU-ускорение через NVIDIA Container Toolkit, запуск LLM локально через Docker Model Runner, оптимизация образов multi-stage сборками и production-деплой.

Почему Docker критически важен для AI

В классической разработке контейнеризация — удобство. В AI/ML — необходимость. Типичный AI-проект тянет за собой десятки зависимостей с жёсткими требованиями к версиям: PyTorch 2.6 работает с CUDA 12.4, но ломается с 12.6; TensorFlow требует определённую версию cuDNN; даже NumPy между мажорными версиями ломает обратную совместимость.

ℹ Проблема воспроизводимости
По данным опросов ML-инженеров, до 40% времени в AI-проектах тратится на настройку окружения и решение конфликтов зависимостей. Docker сокращает эту цифру до минут — один docker pull, и окружение готово.

Вот что Docker даёт AI-разработчику:

ПроблемаБез DockerС Docker
Версии CUDA/cuDNNРучная установка, конфликтыЗафиксированы в образе
Python-зависимостиpip install ломает системный PythonИзолированы в контейнере
Воспроизводимость«У меня работает»Идентичное окружение везде
Деплой моделиРучная настройка сервераdocker run и готово
МасштабированиеСложная оркестрацияKubernetes + Docker = стандарт
Совместная работаREADME на 3 страницыdocker compose up

Docker-образ — это не просто упаковка кода. Это снимок всего окружения: ОС, драйверы, библиотеки, модель, конфигурация. Один артефакт от эксперимента до продакшена.

GPU-ускорение: NVIDIA Container Toolkit

Большинство AI-задач требуют GPU. Docker из коробки не видит видеокарты — для этого нужен NVIDIA Container Toolkit (ранее nvidia-docker). Он связывает контейнер с GPU хоста через Container Device Interface (CDI).

Установка

На хосте с Ubuntu и установленным NVIDIA-драйвером:

# Добавляем репозиторий NVIDIA
curl -fsSL https://nvidia.github.io/libnvidia-container/gpgkey | \
  sudo gpg --dearmor -o /usr/share/keyrings/nvidia-container-toolkit-keyring.gpg

curl -s -L https://nvidia.github.io/libnvidia-container/stable/deb/nvidia-container-toolkit.list | \
  sed 's#deb https://#deb [signed-by=/usr/share/keyrings/nvidia-container-toolkit-keyring.gpg] https://#g' | \
  sudo tee /etc/apt/sources.list.d/nvidia-container-toolkit.list

# Устанавливаем
sudo apt-get update && sudo apt-get install -y nvidia-container-toolkit

# Конфигурируем Docker runtime
sudo nvidia-ctk runtime configure --runtime=docker
sudo systemctl restart docker

Проверка

docker run --rm --gpus all nvidia/cuda:12.4.0-base-ubuntu22.04 nvidia-smi

Если вы видите таблицу с информацией о GPU — всё работает.

⚠ Совместимость версий
Версия CUDA в контейнере должна быть совместима с драйвером на хосте. Правило: драйвер поддерживает свою версию CUDA и все предыдущие. Драйвер 550+ поддерживает CUDA 12.x. Проверяйте матрицу совместимости в документации NVIDIA.

GPU в Docker Compose

services:
  inference:
    image: pytorch/pytorch:2.6.0-cuda12.4-cudnn9-runtime
    deploy:
      resources:
        reservations:
          devices:
            - driver: nvidia
              count: 1  # или "all" для всех GPU
              capabilities: [gpu]
    volumes:
      - ./models:/app/models
    ports:
      - "8000:8000"

graph LR
    A[Приложение в контейнере] --> B[NVIDIA Container Toolkit]
    B --> C[NVIDIA Driver на хосте]
    C --> D[GPU — CUDA Cores]
    B --> E[CDI — Container Device Interface]
    E --> C

Контейнеризация AI-проекта: от Dockerfile до деплоя

Базовые образы для AI

Выбор базового образа определяет размер контейнера, производительность и набор доступных библиотек.

ОбразРазмерGPUКогда использовать
python:3.12-slim~150 MBНетCPU-инференс, обработка данных
pytorch/pytorch:2.6.0-cuda12.4-cudnn9-runtime~5.5 GBДаPyTorch + GPU-инференс
tensorflow/tensorflow:2.18.0-gpu~6 GBДаTensorFlow + GPU
nvidia/cuda:12.4.0-devel-ubuntu22.04~3.5 GBДаКастомная сборка с CUDA
huggingface/transformers-pytorch-gpu~8 GBДаHuggingFace Transformers
💡 runtime vs devel
Образы NVIDIA CUDA бывают двух типов: runtime (только библиотеки для запуска) и devel (компилятор nvcc, заголовочные файлы). Для инференса всегда берите runtime — он на 2–3 GB легче. devel нужен только если вы компилируете CUDA-код внутри контейнера.

Multi-stage сборка для ML-проекта

Multi-stage build — ключевая техника для оптимизации AI-образов. Идея: собираем зависимости в тяжёлом билд-контейнере, а в финальный образ копируем только нужное.

# === Стадия 1: установка зависимостей ===
FROM python:3.12-slim AS builder

WORKDIR /app
COPY requirements.txt .

RUN pip install --no-cache-dir --prefix=/install -r requirements.txt

# === Стадия 2: финальный образ ===
FROM python:3.12-slim AS runtime

WORKDIR /app

# Копируем установленные пакеты из builder
COPY --from=builder /install /usr/local
COPY . .

# Не запускаем от root
RUN useradd -m appuser
USER appuser

EXPOSE 8000
CMD ["uvicorn", "app:app", "--host", "0.0.0.0", "--port", "8000"]

Для GPU-проектов с PyTorch:

# === Build stage ===
FROM pytorch/pytorch:2.6.0-cuda12.4-cudnn9-devel AS builder

WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir --prefix=/install -r requirements.txt

# === Runtime stage ===
FROM pytorch/pytorch:2.6.0-cuda12.4-cudnn9-runtime

WORKDIR /app
COPY --from=builder /install /opt/conda
COPY . .

EXPOSE 8000
CMD ["python", "serve.py"]

Результат multi-stage сборки: финальный образ на 30–50% легче, чем при однослойной сборке. Для образов на базе CUDA это экономия 2–4 GB.

Оптимизация размера образа

AI-образы печально известны своим размером. Несколько приёмов:

# 1. Объединяйте RUN-команды — меньше слоёв
RUN apt-get update && \
    apt-get install -y --no-install-recommends libgl1 libglib2.0-0 && \
    rm -rf /var/lib/apt/lists/*

# 2. Используйте --no-cache-dir для pip
RUN pip install --no-cache-dir torch torchvision

# 3. Удаляйте ненужное после установки
RUN pip install --no-cache-dir flash-attn && \
    find /opt/conda -name "*.pyc" -delete && \
    find /opt/conda -name "__pycache__" -type d -exec rm -rf {} +

# 4. Используйте .dockerignore
# .dockerignore:
# .git
# __pycache__
# *.pyc
# data/raw/
# wandb/
# checkpoints/

graph TD
    A[Dockerfile] --> B{Multi-stage?}
    B -->|Да| C[Build stage: devel-образ]
    C --> D[Установка зависимостей]
    D --> E[Runtime stage: runtime-образ]
    E --> F[Копирование только нужного]
    F --> G[Финальный образ: минимальный размер]
    B -->|Нет| H[Один слой: всё вместе]
    H --> I[Образ +2-4 GB лишнего]

Docker Model Runner: LLM прямо в Docker

В 2025 году Docker представил Model Runner — встроенный инструмент для запуска LLM локально. Модели хранятся как OCI-артефакты (тот же стандарт, что и для Docker-образов) и запускаются через OpenAI-совместимый API.

Как это работает

# Скачиваем модель
docker model pull ai/llama3.2:3B-Q8_0

# Запускаем инференс
docker model run ai/llama3.2:3B-Q8_0 "Объясни Docker в одном абзаце"

Model Runner поднимает API на localhost:12434, совместимый с OpenAI SDK. Любой инструмент, работающий с OpenAI API, можно направить на Model Runner:

from openai import OpenAI

client = OpenAI(
    base_url="http://localhost:12434/engines/llama3.2/v1/",
    api_key="not-needed"
)

response = client.chat.completions.create(
    model="ai/llama3.2:3B-Q8_0",
    messages=[{"role": "user", "content": "Что такое контейнеризация?"}]
)
print(response.choices[0].message.content)

Движки инференса

ДвижокGPUПлатформыНазначение
llama.cppMetal, CUDA, VulkanВсеУмолчание, универсальный
vLLMNVIDIA CUDALinux x86_64, Windows WSL2Высокая пропускная способность
DiffusersNVIDIA CUDALinuxГенерация изображений

Model Runner в Docker Compose

services:
  app:
    build: .
    environment:
      - OPENAI_BASE_URL=http://model-runner.docker.internal/engines/llama3.2/v1/
    ports:
      - "8080:8080"

  model-runner:
    provider:
      type: model
      options:
        model: ai/llama3.2:3B-Q8_0
💡 Model Runner vs Ollama
Docker Model Runner не заменяет Ollama или vLLM — он дополняет их. Главное преимущество: модели живут в той же экосистеме, что и остальные контейнеры. Единый docker compose up поднимает и приложение, и модель. Для команд, где Docker уже в стеке, это упрощает workflow.

Production-деплой: чеклист и паттерны

Контейнеризация — половина дела. Для продакшена нужны дополнительные паттерны.

Health Checks

HEALTHCHECK --interval=30s --timeout=10s --retries=3 \
  CMD curl -f http://localhost:8000/health || exit 1

Для ML-сервисов health check должен проверять не только HTTP-ответ, но и доступность модели:

@app.get("/health")
async def health():
    try:
        # Проверяем что модель загружена и GPU доступен
        dummy = torch.tensor([1.0]).to(device)
        return {"status": "ok", "gpu": torch.cuda.is_available()}
    except Exception as e:
        return JSONResponse(status_code=503, content={"status": "error"})

Управление ресурсами

services:
  model-api:
    image: my-model:latest
    deploy:
      resources:
        limits:
          memory: 16G
        reservations:
          memory: 8G
          devices:
            - driver: nvidia
              count: 1
              capabilities: [gpu]
    # Перезапуск при падении
    restart: unless-stopped
    # Логирование
    logging:
      driver: json-file
      options:
        max-size: "50m"
        max-file: "3"

Полный docker-compose.yml для AI-сервиса

version: "3.9"

services:
  api:
    build:
      context: .
      dockerfile: Dockerfile
    ports:
      - "8000:8000"
    volumes:
      - model-cache:/app/models
    environment:
      - MODEL_NAME=llama-3.2-3b
      - DEVICE=cuda
      - NUM_WORKERS=2
    deploy:
      resources:
        reservations:
          devices:
            - driver: nvidia
              count: 1
              capabilities: [gpu]
    restart: unless-stopped
    healthcheck:
      test: ["CMD", "curl", "-f", "http://localhost:8000/health"]
      interval: 30s
      timeout: 10s
      retries: 3

  redis:
    image: redis:7-alpine
    volumes:
      - redis-data:/data

  prometheus:
    image: prom/prometheus:latest
    volumes:
      - ./prometheus.yml:/etc/prometheus/prometheus.yml
    ports:
      - "9090:9090"

volumes:
  model-cache:
  redis-data:
⚠ Безопасность
Не запускайте контейнеры от root. Не храните API-ключи и секреты в образе — используйте Docker secrets или переменные окружения. Сканируйте образы на уязвимости через docker scout cves перед деплоем.

Пайплайн от разработки до продакшена


graph LR
    A[Разработка] --> B[Dockerfile + compose]
    B --> C[CI: Build + Test]
    C --> D[Registry: Push образа]
    D --> E{Окружение}
    E --> F[Staging: docker compose]
    E --> G[Production: Kubernetes]
    F --> H[Тесты + мониторинг]
    H --> G

Заключение

Docker стал стандартом де-факто для AI-проектов — от экспериментов на ноутбуке до production-инференса с GPU-кластерами. Вот ключевые выводы:

  • NVIDIA Container Toolkit — обязательный компонент для GPU-проектов. Установка занимает минуты, а проблемы с CUDA-совместимостью исчезают навсегда.
  • Multi-stage сборки сокращают размер AI-образов на 30–50%. Разделяйте devel и runtime стадии.
  • Docker Model Runner упрощает работу с LLM, встраивая инференс в привычный Docker-workflow с OpenAI-совместимым API.
  • Production-деплой требует health checks, лимитов ресурсов, логирования и сканирования безопасности — Docker Compose покрывает всё это декларативно.

Начните с контейнеризации текущего проекта: напишите Dockerfile, добавьте .dockerignore, настройте Compose — и вы получите окружение, которое одинаково работает у каждого в команде и на каждом сервере.