Развёртывание полного AI-стека с Docker Compose: оркестрация в один клик от LLM до векторной базы данных
Настройка среды разработки AI всё ещё занимает три дня?
В 2026 году настройка среды разработки AI остаётся кошмаром для разработчиков. Нужно установить Ollama для LLM-сервинга, настроить Qdrant для векторного хранилища, настроить сервисы эмбеддингов, создать API-шлюз для аутентификации и разобраться с драйверами GPU, версиями CUDA и загрузкой моделей... Три дня прошло, а вы не написали ни строчки кода.
Развёртывание полного AI-стека с Docker Compose оркестрирует всё в одном файле. docker compose up -d запускает весь AI-стек за минуты. Эта статья — полное практическое руководство, охватывающее 7 основных паттернов, 5 распространённых ловушек, 10 шагов устранения ошибок и стратегии продакшн-усиления.
Ключевые выводы
- Развёртывание полного AI-стека с Docker Compose = LLM + Векторная БД + Эмбеддинги + API-шлюз + Мониторинг, один файл для управления всем
- Ollama + OpenWebUI — самое зрелое решение для локального LLM-сервинга
- Qdrant/Milvus — основные векторные базы данных с максимально простым Docker-развёртыванием
- Проброс GPU критически важен для AI-развёртывания — настраивается через
deploy.resources.reservations.devices - Продакшн требует аутентификации, ограничения запросов, мониторинга и усиления резервного копирования
Содержание
- Обзор архитектуры полного AI-стека
- Паттерн 1: LLM-сервинг с Ollama + OpenWebUI
- Паттерн 2: Векторные базы данных Qdrant/Milvus
- Паттерн 3: Сервисы эмбеддингов и управление моделями
- Паттерн 4: API-шлюз и аутентификация
- Паттерн 5: Проброс GPU и лимиты ресурсов
- Паттерн 6: Мониторинг и наблюдаемость
- Паттерн 7: Продакшн-усиление и безопасность
- 5 распространённых ловушек и решения
- 10 способов устранения распространённых ошибок
- Продвинутые советы по оптимизации
- Сравнение: Docker Compose vs K8s vs Docker Swarm
- Рекомендуемые онлайн-инструменты
- Итоги
Обзор архитектуры полного AI-стека
Развёртывание полного AI-стека с Docker Compose строится на 7-уровневой архитектуре, от GPU внизу до API-шлюза наверху:
┌─────────────────────────────────────────────────────┐
│ API Gateway │
│ (Traefik / Nginx) │
│ Auth · Rate Limit · Routing · TLS │
├──────────┬──────────┬──────────┬────────────────────┤
│ OpenWebUI│ RAG App │ Agent │ Admin Panel │
│ (Chat) │ (Search) │ (Proxy) │ (Management) │
├──────────┴──────────┴──────────┴────────────────────┤
│ Embedding Service │
│ (TEI / Infinity / FastEmbed) │
├──────────────────┬──────────────────────────────────┤
│ Ollama LLM │ vLLM / TGI │
│ (Model Serving) │ (High-Perf Inference) │
├──────────────────┴──────────────────────────────────┤
│ Vector Database │
│ (Qdrant / Milvus / Weaviate) │
├─────────────────────────────────────────────────────┤
│ Infrastructure │
│ Redis · PostgreSQL · MinIO · Prometheus │
├─────────────────────────────────────────────────────┤
│ GPU / CPU Runtime │
│ NVIDIA CUDA · ROCm · CPU Fallback │
└─────────────────────────────────────────────────────┘
Структура каталогов проекта для развёртывания полного AI-стека с Docker Compose:
ai-stack/
├── docker-compose.yml
├── docker-compose.gpu.yml
├── docker-compose.prod.yml
├── .env
├── ollama/
│ └── Modelfile
├── qdrant/
│ └── config.yaml
├── traefik/
│ ├── traefik.yml
│ └── acme.json
├── monitoring/
│ ├── prometheus.yml
│ └── grafana/
│ └── dashboards/
└── scripts/
├── init-models.sh
└── backup-vectors.sh
Паттерн 1: LLM-сервинг с Ollama + OpenWebUI
Ollama — самое зрелое решение для локального LLM-сервинга в 2026 году, поддерживающее Llama 4, Qwen 3, DeepSeek V3 и другие основные модели. OpenWebUI предоставляет веб-интерфейс в стиле ChatGPT.
Базовая конфигурация
services:
ollama:
image: ollama/ollama:latest
container_name: ollama
ports:
- "11434:11434"
volumes:
- ollama_data:/root/.ollama
environment:
OLLAMA_KEEP_ALIVE: "24h"
OLLAMA_NUM_PARALLEL: "4"
OLLAMA_MAX_LOADED_MODELS: "3"
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:11434/api/tags"]
interval: 30s
timeout: 10s
retries: 3
start_period: 60s
restart: unless-stopped
open-webui:
image: ghcr.io/open-webui/open-webui:main
container_name: open-webui
ports:
- "3000:8080"
volumes:
- open_webui_data:/app/backend/data
environment:
OLLAMA_BASE_URL: "http://ollama:11434"
WEBUI_SECRET_KEY: "${WEBUI_SECRET_KEY}"
ENABLE_SIGNUP: "false"
DEFAULT_USER_ROLE: "user"
depends_on:
ollama:
condition: service_healthy
restart: unless-stopped
volumes:
ollama_data:
open_webui_data:
Автоматическая загрузка моделей
После запуска Ollama необходимо загрузить модели вручную. Автоматизируйте это с помощью скрипта инициализации:
#!/bin/bash
# scripts/init-models.sh
MODELS=(
"qwen3:8b"
"llama4:8b"
"deepseek-v3:8b"
"nomic-embed-text"
)
for model in "${MODELS[@]}"; do
echo "Pulling model: $model"
until curl -s http://localhost:11434/api/pull -d "{\"name\":\"$model\"}" | grep -q "success"; do
echo " Retrying $model..."
sleep 5
done
echo " ✓ $model ready"
done
echo "All models pulled successfully!"
Добавьте сервис инициализации в Docker Compose:
model-init:
image: curlimages/curl:latest
container_name: model-init
depends_on:
ollama:
condition: service_healthy
volumes:
- ./scripts/init-models.sh:/init-models.sh:ro
entrypoint: ["/bin/sh", "/init-models.sh"]
restart: "no"
Пользовательский Modelfile
# ollama/Modelfile
FROM qwen3:8b
PARAMETER temperature 0.7
PARAMETER top_p 0.9
PARAMETER num_ctx 8192
PARAMETER stop "<|im_end|>"
SYSTEM """
You are a professional AI assistant. When answering questions:
1. Give a concise conclusion first
2. Then provide detailed explanation
3. If uncertain, say so explicitly
"""
Создайте пользовательскую модель:
docker exec ollama ollama create my-assistant -f /root/.ollama/Modelfile
Паттерн 2: Векторные базы данных Qdrant/Milvus
Векторные базы данных — ядро архитектуры RAG. Развёртывание полного AI-стека с Docker Compose обычно использует Qdrant (лёгкий) или Milvus (масштабный).
Конфигурация Qdrant (рекомендуется для малых и средних проектов)
qdrant:
image: qdrant/qdrant:latest
container_name: qdrant
ports:
- "6333:6333"
- "6334:6334"
volumes:
- qdrant_data:/qdrant/storage
- ./qdrant/config.yaml:/qdrant/config/production.yaml:ro
environment:
QDRANT__SERVICE__GRPC_PORT: "6334"
QDRANT__LOG_LEVEL: "INFO"
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:6333/healthz"]
interval: 10s
timeout: 5s
retries: 3
restart: unless-stopped
Конфигурационный файл Qdrant:
# qdrant/config.yaml
storage:
performance:
max_search_threads: 4
wal:
wal_capacity_mb: 32
wal_segments_ahead: 0
optimizers:
indexing_threshold: 20000
memmap_threshold: 50000
service:
max_request_size_mb: 64
enable_cors: true
telemetry_disabled: true
Конфигурация Milvus (рекомендуется для масштабных проектов)
etcd:
image: quay.io/coreos/etcd:v3.5.16
container_name: milvus-etcd
environment:
ETCD_AUTO_COMPACTION_MODE: "revision"
ETCD_AUTO_COMPACTION_RETENTION: "1000"
ETCD_QUOTA_BACKEND_BYTES: "4294967296"
volumes:
- etcd_data:/etcd
command: etcd -advertise-client-urls=http://127.0.0.1:2379 -listen-client-urls http://0.0.0.0:2379
restart: unless-stopped
minio:
image: minio/minio:latest
container_name: milvus-minio
environment:
MINIO_ACCESS_KEY: "${MINIO_ACCESS_KEY}"
MINIO_SECRET_KEY: "${MINIO_SECRET_KEY}"
ports:
- "9001:9001"
- "9000:9000"
volumes:
- minio_data:/minio_data
command: minio server /minio_data --console-address ":9001"
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:9000/minio/health/live"]
interval: 30s
timeout: 20s
retries: 3
restart: unless-stopped
milvus:
image: milvusdb/milvus:v2.5-latest
container_name: milvus
ports:
- "19530:19530"
- "9091:9091"
volumes:
- milvus_data:/var/lib/milvus
environment:
ETCD_ENDPOINTS: "etcd:2379"
MINIO_ADDRESS: "minio:9000"
depends_on:
- etcd
- minio
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:9091/healthz"]
interval: 30s
timeout: 20s
retries: 3
start_period: 90s
restart: unless-stopped
Сравнение векторных баз данных
| Характеристика | Qdrant | Milvus | Weaviate | ChromaDB |
|---|---|---|---|---|
| Сложность развёртывания | Очень низкая (1 контейнер) | Высокая (3+ контейнера) | Низкая (1 контейнер) | Очень низкая (1 контейнер) |
| Производительность (миллионы) | Отличная | Отличная | Хорошая | Удовлетворительная |
| Производительность (миллиарды) | Хорошая | Отличная | Удовлетворительная | Н/Д |
| Фильтрованный поиск | ✅ Мощный | ✅ Мощный | ✅ Хороший | ⚠️ Базовый |
| Персистентность | ✅ | ✅ | ✅ | ⚠️ По умолчанию в памяти |
| Мульти-реплика | ✅ | ✅ | ✅ | ❌ |
| Поддержка gRPC | ✅ | ✅ | ❌ | ❌ |
| Совместимость с Docker Compose | ✅ Лучшая | ⚠️ Тяжёлый | ✅ Хорошая | ✅ Только разработка |
| Готовность к продакшну | ✅ | ✅ | ✅ | ❌ Только разработка |
Рекомендация: Для развёртывания полного AI-стека с Docker Compose Qdrant — первый выбор: простое развёртывание, отличная производительность. Рассмотрите Milvus, когда количество векторов превышает 100 миллионов. ChromaDB подходит только для прототипирования.
Паттерн 3: Сервисы эмбеддингов и управление моделями
Сервисы эмбеддингов преобразуют текст в векторы — критический этап в конвейере RAG. Оркестрация контейнеров Docker Compose AI предлагает три основных решения.
Hugging Face TEI (рекомендуется)
tei:
image: ghcr.io/huggingface/text-embeddings-inference:latest
container_name: tei
ports:
- "8080:80"
volumes:
- tei_cache:/data
environment:
MODEL_ID: "BAAI/bge-m3"
REVISION: "main"
MAX_BATCH_TOKENS: "16384"
MAX_CLIENT_BATCH_SIZE: "32"
HF_TOKEN: "${HF_TOKEN}"
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:80/health"]
interval: 10s
timeout: 5s
retries: 3
start_period: 120s
restart: unless-stopped
Сервис эмбеддингов Infinity
infinity:
image: michaelf34/infinity:latest
container_name: infinity
ports:
- "7997:7997"
volumes:
- infinity_cache:/app/.cache
environment:
MODEL_ID: "BAAI/bge-m3"
ENGINE: "optimum"
BATCH_SIZE: "32"
command: >
--model-id BAAI/bge-m3
--engine optimum
--port 7997
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:7997/health"]
interval: 10s
timeout: 5s
retries: 3
restart: unless-stopped
Пример использования сервиса эмбеддингов
import httpx
import numpy as np
async def get_embeddings(texts: list[str]) -> list[list[float]]:
async with httpx.AsyncClient(timeout=30.0) as client:
response = await client.post(
"http://tei:80/embed",
json={"inputs": texts}
)
response.raise_for_status()
return response.json()
async def search_similar(query: str, top_k: int = 5) -> list[dict]:
query_embedding = await get_embeddings([query])
async with httpx.AsyncClient(timeout=30.0) as client:
response = await client.post(
"http://qdrant:6333/collections/documents/points/search",
json={
"vector": query_embedding[0],
"limit": top_k,
"with_payload": True
}
)
response.raise_for_status()
return response.json()["result"]
Сравнение сервисов эмбеддингов
| Характеристика | TEI | Infinity | FastEmbed |
|---|---|---|---|
| GPU-ускорение | ✅ Нативное | ✅ Нативное | ❌ Только CPU |
| Пакетный вывод | ✅ Эффективный | ✅ Эффективный | ⚠️ Удовлетворительный |
| Мульти-модель | ✅ | ✅ | ✅ |
| Размер Docker-образа | ~2ГБ | ~4ГБ | ~500МБ |
| Готовность к продакшну | ✅ | ✅ | ⚠️ Только разработка |
| API, совместимая с OpenAI | ✅ | ✅ | ❌ |
Паттерн 4: API-шлюз и аутентификация
Продакшн-развёртывание полного AI-стека с Docker Compose требует API-шлюза для унифицированной аутентификации, ограничения запросов и маршрутизации.
Конфигурация Traefik
traefik:
image: traefik:v3.2
container_name: traefik
ports:
- "80:80"
- "443:443"
- "8080:8080"
volumes:
- /var/run/docker.sock:/var/run/docker.sock:ro
- ./traefik/traefik.yml:/etc/traefik/traefik.yml:ro
- traefik_certs:/etc/traefik/certs
- ./traefik/dynamic:/etc/traefik/dynamic:ro
command:
- "--api.dashboard=true"
- "--providers.docker=true"
- "--providers.docker.exposedbydefault=false"
- "--providers.file.directory=/etc/traefik/dynamic"
- "--entrypoints.web.address=:80"
- "--entrypoints.websecure.address=:443"
- "--entrypoints.web.http.redirections.entrypoint.to=websecure"
labels:
traefik.enable: "true"
traefik.http.routers.traefik.rule: "Host(`traefik.ai-stack.local`)"
traefik.http.routers.traefik.entrypoints: "websecure"
traefik.http.routers.traefik.tls: "true"
traefik.http.services.traefik.loadbalancer.server.port: "8080"
restart: unless-stopped
OpenWebUI с Traefik
open-webui:
image: ghcr.io/open-webui/open-webui:main
container_name: open-webui
volumes:
- open_webui_data:/app/backend/data
environment:
OLLAMA_BASE_URL: "http://ollama:11434"
labels:
traefik.enable: "true"
traefik.http.routers.webui.rule: "Host(`chat.ai-stack.local`)"
traefik.http.routers.webui.entrypoints: "websecure"
traefik.http.routers.webui.tls: "true"
traefik.http.services.webui.loadbalancer.server.port: "8080"
depends_on:
ollama:
condition: service_healthy
restart: unless-stopped
Middleware аутентификации
# traefik/dynamic/auth.yml
http:
middlewares:
auth-middleware:
forwardAuth:
address: "http://auth-service:8000/verify"
trustForwardHeader: true
authResponseHeaders:
- "X-User-Id"
- "X-User-Role"
rate-limit:
rateLimit:
average: 30
burst: 60
period: 1m
routers:
api-router:
rule: "Host(`api.ai-stack.local`)"
entrypoints:
- "websecure"
tls: true
middlewares:
- "auth-middleware"
- "rate-limit"
service: "ollama-api"
Паттерн 5: Проброс GPU и лимиты ресурсов
GPU — ядро AI-развёртывания. Развёртывание полного AI-стека с Docker Compose включает проброс GPU через deploy.resources.reservations.devices.
Проброс NVIDIA GPU
ollama:
image: ollama/ollama:latest
container_name: ollama
volumes:
- ollama_data:/root/.ollama
deploy:
resources:
reservations:
devices:
- driver: nvidia
count: 1
capabilities: [gpu]
limits:
memory: 16G
cpus: "8.0"
environment:
NVIDIA_VISIBLE_DEVICES: "all"
NVIDIA_DRIVER_CAPABILITIES: "compute,utility"
OLLAMA_KEEP_ALIVE: "24h"
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:11434/api/tags"]
interval: 30s
timeout: 10s
retries: 3
restart: unless-stopped
Распределение нескольких GPU
ollama:
image: ollama/ollama:latest
deploy:
resources:
reservations:
devices:
- driver: nvidia
device_ids: ["0"]
capabilities: [gpu]
environment:
CUDA_VISIBLE_DEVICES: "0"
tei:
image: ghcr.io/huggingface/text-embeddings-inference:latest
deploy:
resources:
reservations:
devices:
- driver: nvidia
device_ids: ["1"]
capabilities: [gpu]
environment:
CUDA_VISIBLE_DEVICES: "1"
Конфигурация CPU-резерва
ollama-cpu:
image: ollama/ollama:latest
profiles: ["cpu-only"]
container_name: ollama
volumes:
- ollama_data:/root/.ollama
environment:
OLLAMA_NUM_PARALLEL: "2"
OLLAMA_MAX_LOADED_MODELS: "1"
deploy:
resources:
limits:
memory: 8G
cpus: "4.0"
ollama-gpu:
image: ollama/ollama:latest
profiles: ["gpu"]
container_name: ollama
volumes:
- ollama_data:/root/.ollama
deploy:
resources:
reservations:
devices:
- driver: nvidia
count: all
capabilities: [gpu]
limits:
memory: 16G
Команды запуска:
# Режим GPU
docker compose --profile gpu up -d
# Режим CPU
docker compose --profile cpu-only up -d
Скрипт мониторинга ресурсов GPU
import subprocess
import json
import time
def monitor_gpu_usage(interval: int = 60):
while True:
result = subprocess.run(
["nvidia-smi", "--query-gpu=index,name,memory.used,memory.total,utilization.gpu",
"--format=csv,noheader,nounits"],
capture_output=True, text=True
)
for line in result.stdout.strip().split("\n"):
idx, name, mem_used, mem_total, util = line.split(", ")
print(f"GPU {idx} ({name}): {mem_used}/{mem_total}MB, Util: {util}%")
time.sleep(interval)
if __name__ == "__main__":
monitor_gpu_usage()
Паттерн 6: Мониторинг и наблюдаемость
Мониторинг развёртывания полного AI-стека с Docker Compose должен охватывать утилизацию GPU, задержку вывода, производительность векторной базы данных и другие AI-специфические метрики.
Prometheus + Grafana
prometheus:
image: prom/prometheus:v3.2.0
container_name: prometheus
volumes:
- ./monitoring/prometheus.yml:/etc/prometheus/prometheus.yml:ro
- prometheus_data:/prometheus
command:
- "--config.file=/etc/prometheus/prometheus.yml"
- "--storage.tsdb.retention.time=30d"
- "--storage.tsdb.retention.size=10GB"
ports:
- "9090:9090"
restart: unless-stopped
grafana:
image: grafana/grafana:11.5.0
container_name: grafana
volumes:
- grafana_data:/var/lib/grafana
- ./monitoring/grafana/dashboards:/etc/grafana/provisioning/dashboards:ro
- ./monitoring/grafana/datasources:/etc/grafana/provisioning/datasources:ro
environment:
GF_SECURITY_ADMIN_USER: "${GRAFANA_USER}"
GF_SECURITY_ADMIN_PASSWORD: "${GRAFANA_PASSWORD}"
GF_USERS_ALLOW_SIGN_UP: "false"
ports:
- "3001:3000"
depends_on:
- prometheus
restart: unless-stopped
dcgm-exporter:
image: nvidia/dcgm-exporter:latest
container_name: dcgm-exporter
deploy:
resources:
reservations:
devices:
- driver: nvidia
count: all
capabilities: [gpu]
ports:
- "9400:9400"
restart: unless-stopped
Конфигурация Prometheus
# monitoring/prometheus.yml
global:
scrape_interval: 15s
evaluation_interval: 15s
scrape_configs:
- job_name: "ollama"
static_configs:
- targets: ["ollama:11434"]
metrics_path: "/metrics"
scrape_interval: 30s
- job_name: "qdrant"
static_configs:
- targets: ["qdrant:6333"]
metrics_path: "/metrics"
scrape_interval: 30s
- job_name: "dcgm"
static_configs:
- targets: ["dcgm-exporter:9400"]
scrape_interval: 10s
- job_name: "node-exporter"
static_configs:
- targets: ["node-exporter:9100"]
- job_name: "traefik"
static_configs:
- targets: ["traefik:8080"]
Ключевые правила оповещений
# monitoring/alerts.yml
groups:
- name: ai-stack
rules:
- alert: OllamaHighLatency
expr: histogram_quantile(0.95, rate(ollama_request_duration_seconds_bucket[5m])) > 30
for: 5m
labels:
severity: warning
annotations:
summary: "Задержка вывода Ollama слишком высока"
- alert: GPUMemoryHigh
expr: DCGM_FI_DEV_FB_USED / DCGM_FI_DEV_FB_TOTAL > 0.9
for: 5m
labels:
severity: critical
annotations:
summary: "Использование памяти GPU превышает 90%"
- alert: QdrantHighLatency
expr: histogram_quantile(0.95, rate(qdrant_request_duration_seconds_bucket[5m])) > 5
for: 5m
labels:
severity: warning
annotations:
summary: "Задержка запросов Qdrant слишком высока"
- alert: OllamaContainerDown
expr: up{job="ollama"} == 0
for: 2m
labels:
severity: critical
annotations:
summary: "Сервис Ollama недоступен"
Паттерн 7: Продакшн-усиление и безопасность
Безопасность — базовое требование для продакшн-развёртывания полного AI-стека с Docker Compose.
Управление секретами
services:
ollama:
image: ollama/ollama:latest
secrets:
- hf_token
environment:
HF_TOKEN_FILE: /run/secrets/hf_token
qdrant:
image: qdrant/qdrant:latest
secrets:
- qdrant_api_key
environment:
QDRANT__SERVICE__API_KEY_FILE: /run/secrets/qdrant_api_key
secrets:
hf_token:
file: ./secrets/hf_token.txt
qdrant_api_key:
file: ./secrets/qdrant_api_key.txt
db_password:
file: ./secrets/db_password.txt
Сетевая изоляция
networks:
frontend:
driver: bridge
backend:
driver: bridge
internal: true
monitoring:
driver: bridge
internal: true
services:
traefik:
networks:
- frontend
- backend
open-webui:
networks:
- frontend
- backend
ollama:
networks:
- backend
qdrant:
networks:
- backend
tei:
networks:
- backend
prometheus:
networks:
- monitoring
- backend
grafana:
networks:
- frontend
- monitoring
Стратегия резервного копирования
#!/bin/bash
# scripts/backup-vectors.sh
BACKUP_DIR="/backups/$(date +%Y%m%d_%H%M%S)"
mkdir -p "$BACKUP_DIR"
echo "Backing up Qdrant..."
curl -s -X POST "http://localhost:6333/snapshots" | jq .
echo "Backing up Ollama models list..."
curl -s "http://localhost:11434/api/tags" | jq . > "$BACKUP_DIR/ollama_models.json"
echo "Backing up environment config..."
cp .env "$BACKUP_DIR/.env.backup"
cp docker-compose.yml "$BACKUP_DIR/docker-compose.yml.backup"
echo "Backup completed: $BACKUP_DIR"
Полная продакшн-конфигурация
# docker-compose.prod.yml
services:
ollama:
deploy:
resources:
reservations:
devices:
- driver: nvidia
count: 1
capabilities: [gpu]
limits:
memory: 16G
cpus: "8.0"
restart_policy:
condition: on-failure
delay: 10s
max_attempts: 5
window: 120s
logging:
driver: json-file
options:
max-size: "100m"
max-file: "5"
read_only: true
tmpfs:
- /tmp
qdrant:
deploy:
resources:
limits:
memory: 4G
cpus: "2.0"
restart_policy:
condition: on-failure
delay: 5s
max_attempts: 3
logging:
driver: json-file
options:
max-size: "50m"
max-file: "3"
5 распространённых ловушек и решения
Ловушка 1: Таймаут загрузки моделей Ollama
Симптом: После docker compose up Ollama зависает при загрузке моделей. Большие модели (напр., Llama 4 70B) могут загружаться более часа.
Решение: Используйте сервис model-init для асинхронной загрузки. Сам сервис Ollama не должен ждать модели.
model-init:
image: curlimages/curl:latest
depends_on:
ollama:
condition: service_healthy
volumes:
- ./scripts/init-models.sh:/init-models.sh:ro
entrypoint: ["/bin/sh", "/init-models.sh"]
restart: "no"
deploy:
resources:
limits:
memory: 256M
Ловушка 2: OOM контейнера Qdrant
Симптом: По мере роста векторных данных контейнер Qdrant получает OOM Killed.
Решение: Установите лимиты памяти и включите отображение памяти.
qdrant:
image: qdrant/qdrant:latest
deploy:
resources:
limits:
memory: 8G
environment:
QDRANT__STORAGE__PERFORMANCE__MAX_SEARCH_THREADS: "4"
QDRANT__STORAGE__WAL__WAL_CAPACITY_MB: "64"
Ловушка 3: Несовпадение версии драйвера GPU
Симптом: docker compose up выдаёт ошибку CUDA driver version is insufficient.
Решение: Убедитесь, что драйвер NVIDIA на хосте ≥ 535, установите nvidia-container-toolkit.
# Проверить версию драйвера
nvidia-smi | head -3
# Установить nvidia-container-toolkit (Ubuntu)
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
sudo nvidia-ctk runtime configure --runtime=docker
sudo systemctl restart docker
Ловушка 4: Сервис эмбеддингов и LLM конкурируют за GPU
Симптом: Недостаточно памяти GPU при одновременной работе TEI и Ollama; загрузка модели не удаётся.
Решение: Используйте device_ids для точного распределения GPU или запускайте сервис эмбеддингов на CPU.
ollama:
deploy:
resources:
reservations:
devices:
- driver: nvidia
device_ids: ["0"]
capabilities: [gpu]
tei:
# Запуск сервиса эмбеддингов в режиме CPU
environment:
MODEL_ID: "BAAI/bge-m3"
# Без назначения GPU, используется CPU
Ловушка 5: Ошибка разрешения DNS между контейнерами
Симптом: OpenWebUI сообщает ollama: Name or service not known.
Решение: Убедитесь, что все сервисы в одной сети, используйте container_name или имя сервиса как имя хоста.
networks:
ai-network:
driver: bridge
services:
ollama:
container_name: ollama
networks:
- ai-network
open-webui:
container_name: open-webui
networks:
- ai-network
environment:
OLLAMA_BASE_URL: "http://ollama:11434"
10 способов устранения распространённых ошибок
1. could not select device driver — NVIDIA runtime не установлен
# Установить nvidia-container-toolkit и перезапустить Docker
sudo nvidia-ctk runtime configure --runtime=docker
sudo systemctl restart docker
docker info | grep -i runtime
# Должен отображаться nvidia runtime
2. OOM Killed — Недостаточно памяти GPU
# Проверить память GPU
nvidia-smi
# Использовать меньшие или квантованные модели
docker exec ollama ollama run qwen3:4b
3. Connection refused к Ollama — Сервис не готов
# Проверить статус здоровья Ollama
docker compose ps
docker compose logs ollama
# Дождаться прохождения healthcheck перед подключением
4. permission denied на Docker-сокете
sudo usermod -aG docker $USER
newgrp docker
5. Qdrant collection not found — Коллекция не создана
curl -X PUT "http://localhost:6333/collections/documents" \
-H "Content-Type: application/json" \
-d '{"vectors": {"size": 1024, "distance": "Cosine"}}'
6. model not found — Модель Ollama не загружена
docker exec ollama ollama pull qwen3:8b
7. CUDA out of memory — Переполнение памяти GPU при выводе
# Уменьшить параллельные запросы
# Настроить в docker-compose.yml
environment:
OLLAMA_NUM_PARALLEL: "1"
OLLAMA_MAX_LOADED_MODELS: "1"
8. TLS handshake error — Проблема с сертификатом Traefik
# Проверить права доступа к файлу сертификата
chmod 600 traefik/acme.json
# Проверить логи Traefik
docker compose logs traefik
9. too many open files — Лимит файловых дескрипторов
# Временно увеличить лимит
ulimit -n 65536
# Постоянная настройка (/etc/security/limits.conf)
# * soft nofile 65536
# * hard nofile 65536
10. vector dimension mismatch — Несовпадение размерностей эмбеддингов
# Убедиться, что размерность коллекции Qdrant соответствует выходу модели эмбеддингов
# bge-m3: 1024 размерности
# nomic-embed-text: 768 размерностей
curl -X PUT "http://localhost:6333/collections/documents" \
-d '{"vectors": {"size": 1024, "distance": "Cosine"}}'
Продвинутые советы по оптимизации
Многоэтапный прогрев моделей
model-warmer:
image: curlimages/curl:latest
container_name: model-warmer
depends_on:
ollama:
condition: service_healthy
entrypoint: >
/bin/sh -c "
echo 'Warming up models...' &&
curl -s http://ollama:11434/api/generate -d '{\"model\":\"qwen3:8b\",\"prompt\":\"hi\",\"stream\":false}' > /dev/null &&
curl -s http://ollama:11434/api/generate -d '{\"model\":\"nomic-embed-text\",\"prompt\":\"test\",\"stream\":false}' > /dev/null &&
echo 'Models warmed up!'
"
restart: "no"
Умная выгрузка моделей
ollama:
environment:
OLLAMA_KEEP_ALIVE: "5m"
OLLAMA_NUM_PARALLEL: "4"
OLLAMA_MAX_LOADED_MODELS: "2"
OLLAMA_KEEP_ALIVE: "5m" автоматически выгружает модели, простаивающие 5 минут, освобождая память GPU.
Цепочки зависимостей проверок здоровья
tei:
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:80/health"]
interval: 10s
timeout: 5s
retries: 5
start_period: 120s
qdrant:
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:6333/healthz"]
interval: 10s
timeout: 5s
retries: 3
rag-app:
depends_on:
ollama:
condition: service_healthy
tei:
condition: service_healthy
qdrant:
condition: service_healthy
Docker Compose Watch для разработки
# docker-compose.yml
services:
rag-app:
build: .
develop:
watch:
- action: rebuild
path: ./app
target: /app
- action: sync
path: ./app/static
target: /app/static
Сравнение: Docker Compose vs K8s vs Docker Swarm
| Измерение | Docker Compose | Kubernetes | Docker Swarm |
|---|---|---|---|
| Сложность развёртывания AI-стека | ⭐ Очень низкая | ⭐⭐⭐⭐⭐ Очень высокая | ⭐⭐ Низкая |
| Планирование GPU | ✅ Нативное | ✅ Device Plugin | ⚠️ Требует настройки |
| Автомасштабирование | ❌ | ✅ HPA | ⚠️ Ручное |
| Обнаружение сервисов | ✅ DNS | ✅ CoreDNS | ✅ DNS |
| Плавающие обновления | ⚠️ Требуют скриптов | ✅ Нативные | ✅ Нативные |
| Управление конфигурацией | ✅ .env | ✅ ConfigMap | ⚠️ Config |
| Управление секретами | ✅ Docker Secret | ✅ K8s Secret | ⚠️ Базовое |
| Экосистема мониторинга | ✅ Prometheus | ✅ Полная | ⚠️ Ограниченная |
| Многоузловая оркестрация | ❌ Один узел | ✅ Основная возможность | ✅ Нативная |
| Кривая обучения | Низкая | Высокая | Низкая |
| Активность сообщества | ✅ Активное | ✅ Очень активное | ❌ Снижается |
| Подходящий масштаб AI-проекта | 1-5 GPU | 10+ GPU | 2-5 GPU |
Рекомендация: Развёртывание полного AI-стека с Docker Compose идеально для сценариев с одной машиной и 1-5 GPU — лучший выбор для AI-разработки и малого продакшна. Для 5+ GPU или многоузловых потребностей рассмотрите Kubernetes + KServe/vLLM. Docker Swarm не рекомендуется для AI-развёртывания.
Рекомендуемые онлайн-инструменты
- Форматирование JSON - Форматирование JSON-данных Docker Compose и ответов API
- Кодирование Base64 - Кодирование конфигураций Secrets и API Key
- cURL в код - Конвертация cURL-команд Qdrant/Ollama в код Python/JS
Итоги
Развёртывание полного AI-стека с Docker Compose превращает настройку среды разработки AI из «три дня на настройку» в «одна команда для запуска». Ollama + OpenWebUI обслуживает LLM-сервинг, Qdrant — векторное хранилище, TEI — эмбеддинги, Traefik — шлюз, Prometheus + Grafana — мониторинг, а проброс GPU ускоряет вывод. 7 паттернов покрывают всю цепочку от разработки до продакшна, 5 распространённых ловушек и 10 шагов устранения ошибок помогут избежать обходных путей. Для AI-проектов с 1-5 GPU Docker Compose — самое практичное решение оркестрации AI-контейнеров в 2026 году.
Связанные публикации
- Продакшн-развёртывание Docker Compose - 7 продакшн-стратегий от проверок здоровья до обновлений без простоя
- Руководство по продакшн-развёртыванию Python AI - Лучшие практики продакшн-развёртывания моделей Python AI
- Руководство по усилению безопасности Docker - Усиление безопасности контейнеров и защита от уязвимостей
Внешние ссылки
- Официальная документация Ollama - Полная документация по LLM-сервингу Ollama
- Официальная документация Qdrant - Руководство по развёртыванию и оптимизации векторной базы данных
Попробуйте эти локальные браузерные инструменты — регистрация не требуется →