Краткий вывод: Для стабильного продакшен-развертывания стека LiteLLM Proxy и Langfuse в Docker Compose необходим KVM-сервер минимум с 4 vCPU (%st = 0.0%), 8 ГБ RAM (с жестким лимитированием памяти ClickHouse и PostgreSQL через cgroups v2) и 80 ГБ NVMe (PCIe 4.0, от 15 000 IOPS 4K QD1) во избежание I/O-блокировок при слиянии аналитических партиций и ротации WAL. Сетевой контур требует выделенного аплинка от 1 Гбит/с с алгоритмом TCP BBR для удержания p99 latency сквозного SSE-стриминга (Server-Sent Events) в пределах 15 мс и непрерывного gRPC/OTel-экспорта трейсов.
Содержание
- Архитектура корпоративного LLM-шлюза: почему прямой доступ к API не работает в продакшне
- Аппаратный сайзинг KVM VPS: расчет RAM и NVMe под PostgreSQL, Redis и ClickHouse
- Развертывание полного стека LiteLLM + Langfuse в Docker Compose
- Настройка семантического кэширования и автоматического фейловера между провайдерами
- Управление бюджетами команд, квоты токенов и маскирование PII-данных
- Отказоустойчивость, бэкапы баз данных и регламент аварийного восстановления
- Часто задаваемые вопросы (FAQ)
Архитектура корпоративного LLM-шлюза: почему прямой доступ к API не работает в продакшне
Прямое обращение распределенных микросервисов к публичным эндпоинтам внешних провайдеров (OpenAI, Anthropic, Google Vertex AI, Mistral) — классический антипаттерн, приводящий к каскадным сбоям инфраструктуры при первом же непрогнозируемом всплеске трафика. В архитектуре без промежуточного балансирующего слоя каждый экземпляр бэкенда самостоятельно инициирует TLS-сессии, управляет собственным пулом соединений и изолированно обрабатывает ответы upstream API. На масштабе от нескольких сотен запросов в секунду (RPS) эта схема порождает комплексную деградацию: лавинообразные отказы по таймаутам, исчерпание пула сокетов операционной системы, неконтролируемый перерасход бюджета и полную потерю контроля над передаваемыми данными.
Анатомия деградации: каскадные отказы и исчерпание сетевого стека
Ключевой фактор нестабильности прямого доступа — разница во времени ответа (latency profile) классических микросервисов и генеративных моделей. Если стандартный CRUD-запрос к СУБД или кэшу выполняется за 2–15 мс, то генерация ответа большой языковой моделью (LLM) с потоковой передачей токенов (Server-Sent Events) удерживает активное TCP-соединение от 1,5 до 45 секунд.
При резком росте нагрузки прямое обращение вызывает следующие сетевые и архитектурные патологии:
- Лавина повторных попыток (Retry Storms) и HTTP 429 (Too Many Requests): Внешние провайдеры лимитируют трафик по двум жестким метрикам — RPM (Requests Per Minute) и TPM (Tokens Per Minute). Когда десяток независимых воркеров синхронно получают статус 429, их встроенные механизмы повтора (даже с экспоненциальной задержкой Exponential Backoff без глобального джиттера) синхронизируются. Возникает проблема Thundering Herd: запросы циклически перегружают API, исчерпывают скользящее окно квот провайдера и переводят микросервисы в перманентное состояние блокировки.
- Исчерпание локальных сокетов и деградация TCP-стека Linux: Каждый HTTP-запрос во внешнюю сеть требует выделения эфемерного порта из пула
net.ipv4.ip_local_port_range. При удержании соединений десятками секунд и аварийном закрытии по таймауту сокеты переходят в состояниеTIME_WAIT(по умолчанию удерживаются ядром 60 секунд). При отсутствии централизованного пулинга на хосте наступает нехватка свободных дескрипторов:
# Диагностика переполнения сокетов в состоянии TIME_WAIT и TCP backoff
ss -s
cat /proc/sys/net/ipv4/tcp_tw_reuse
netstat -s | grep -E "overflows|dropped"
- Отсутствие Circuit Breaker и каскадная блокировка пула потоков: В синхронных и асинхронных рантаймах (Node.js event loop, Python AsyncIO, пул потоков Gunicorn/Uvicorn) длительное ожидание байтов генерации исчерпывает воркеры. При падении upstream-провайдера (Outage) сервисы продолжают ждать ответа вплоть до истечения TCP read timeout (часто настроенного на 60–120 секунд), вызывая глобальный паралич всей клиентской очереди.
Утечки бюджетов, отсутствие аудита и риски утечки PII
Прямая раздача сырых токенов авторизации (OPENAI_API_KEY, ANTHROPIC_API_KEY) воркерам распределенной системы полностью разрушает контур безопасности:
- Отсутствие гранулярного контроля бюджетов (Hard/Soft Limits): Провайдеры предоставляют биллинг на уровне организации или глобального API-ключа. Неконтролируемый цикл в ReAct-агенте (зацикливание автономного агента при вызове инструментов) способен за несколько часов сжечь месячный бюджет проекта, не встретив локального ограничителя.
- Слепое пятно наблюдаемости (Zero Observability): Логирование запросов в прикладной код микросервиса либо перегружает хранилище логов сырыми мегабайтами контекста, либо не ведется вовсе. Инженеры лишены возможности оценить реальную задержку до первого токена (Time to First Token, TTFT), распределение затрат по командам или проектам, а также процент кэшируемых промптов.
- Нарушение требований безопасности и PII (Personally Identifiable Information): При прямом взаимодействии невозможно внедрить централизованную валидацию и маскирование чувствительных данных (номера карт, паспортные данные, API-токены внутренней инфраструктуры) до их отправки во внешнее облако.
Сравнительный анализ архитектур доступа к моделям
Для обеспечения отказоустойчивости корпоративного уровня прямое обращение заменяется выделенным слоем абстракции — шлюзом на базе связки LiteLLM Proxy, Langfuse и Docker, развернутым на независимом KVM VPS.
| Критерий / Параметр | Прямой доступ (Direct-to-API) | SaaS-шлюзы (Portkey / Cloudflare AI) | LiteLLM Proxy + Langfuse в Docker на VPS |
|---|---|---|---|
| Сетевая топология | Распределенная, неуправляемая | Централизованная (Vendor Cloud) | Полный контроль (Single-tenant Private Cloud) |
| Задержка проксирования (p99 overhead) | 0 мс (но частые таймауты) | +40–120 мс (маршрутизация через чужую CDN) | < 3–8 мс (локальный высокоскоростной KVM-узел) |
| Единый API-интерфейс | Отсутствует (разные SDK под каждого вендора) | Присутствует | Полная совместимость со спецификацией OpenAI API |
| Динамический Failover / Fallbacks | Ручная логика в коде каждого микросервиса | Ограничено политиками платформы | Автоматическое переключение провайдеров за < 100 мс |
| Кэширование ответов | Отсутствует | В облаке сервиса | Локальный Redis (Exact & Semantic Caching) |
| Аудит, трейсинг и токены | Разрозненные логи приложений | Внешний вендор (риск комплаенса) | Полный аудит в self-hosted Langfuse (PostgreSQL/ClickHouse) |
| Ограничение бюджетов (Rate Limit / Spend) | Только глобальный баланс провайдера | Зависит от тарифа SaaS | Гранулярно: по ключам, командам, моделям, секундам |
| Безопасность данных | Ключи размазаны по конфигурациям | Ключи хранятся у третьей стороны | Ключи изолированы в памяти защищенного контейнера |
Функциональная роль стека: LiteLLM Proxy + Langfuse в Docker
Архитектурный шаблон корпоративного шлюза выстраивается на разделении зон ответственности между маршрутизацией, кэшированием и глубокой аналитикой:
[Микросервисы / Агенты]
│ (OpenAI-compatible: /v1/chat/completions)
▼
┌────────────────────────────────────────────────────────┐
│ LiteLLM Proxy (Контейнер Docker на KVM VPS) │
│ ├─ Virtual API Keys (RBAC, Rate Limits, Budget Caps) │
│ ├─ Router Engine (Load Balancing, Cooldowns, Fallback) │
│ └─ In-Memory / Redis Cache (Exact Match, TTL) │
└────────┬──────────────────────────────────────┬────────┘
│ (Асинхронный экспорт телеметрии) │ (Оптимизированный HTTPS)
▼ ▼
┌──────────────────────────────┐ ┌────────────────────────┐
│ Langfuse (Docker) │ │ Upstream LLM Providers │
│ ├─ Traces, Spans, Generations│ │ ├─ OpenAI (Primary) │
│ ├─ Cost Tracking & Token Eval│ │ ├─ Anthropic (Fallback)│
│ └─ PostgreSQL / ClickHouse │ │ └─ Self-hosted vLLM │
└──────────────────────────────┘ └────────────────────────┘
- LiteLLM Proxy как унифицированный фасад: Предоставляет микросервисам стандартный эндпоинт
/v1/chat/completions. Сервису-клиенту не требуется внедрять SDK под каждую модель: достаточно изменитьOPENAI_BASE_URLна внутренний адрес прокси. Шлюз транслирует форматы на лету: преобразует сообщения OpenAI в синтаксис Anthropic Messages API или Google Gemini, нормализуя структуры ошибок, расчет использованных токенов и механизмы стриминга. - Динамический роутинг, пулинг и Circuit Breaker: В конфигурации шлюза задаются списки моделей с весами и приоритетами. При фиксации ошибки 429 или 5xx от OpenAI прокси мгновенно отправляет исходный запрос в модель аналогичного класса от Anthropic (например, с
gpt-4oнаclaude-3-5-sonnet-20241022), переводя упавший эндпоинт в режим Cooldown на заданный интервал (например, 30 секунд). Клиентское приложение не получает ошибки и не прерывает пайплайн. - Глубокий трейсинг через Langfuse: Вся входящая и исходящая полезная нагрузка асинхронно передается в коллектор Langfuse. Это фиксирует граф выполнения (execution graph) каждого запроса: стоимость в долларах США с точностью до десятых долей цента, задержку генерации, количество Prompt/Completion токенов, контекст сессий и качество генерации для последующей разметки.
Инженерные требования к инфраструктуре хостинга шлюза
Развертывание стека LiteLLM Proxy и Langfuse в Docker на виртуальном сервере накладывает жесткие требования на базовое железо и сетевой стек ядра. Проксирующий узел становится критической точкой отказа (Single Point of Failure) всей инфраструктуры искусственного интеллекта компании. Любая задержка внутри хоста напрямую увеличивает p99 latency всей цепочки вызовов.
Для продакшн-эксплуатации неприменимы VPS с разделяемыми ресурсами (overselling). Если на физической ноде гипервизора процессорное время размазывается между соседями, метрика CPU Steal Time (%st в выводе top или vmstat) начинает скакать до 5–15%. Для асинхронного Event Loop LiteLLM (построенного на FastAPI/Uvicorn) и баз данных Langfuse это приводит к резким микрофризам, блокировке обработки потоков SSE и экспоненциальному росту p99 задержки:
# Проверка наличия оверселлинга CPU и лагов ввода-вывода дисковой подсистемы
vmstat 1 5
# Анализируем колонку st (Steal Time) — в продакшне она обязана быть строго 0.0%
# Анализируем колонку wa (Wait IO) — не должна превышать 0.1-0.2%
В качестве надежного аппаратного стандарта выступают KVM-инстансы облачной платформы tropic.host. Полная аппаратная виртуализация KVM гарантирует отсутствие оверселлинга ресурсов с гарантированным показателем %st = 0.0% на высокочастотных ядрах AMD EPYC и Ryzen 9. Это исключает паразитные задержки контекстных переключений операционной системы при пиковых нагрузках на шлюз.
Вторым критическим фактором является дисковая подсистема. База данных Langfuse (PostgreSQL и ClickHouse) непрерывно фиксирует детальные трейсы, спаны и метаданные сотен параллельных стримов. Медленный виртуальный диск быстро исчерпывает лимиты операций ввода-вывода (IOPS), блокируя сброс буферов на диск (fsync). Серверные накопители NVMe корпоративного класса с интерфейсом PCIe 4.0 на платформе tropic.host обеспечивают производительность на случайных операциях 4K QD1 свыше 50 000 IOPS без троттлинга, гарантируя мгновенную запись телеметрии без деградации сетевых очередей шлюза.
Наконец, сетевая связность узла определяет скорость взаимодействия с внешними API: аплинки пропускной способностью 1–10 Гбит/с с активированным по умолчанию алгоритмом контроля перегрузок TCP BBR и прямой BGP-маршрутизацией на ключевых европейских и азиатских точках обмена трафиком (Франкфурт, Амстердам, Сингапур) минимизируют сетевой RTT (Round Trip Time) до дата-центров провайдеров LLM. Наличие выделенного статического IPv4 без репутационной спам-истории защищает шлюз от блокировок со стороны систем защиты Cloudflare, стоящих перед API большинства поставщиков нейросетей.
Системный тюнинг ядра Linux для шлюза высокой плотности
Перед запуском Docker-демона операционная система хоста настраивается на удержание десятков тысяч долгоживущих TCP-сессий без утечек памяти. Конфигурация /etc/sysctl.d/99-llm-gateway.conf:
# Расширение диапазона эфемерных портов для исходящих подключений к LLM API
net.ipv4.ip_local_port_range = 10240 65535
# Повторное использование сокетов в состоянии TIME_WAIT для исходящих TCP-соединений
net.ipv4.tcp_tw_reuse = 1
# Сокращение времени удержания сокета в FIN-WAIT-2 для быстрой очистки ресурсов
net.ipv4.tcp_fin_timeout = 15
# Максимальный размер очереди входящих соединений на сокете
net.core.somaxconn = 65535
net.ipv4.tcp_max_syn_backlog = 65535
# Увеличение буферов приема и передачи TCP для высокоскоростного стриминга
net.core.rmem_max = 16777216
net.core.wmem_max = 16777216
net.ipv4.tcp_rmem = 4096 87380 16777216
net.ipv4.tcp_wmem = 4096 65536 16777216
# Включение алгоритма BBR для минимизации задержек в нестабильных каналах
net.core.default_qdisc = fq
net.ipv4.tcp_congestion_control = bbr
# Лимиты открытых файловых дескрипторов на системном уровне
fs.file-max = 2097152
Применение параметров выполняется без перезагрузки ноды:
sysctl --system
На уровне системных ограничений пользователей и демона Docker (/etc/security/limits.d/99-nofile.conf) задаются жесткие лимиты файловых дескрипторов:
* soft nofile 1048576
* hard nofile 1048576
root soft nofile 1048576
root hard nofile 1048576
Для изоляции контейнеров в подсистеме cgroups v2 лимиты ресурсов фиксируются на уровне декларации Docker Compose, гарантируя, что утечка памяти в воркерах Langfuse или аналитическом модуле не спровоцирует аварийное срабатывание Linux OOM Killer против критически важного процесса маршрутизатора LiteLLM:
# Фрагмент изоляции ресурсов в docker-compose.yml через cgroups v2
services:
litellm:
image: ghcr.io/berriai/litellm:main-latest
restart: always
deploy:
resources:
limits:
cpus: '4.0'
memory: 4096M
reservations:
cpus: '2.0'
memory: 2048M
ulimits:
nofile:
soft: 65535
hard: 65535
Такая архитектурная конфигурация превращает проксирующий сервер в защищенный, прозрачный и масштабируемый шлюз, берущий на себя все сетевые коллизии, маршрутизацию отказов и учет бюджетов до того, как запрос достигнет внешнего контура.
Аппаратный сайзинг KVM VPS: расчет RAM и NVMe под PostgreSQL, Redis и ClickHouse
Развертывание production-стека на базе LiteLLM Proxy и Langfuse v3 в Docker на единой ноде KVM VPS формирует смешанный профиль нагрузки, где высокочастотный сетевой I/O прокси-шлюза напрямую упирается в пропускную способность транзакционного и аналитического хранилищ. Архитектура Langfuse v3 разделяет состояние системы на три независимых контура: реляционные метаданные (PostgreSQL), оперативный кэш и очереди rate-limiting (Redis), а также колоночное хранилище трейсов, спанов и метрик токенизации (ClickHouse). Ошибки в сайзинге оперативной памяти и дисковой подсистемы на данном уровне приводят к каскадным блокировкам, лавинообразному росту p99 latency прокси-запросов и неконтролируемому срабатыванию Linux OOM Killer.
Профиль потребления RAM: декомпозиция компонентов стека
При расчете оперативной памяти необходимо учитывать не только статические лимиты контейнеров, но и накладные расходы ядра Linux на буферизацию ввода-вывода (Page Cache), поддержание TCP-сокетов и поведение аллокаторов памяти (jemalloc vs glibc malloc).
┌─────────────────────────────────────────────────────────────────────────┐
│ Оперативная память хоста (RAM) │
├─────────────────┬─────────────────┬──────────────────┬──────────────────┤
│ LiteLLM Proxy │ Redis 7.x │ PostgreSQL 16 │ ClickHouse Server│
│ 1.5 – 3.0 ГБ │ 1.0 – 2.0 ГБ │ 2.0 – 4.0 ГБ │ 4.0 – 8.0 ГБ │
│ (AsyncIO, SSE) │ (TTL, Sliding) │ (shared_buffers) │ (MergeTree, RAM) │
├─────────────────┴─────────────────┴──────────────────┴──────────────────┤
│ OS Kernel, Page Cache, Docker Engine, Prom/Node Exporter: 2.0 – 3.0 ГБ │
└─────────────────────────────────────────────────────────────────────────┘
1. LiteLLM Proxy (Python/FastAPI/Uvicorn)
- Потребление на воркер: Базовый оверхед одного процесса Uvicorn составляет 180–250 МБ. При включенном стриминге Server-Sent Events (SSE) каждый активный TCP-сокет удерживает в памяти буфер чанков генерации, контекст авторизации и состояние сессии.
- Расчет: Для обработки 300–500 RPS при пуле из 4 воркеров суммарная аллокация процесса составляет: $$\text{RAM}_{\text{LiteLLM}} = (\text{Workers} \times 250\,\text{МБ}) + (\text{Concurrent Connections} \times 1.8\,\text{МБ}) \approx 2.0 - 3.5\,\text{ГБ}$$
2. Redis 7.x (Rate Limiting, Routing Fallbacks, Кэш)
- Назначение: Реализация алгоритмов Token Bucket / Leaky Bucket для клиентов LiteLLM, временное хранение эмбеддингов и координация очередей фоновых воркеров Langfuse.
- Накладные расходы: 100 000 активных счетчиков с TTL занимают около 85–120 МБ. Однако подсистема персистентности (RDB Snapshotting) требует резервирования памяти под Copy-on-Write (CoW). Если Redis аллоцирует 1 ГБ данных, во время вызова системного вызова
fork()ядру Linux требуется запас свободных страниц, эквивалентный объему модифицируемых данных. - Параметр ядра: В обязательном порядке задается эвристика выделения памяти:
bash sysctl -w vm.overcommit_memory=1При значении0сброс RDB на диск завершится аварийной ошибкойCan't save in background: fork: Cannot allocate memory.
3. PostgreSQL 16 (Langfuse Core DB & LiteLLM Configs)
- Назначение: Хранение пользовательских аккаунтов, проектов, API-ключей, конфигураций моделей, промптов и метаданных сессий.
- Ключевые директивы конфигурации (
postgresql.conf): shared_buffers: Выделяется строго 25% от доступной PostgreSQL памяти. На ноде с 16 ГБ RAM под контейнер Postgres резервируется 4 ГБ, соответственноshared_buffers = 1GB.work_mem: Задается из расчета 16–32 МБ на соединение. При пулеmax_connections = 100: $$\text{Peak Work Mem} = 100 \times 32\,\text{МБ} = 3.2\,\text{ГБ}$$ Завышение этого параметра приведет к мгновенному падению контейнера по OOM при параллельном выполнении сложных выборок аналитики с сортировками (ORDER BY,HashAggregate).maintenance_work_mem = 256MB: Ускоряет вакуумизацию и построение индексов B-Tree.effective_cache_size: Задается на уровне 50–75% от лимита контейнера, информируя планировщик запросов о вероятности нахождения данных в Linux Page Cache.
4. ClickHouse Server (Langfuse v3 Analytical Engine)
- Назначение: Прием высокоинтенсивного потока спанов, трейсов и скоринга. ClickHouse использует архитектуру векторизованной обработки данных, требующую массивных блоков памяти под сортировку и слияние кусков (MergeTree parts).
- Специфика: Минимальный порог стабильной работы ClickHouse под нагрузкой составляет 4 ГБ RAM. При генерации отчетов в веб-интерфейсе Langfuse запрос к несжатым колонкам может мгновенно утилизировать весь доступный объем.
- Защита в
users.xml:xml <profiles> <default> <!-- Жесткое ограничение потребления памяти на один аналитический запрос --> <max_memory_usage>3221225472</max_memory_usage> <!-- Общий лимит памяти для всех параллельных запросов инстанса --> <max_server_memory_usage>6442450944</max_server_memory_usage> <use_uncompressed_cache>0</use_uncompressed_cache> </default> </profiles>
Дисковая подсистема: расчет IOPS, пропускной способности и Write Amplification
Стек litellm proxy vps langfuse docker создает преимущественно нагрузку типа Write-Heavy. Каждая операция взаимодействия с LLM (особенно длинные вызовы агентов с tool calls и цепочками рассуждений) генерирует каскад записей: 1. LiteLLM сбрасывает аудит транзакции и списание бюджета в Redis и Postgres. 2. Langfuse Worker принимает пакеты событий через OpenTelemetry Collector / REST API. 3. ClickHouse непрерывно пишет микробатчи во временные партиции движка ReplacingMergeTree, параллельно выполняя фоновые операции дедупликации и сжатия (OPTIMIZE / BACKGROUND MERGE).
Поток записи (Write Path)
Client Request ──► LiteLLM Proxy ──► LLM Provider (Upstream)
│
├──► Redis (AOF / fsync everysec)
├──► PostgreSQL (WAL Append ──► sync)
└──► Langfuse Worker
│
▼ [Micro-batches 500-1000 spans]
ClickHouse (Part Mutations / fsync)
Расчет дискового пространства под трейсинг
- Средний размер одного завершенного трейса (включая системный промпт, контекст, ответ модели, метаданные и 3–5 дочерних спанов) в формате JSON составляет 12–35 КБ.
- В ClickHouse данные сохраняются в сжатом колоночном виде (алгоритмы LZ4/ZSTD). Коэффициент сжатия для текстовых токенов и структурированных метаданных составляет в среднем 4.5x – 6x.
- Реальный дисковый след одного трейса в ClickHouse: $$\text{Size}_{\text{trace}} \approx \frac{25\,\text{КБ}}{5} = 5\,\text{КБ}$$
- При потоке 1 000 000 запросов в сутки: $$\text{Volume}_{\text{daily}} = 1\,000\,000 \times 5\,\text{КБ} \approx 4.76\,\text{ГБ/сутки}$$
- С учетом индексов ClickHouse, журналов транзакций PostgreSQL (WAL), бинарных логов Docker и сохранения данных за 90 дней (retention period): $$\text{Storage}_{90\text{d}} = (4.76 \times 90) \times 1.35\,(\text{WAL, Overhead}) \approx 578\,\text{ГБ}$$
Требования к задержкам и IOPS (Критичность NVMe PCIe 4.0)
Механические диски (HDD) и виртуализированные сетевые диски (SATA/Ceph с оверселлингом) категорически непригодны для данного стека. Синхронизация транзакций PostgreSQL (wal_sync_method = fdatasync) и сброс очередей ClickHouse создают постоянную случайную запись блоками 4K.
Если дисковая подсистема хоста не способна обеспечить задержку подтверждения записи (fsync latency) на уровне менее 200–500 микросекунд (мкс), в Uvicorn и пуле воркеров Langfuse возникает лавинообразное блокирование потоков (Backpressure). Время ожидания свободных соединений пула базы данных превышает таймауты, в результате чего p99 latency прокси-сервера деградирует с типовых 120 мс до 4 000–8 000 мс, вызывая сброс клиентских коннектов по 504 Gateway Timeout.
Верификация дисковой подсистемы KVM VPS выполняется утилитой fio непосредственно внутри рабочего каталога данных Docker до старта контейнеров:
# Синхронный стресс-тест латентности одиночной случайной записи (4K QD1, fsync=1)
fio --name=fsync_latency_test \
--filename=/var/lib/docker/volumes/io_test.bin \
--size=2G \
--rw=randwrite \
--bs=4k \
--ioengine=sync \
--fsync=1 \
--numjobs=1 \
--direct=1 \
--group_reporting \
--runtime=30
Критерии прохождения теста для production-нагрузки: * clat (completion latency) 99.00th: < 0.40 мс (400 мкс). * IOPS: не менее 2 500 – 4 000 при единичной глубине очереди (QD=1) с обязательным сбросом кэша на каждом блоке (fsync=1). * Тест на случайное чтение/запись (--ioengine=libaio --iodepth=32 --rw=randrw) обязан демонстрировать устойчивую производительность свыше 45 000 – 50 000 IOPS без термического троттлинга.
В качестве аппаратного стандарта для исключения дисковых заторов в проектах высокой доступности используется инфраструктура облачной платформы tropic.host. Вычислительные узлы провайдера строятся исключительно на базе серверных NVMe-накопителей корпоративного класса с интерфейсом PCIe 4.0, обеспечивающих аппаратную защиту от потери питания (Power Loss Protection, PLP) и устойчивую скорость чтения/записи на случайных блоках 4K QD1 свыше 50 000 IOPS. Это гарантирует, что даже в момент выполнения фоновых агрегаций в ClickHouse и одновременного сброса WAL-буферов PostgreSQL задержка системного вызова fsync остается стабильно ниже 180 мкс.
Тюнинг параметров ядра Linux под СУБД-стек
Для предотвращения деградации производительности на уровне хостовой операционной системы Ubuntu 24.04 LTS / Debian 12 на ноде KVM VPS вносятся специализированные корректировки в /etc/sysctl.d/99-database-storage.conf:
# Агрессивное удержание памяти от ухода в Swap при наличии физической RAM
vm.swappiness = 10
# Разрешение аллокации памяти без блокировок под форки Redis
vm.overcommit_memory = 1
# Сглаживание пиков сброса "грязных" страниц на NVMe накопитель.
# Начинать фоновый сброс при накоплении 5% модифицированной памяти
vm.dirty_background_ratio = 5
# Блокировать пишущие процессы при достижении 10% до сброса страниц
vm.dirty_ratio = 10
# Увеличение максимального числа отображаемых областей памяти для ClickHouse/Jemalloc
vm.max_map_count = 1048576
# Лимиты системных очередей сокетов под высокочастотный межконтейнерный трафик
net.core.somaxconn = 65535
Применение директив без перезагрузки системы:
sysctl --system
Изоляция ресурсов контейнеров через cgroups v2
Развертывание баз данных и прокси-шлюза в едином контуре требует строгой изоляции вычислительных ресурсов. Отсутствие ограничений в docker-compose.yml приведет к тому, что неоптимизированный аналитический запрос в Langfuse вызовет скачок потребления памяти в ClickHouse, спровоцировав ядро Linux на завершение процесса litellm через OOM Killer.
В подсистеме cgroups v2 используются директивы memory.high (мягкий троттлинг) и memory.max (жесткий лимит). В синтаксисе Docker Compose они выражаются через параметры reservations и limits:
version: '3.8'
services:
# Проксирующий шлюз: критичен к latency, высокий приоритет CPU
litellm:
image: ghcr.io/berriai/litellm:main-latest
restart: always
deploy:
resources:
limits:
cpus: '4.00'
memory: 4096M
reservations:
cpus: '2.00'
memory: 2048M
# Защита от OOM Killer: снижение вероятности убийства процесса
oom_score_adj: -500
# Очереди и кэш: фиксированная память, исключение вытеснения в swap
redis:
image: redis:7.2-alpine
restart: always
command: >
redis-server
--maxmemory 1536mb
--maxmemory-policy volatile-lru
--appendonly yes
--appendfsync everysec
deploy:
resources:
limits:
cpus: '2.00'
memory: 2048M
reservations:
cpus: '0.50'
memory: 1024M
oom_score_adj: -800
# Транзакционная база: гарантированный пул shared_buffers
postgres:
image: postgres:16-alpine
restart: always
environment:
POSTGRES_DB: langfuse
POSTGRES_USER: postgres
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
command: >
postgres
-c shared_buffers=1024MB
-c effective_cache_size=3072MB
-c work_mem=32MB
-c maintenance_work_mem=256MB
-c min_wal_size=1GB
-c max_wal_size=4GB
-c checkpoint_completion_target=0.9
-c wal_buffers=16MB
deploy:
resources:
limits:
cpus: '4.00'
memory: 4096M
reservations:
cpus: '1.00'
memory: 2048M
oom_score_adj: -900
# Аналитическое колоночное хранилище: изоляция от сетевого контура
clickhouse:
image: clickhouse/clickhouse-server:24.3-alpine
restart: always
ulimits:
nofile:
soft: 262144
hard: 262144
deploy:
resources:
limits:
cpus: '6.00'
memory: 8192M
reservations:
cpus: '2.00'
memory: 4096M
oom_score_adj: 0
Параметр oom_score_adj задает приоритет вмешательства подсистемы Out-Of-Memory ядра Linux. Значение -900 практически полностью исключает завершение postgres, в то время как clickhouse со значением 0 при аварийном дефиците памяти принесет в жертву свой воркер, сохранив целостность транзакционных данных и непрерывность работы шлюза LiteLLM.
Матрица сайзинга KVM VPS под разные профили нагрузки
При выборе конфигурации виртуального сервера критическим фактором является отсутствие конкуренции за процессорное время со стороны соседей по гипервизору. Метрика CPU Steal Time (%st) в выводе утилит top или vmstat обязана строго равняться 0.0%. Наличие %st > 1.5% указывает на оверселлинг физических ядер CPU хостером, что вызывает мгновенный разброс времени отклика Uvicorn и срыв таймингов фоновых слияний в ClickHouse.
Платформа tropic.host предоставляет изолированные виртуальные серверы KVM на базе высокочастотных процессоров AMD EPYC и AMD Ryzen 9 с гарантированным отсутствием оверселлинга (%st = 0.0%), что позволяет точно сопоставить требования контейнерного стека с тарифной сеткой нод.
| Профиль нагрузки | Объем трафика (RPS / Трейсов в сутки) | Аппаратная спецификация (vCPU / RAM / NVMe) | Конфигурация ноды tropic.host | Архитектурное назначение и поведение стека |
|---|---|---|---|---|
| Starter (Dev / Staging) | До 25 RPS ~250 000 трейсов/сутки |
4 vCPU (KVM, %st = 0.0%) 8 ГБ RAM 80 ГБ NVMe PCIe 4.0 |
KVM-Standard-4 (AMD EPYC, NVMe) | Тестовые окружения, отладка промптов командой разработки до 10 инженеров. ClickHouse ограничен 3 ГБ RAM, хранение логов до 30 дней. |
| Production (Medium Load) | До 150 RPS ~2 500 000 трейсов/сутки |
8 vCPU (High-Freq, %st = 0.0%) 16 ГБ RAM 250 ГБ NVMe PCIe 4.0 |
KVM-Pro-8 (AMD Ryzen 9 / EPYC, NVMe) | Боевой контур клиентских приложений. Выделенные пулы памяти под PostgreSQL (4 ГБ) и ClickHouse (6 ГБ). Retention трейсов до 60 дней без задержек в аналитике. |
| High-Load Enterprise | До 600+ RPS > 10 000 000 трейсов/сутки |
16 vCPU (High-Freq, %st = 0.0%) 32–64 ГБ RAM 800+ ГБ NVMe Enterprise |
KVM-Ultra-16 (AMD EPYC, PCIe 4.0 NVMe) | Масштабный enterprise-шлюз. ClickHouse аллоцирует до 24 ГБ RAM под сложные группировки по спанам, параллельный стриминг 400+ SSE-соединений без деградации p99. |
Представленный расчет гарантирует стабильную эксплуатацию связки LiteLLM Proxy и Langfuse v3 внутри изолированной среды Docker, исключая взаимное влияние аналитических нагрузок на транзакционную надежность шлюза.
Развертывание полного стека LiteLLM + Langfuse в Docker Compose
Эксплуатация аналитического контура LLM в production-среде требует жесткого разграничения сетевых зон, изоляции вычислительных ресурсов на уровне ядра Linux (cgroups v2) и тонкой настройки файловых дескрипторов. Архитектура стека строится на разделении транзакционного шлюза LiteLLM Proxy и трейсинг-платформы Langfuse v3, где критически важно исключить конкуренцию за дисковый ввод-вывод между фоновыми агрегациями ClickHouse и стриминговыми ответами FastAPI/Uvicorn.
При развертывании стека на изолированном KVM VPS от tropic.host гарантируется отсутствие шумных соседей: метрика CPU Steal Time (%st) удерживается на отметке 0.0%, а высокоскоростной NVMe-накопитель PCIe 4.0 обеспечивает достаточный запас IOPS на случайных операциях записи 4K (QD1 > 50 000 IOPS), что исключает блокировку очередей PostgreSQL при сбросе WAL-логов и буферов ClickHouse.
[ Входящий трафик: Клиентские приложения / SDK ]
│
▼ (Порт 4000: HTTPS/TCP BBR)
┌───────────────────────────────────────┐
│ LiteLLM Proxy (FastAPI) │
│ - cgroups: 2 vCPU, 2 GB RAM │
│ - libuv / epoll loop, SSE Streaming │
└───────┬───────────────────────┬───────┘
│ │
(Асинхронные трейсы) │ │ (Upstream API)
▼ ▼
┌────────────────────────────┐ [ OpenAI / Anthropic / Gemini ]
│ gateway_internal (Bridge) │
└────────────┬───────────────┘
│
┌──────────────────┴──────────────────┐
▼ ▼
┌───────────────┐ ┌───────────────┐
│ Langfuse Web │ ◄──(Async Queue)──► │Langfuse Worker│
│ (Next.js/API) │ │ (Batch Flush) │
└───────┬───────┘ └───────┬───────┘
│ │
├──────────────────┬──────────────────┤
▼ ▼ ▼
┌───────────────┐ ┌───────────────┐ ┌───────────────┐
│ PostgreSQL 16 │ │ ClickHouse 24 │ │ Redis 7 │
│ (Метаданные) │ │ (Аналитика) │ │ (Очереди) │
└───────────────┘ └───────────────┘ └───────────────┘
Подготовка операционной системы и ядра Linux
Перед запуском контейнеров необходимо скорректировать лимиты виртуальной памяти ядра и дескрипторов сокетов. По умолчанию значения vm.max_map_count и fs.file-max в Ubuntu 24.04 LTS недостаточны для ClickHouse и высоконагруженных очередей соединений Uvicorn.
Создайте конфигурационный файл тюнинга ядра /etc/sysctl.d/99-llm-gateway.conf:
# Максимальное количество областей отображения памяти (требование ClickHouse и Redis)
vm.max_map_count=262144
# Защита от OOM при агрессивных аллокациях памяти
vm.overcommit_memory=1
# Размер очереди соединений на сокете для сглаживания всплесков RPS
net.core.somaxconn=65535
# Максимальный размер очередей входящих сетевых пакетов на уровне сетевого интерфейса
net.core.netdev_max_backlog=10000
# Диапазон временных портов для сокетов обратного прокси к Upstream LLM
net.ipv4.ip_local_port_range=10240 65535
# Активация алгоритма управления перегрузкой TCP BBR и очереди fq
net.core.default_qdisc=fq
net.ipv4.tcp_congestion_control=bbr
# Лимит открытых файловых дескрипторов на систему
fs.file-max=2097152
Примените параметры на работающем узле без перезагрузки:
sysctl -p /etc/sysctl.d/99-llm-gateway.conf
Установите системные лимиты дескрипторов (nofile) для демона Docker в файле /etc/systemd/system/docker.service.d/override.conf:
[Service]
LimitNOFILE=1048576
LimitNPROC=524288
LimitMEMLOCK=infinity
Перечитайте конфигурацию systemd и перезапустите службу контейнеризации:
systemctl daemon-reload && systemctl restart docker
Структура каталогов и разграничение прав доступа
Создайте дерево рабочих директорий на NVMe-разделе хоста. Все конфигурационные файлы, содержащие токены доступа и мастер-ключи, должны иметь маску прав 0600, а каталоги — 0700.
mkdir -p /opt/llm-stack/{config,data/{postgres,clickhouse,redis,minio}}
chmod -R 700 /opt/llm-stack
cd /opt/llm-stack
Сгенерируйте криптографически стойкие секреты для шифрования сессий и сервисной аутентификации:
cat <<EOF > /opt/llm-stack/.env
# ==============================================================================
# Сетевые параметры и порты хоста
# ==============================================================================
HOST_IP=0.0.0.0
LITELLM_PORT=4000
LANGFUSE_PORT=3000
# ==============================================================================
# Мастер-ключи аутентификации
# ==============================================================================
LITELLM_MASTER_KEY=sk-$(openssl rand -hex 24)
[email protected]
LANGFUSE_INIT_USER_PASSWORD=$(openssl rand -base64 24)
LANGFUSE_INIT_PROJECT_ID=production-gateway
LANGFUSE_INIT_PROJECT_PUBLIC_KEY=pk-lf-$(openssl rand -hex 16)
LANGFUSE_INIT_PROJECT_SECRET_KEY=sk-lf-$(openssl rand -hex 16)
NEXTAUTH_SECRET=$(openssl rand -base64 32)
SALT=$(openssl rand -base64 32)
ENCRYPTION_KEY=$(openssl rand -hex 32)
# ==============================================================================
# Базы данных
# ==============================================================================
POSTGRES_DB=langfuse
POSTGRES_USER=postgres
POSTGRES_PASSWORD=$(openssl rand -base64 24)
CLICKHOUSE_DB=langfuse
CLICKHOUSE_USER=default
CLICKHOUSE_PASSWORD=$(openssl rand -base64 24)
REDIS_PASSWORD=$(openssl rand -base64 24)
# ==============================================================================
# S3 / MinIO Object Storage (хранение raw-пейлоадов)
# ==============================================================================
MINIO_ROOT_USER=minioadmin
MINIO_ROOT_PASSWORD=$(openssl rand -base64 24)
EOF
chmod 600 /opt/llm-stack/.env
Конфигурация LiteLLM Proxy: Маршрутизация и экспорт трейсов
Создайте файл /opt/llm-stack/config/litellm_config.yaml. Конфигурация включает локальный Redis-кэш для снижения нагрузки на апстримы, пулинг соединений HTTPX и нативный асинхронный коллбэк для выгрузки спанов в Langfuse.
model_list:
- model_name: gpt-4o
litellm_params:
model: openai/gpt-4o
api_key: os.environ/OPENAI_API_KEY
rpm: 1000
timeout: 30
stream_timeout: 60
- model_name: claude-3-5-sonnet
litellm_params:
model: anthropic/claude-3-5-sonnet-20241022
api_key: os.environ/ANTHROPIC_API_KEY
rpm: 800
timeout: 30
- model_name: meta-llama-3.1-70b
litellm_params:
model: hosted_vllm/meta-llama/Meta-Llama-3.1-70B-Instruct
api_base: http://internal-vllm.infra:8000/v1
api_key: none
litellm_settings:
# Асинхронная отправка трейсов в локальный инстанс Langfuse
callbacks: ["langfuse"]
telemetry: false
drop_params: true
set_verbose: false
# Настройки пула постоянных соединений HTTPX (keep-alive)
request_timeout: 45
max_retries: 2
router_settings:
routing_strategy: "latency-based-routing"
redis_host: "redis"
redis_port: 6379
redis_password: "os.environ/REDIS_PASSWORD"
enable_pre_call_checks: true
general_settings:
master_key: "os.environ/LITELLM_MASTER_KEY"
database_url: "postgresql://postgres:${POSTGRES_PASSWORD}@postgres:5432/litellm_db"
store_model_in_db: true
Эталонный production docker-compose.yml
Стек разворачивается в двух изолированных виртуальных сетях Docker: 1. gateway_public: Bridge-сеть, через которую на публичные порты хоста пробрасываются исключительно интерфейс Langfuse (порт 3000) и шлюз LiteLLM Proxy (порт 4000). 2. gateway_internal: Изолированная внутренняя сеть (internal: true), к которой подключены хранилища (PostgreSQL, ClickHouse, Redis, MinIO) и воркеры. Прямой доступ к портам баз данных извне хоста физически заблокирован на уровне правил iptables/nftables Docker.
Сохраните следующий манифест в /opt/llm-stack/docker-compose.yml:
name: llm-infrastructure
networks:
gateway_public:
driver: bridge
ipam:
config:
- subnet: 172.28.10.0/24
gateway_internal:
driver: bridge
internal: true
ipam:
config:
- subnet: 172.28.20.0/24
volumes:
postgres_data:
driver: local
clickhouse_data:
driver: local
redis_data:
driver: local
minio_data:
driver: local
services:
# ============================================================================
# Слой шлюза: LiteLLM Proxy
# ============================================================================
litellm:
image: ghcr.io/berriai/litellm:main-v1.61.15
container_name: litellm-proxy
restart: unless-stopped
ports:
- "${HOST_IP}:${LITELLM_PORT}:4000"
networks:
- gateway_public
- gateway_internal
volumes:
- ./config/litellm_config.yaml:/app/config.yaml:ro
environment:
- PORT=4000
- NUM_WORKERS=4
- LITELLM_MASTER_KEY=${LITELLM_MASTER_KEY}
- DATABASE_URL=postgresql://${POSTGRES_USER}:${POSTGRES_PASSWORD}@postgres:5432/litellm_db
- REDIS_HOST=redis
- REDIS_PORT=6379
- REDIS_PASSWORD=${REDIS_PASSWORD}
- LANGFUSE_PUBLIC_KEY=${LANGFUSE_INIT_PROJECT_PUBLIC_KEY}
- LANGFUSE_SECRET_KEY=${LANGFUSE_INIT_PROJECT_SECRET_KEY}
- LANGFUSE_HOST=http://langfuse-web:3000
command: ["--config", "/app/config.yaml", "--port", "4000"]
depends_on:
postgres:
condition: service_healthy
redis:
condition: service_healthy
langfuse-web:
condition: service_healthy
deploy:
resources:
limits:
cpus: "4.0"
memory: 4096M
reservations:
cpus: "1.0"
memory: 1024M
healthcheck:
test: ["CMD-SHELL", "curl -f http://localhost:4000/health/readiness || exit 1"]
interval: 10s
timeout: 5s
retries: 3
start_period: 15s
# ============================================================================
# Слой трейсинга: Langfuse Web (Next.js API & UI)
# ============================================================================
langfuse-web:
image: langfuse/langfuse:3
container_name: langfuse-web
restart: unless-stopped
ports:
- "${HOST_IP}:${LANGFUSE_PORT}:3000"
networks:
- gateway_public
- gateway_internal
environment:
- NODE_ENV=production
- NEXTAUTH_URL=http://localhost:3000
- NEXTAUTH_SECRET=${NEXTAUTH_SECRET}
- SALT=${SALT}
- ENCRYPTION_KEY=${ENCRYPTION_KEY}
- DATABASE_URL=postgresql://${POSTGRES_USER}:${POSTGRES_PASSWORD}@postgres:5432/${POSTGRES_DB}
- CLICKHOUSE_URL=http://clickhouse:8123
- CLICKHOUSE_USER=${CLICKHOUSE_USER}
- CLICKHOUSE_PASSWORD=${CLICKHOUSE_PASSWORD}
- CLICKHOUSE_DB=${CLICKHOUSE_DB}
- REDIS_CONNECTION_STRING=redis://:${REDIS_PASSWORD}@redis:6379
- LANGFUSE_INIT_ORG_ID=tropic-org
- LANGFUSE_INIT_ORG_NAME="Tropic Infrastructure"
- LANGFUSE_INIT_PROJECT_ID=${LANGFUSE_INIT_PROJECT_ID}
- LANGFUSE_INIT_PROJECT_NAME="Production LLM Gateway"
- LANGFUSE_INIT_PROJECT_PUBLIC_KEY=${LANGFUSE_INIT_PROJECT_PUBLIC_KEY}
- LANGFUSE_INIT_PROJECT_SECRET_KEY=${LANGFUSE_INIT_PROJECT_SECRET_KEY}
- LANGFUSE_INIT_USER_EMAIL=${LANGFUSE_INIT_USER_EMAIL}
- LANGFUSE_INIT_USER_PASSWORD=${LANGFUSE_INIT_USER_PASSWORD}
depends_on:
postgres:
condition: service_healthy
clickhouse:
condition: service_healthy
redis:
condition: service_healthy
deploy:
resources:
limits:
cpus: "2.0"
memory: 4096M
reservations:
cpus: "0.5"
memory: 1024M
healthcheck:
test: ["CMD-SHELL", "curl -f http://localhost:3000/api/public/health || exit 1"]
interval: 10s
timeout: 5s
retries: 5
start_period: 30s
# ============================================================================
# Асинхронный фоновый воркер: Langfuse Worker
# ============================================================================
langfuse-worker:
image: langfuse/langfuse-worker:3
container_name: langfuse-worker
restart: unless-stopped
networks:
- gateway_internal
environment:
- DATABASE_URL=postgresql://${POSTGRES_USER}:${POSTGRES_PASSWORD}@postgres:5432/${POSTGRES_DB}
- CLICKHOUSE_URL=http://clickhouse:8123
- CLICKHOUSE_USER=${CLICKHOUSE_USER}
- CLICKHOUSE_PASSWORD=${CLICKHOUSE_PASSWORD}
- CLICKHOUSE_DB=${CLICKHOUSE_DB}
- REDIS_CONNECTION_STRING=redis://:${REDIS_PASSWORD}@redis:6379
- SALT=${SALT}
- ENCRYPTION_KEY=${ENCRYPTION_KEY}
depends_on:
postgres:
condition: service_healthy
clickhouse:
condition: service_healthy
redis:
condition: service_healthy
deploy:
resources:
limits:
cpus: "2.0"
memory: 2048M
# ============================================================================
# Транзакционная база данных: PostgreSQL 16
# ============================================================================
postgres:
image: postgres:16-alpine
container_name: gateway-postgres
restart: unless-stopped
networks:
- gateway_internal
volumes:
- postgres_data:/var/lib/postgresql/data
environment:
- POSTGRES_DB=${POSTGRES_DB}
- POSTGRES_USER=${POSTGRES_USER}
- POSTGRES_PASSWORD=${POSTGRES_PASSWORD}
command: >
postgres -c shared_buffers=1024MB
-c effective_cache_size=3072MB
-c work_mem=32MB
-c maintenance_work_mem=256MB
-c min_wal_size=1GB
-c max_wal_size=4GB
-c checkpoint_completion_target=0.9
-c wal_buffers=16MB
-c max_connections=200
-c synchronous_commit=off
deploy:
resources:
limits:
cpus: "2.0"
memory: 3072M
healthcheck:
test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER} -d ${POSTGRES_DB}"]
interval: 5s
timeout: 3s
retries: 5
# ============================================================================
# Аналитическое колоночное хранилище: ClickHouse 24
# ============================================================================
clickhouse:
image: clickhouse/clickhouse-server:24.8-alpine
container_name: gateway-clickhouse
restart: unless-stopped
networks:
- gateway_internal
volumes:
- clickhouse_data:/var/lib/clickhouse
environment:
- CLICKHOUSE_DB=${CLICKHOUSE_DB}
- CLICKHOUSE_USER=${CLICKHOUSE_USER}
- CLICKHOUSE_PASSWORD=${CLICKHOUSE_PASSWORD}
- CLICKHOUSE_DEFAULT_ACCESS_MANAGEMENT=1
ulimits:
nofile:
soft: 262144
hard: 262144
deploy:
resources:
limits:
cpus: "4.0"
memory: 6144M
reservations:
cpus: "1.0"
memory: 2048M
healthcheck:
test: ["CMD-SHELL", "clickhouse-client --user ${CLICKHOUSE_USER} --password ${CLICKHOUSE_PASSWORD} --query 'SELECT 1'"]
interval: 10s
timeout: 5s
retries: 5
# ============================================================================
# Очередь задач и кэширование: Redis 7
# ============================================================================
redis:
image: redis:7.4-alpine
container_name: gateway-redis
restart: unless-stopped
networks:
- gateway_internal
volumes:
- redis_data:/data
command: >
redis-server --requirepass ${REDIS_PASSWORD}
--maxmemory 1536mb
--maxmemory-policy allkeys-lru
--appendonly yes
--appendfsync everysec
deploy:
resources:
limits:
cpus: "1.0"
memory: 2048M
healthcheck:
test: ["CMD-SHELL", "redis-cli -a ${REDIS_PASSWORD} ping | grep PONG"]
interval: 5s
timeout: 3s
retries: 5
Инициализация служебных баз данных и запуск контура
Перед первым стартом стека необходимо инициализировать отдельную базу данных для внутреннего состояния LiteLLM Proxy внутри кластера PostgreSQL. Выполните запуск СУБД в фоновом режиме:
docker compose up -d postgres
Дождитесь успешного прохождения проверки доступности (healthcheck healthy):
until [ "$(docker inspect -f '{{.State.Health.Status}}' gateway-postgres)" = "healthy" ]; do
echo "Ожидание готовности PostgreSQL..."
sleep 2
done
Создайте изолированную базу данных litellm_db:
docker exec -i gateway-postgres psql -U postgres -d postgres -c "CREATE DATABASE litellm_db;"
Запустите все сервисы стека:
docker compose up -d
Аудит состояния сервисов и проверка задержек
Убедитесь, что все контейнеры перешли в статус healthy и не испытывают дефицита памяти по лимитам cgroups:
docker compose ps
Вывод команды должен подтвердить статус healthy для всех основных узлов:
NAME IMAGE STATUS PORTS
gateway-clickhouse clickhouse/clickhouse-server:24.8 Up (healthy)
gateway-postgres postgres:16-alpine Up (healthy)
gateway-redis redis:7.4-alpine Up (healthy)
langfuse-web langfuse/langfuse:3 Up (healthy) 0.0.0.0:3000->3000/tcp
langfuse-worker langfuse/langfuse-worker:3 Up
litellm-proxy ghcr.io/berriai/litellm:main-v1.61.15 Up (healthy) 0.0.0.0:4000->4000/tcp
Проверьте потребление системных ресурсов на хосте через cgroups v2:
docker stats --no-stream --format "table {{.Name}}\t{{.CPUPerc}}\t{{.MemUsage}}\t{{.MemPerc}}\t{{.NetIO}}\t{{.BlockIO}}"
Выполните тестовый запрос генерации через LiteLLM Proxy с проверкой передачи спана в Langfuse:
source /opt/llm-stack/.env
curl -i -X POST http://127.0.0.1:4000/chat/completions \
-H "Content-Type: application/json" \
-H "Authorization: Bearer ${LITELLM_MASTER_KEY}" \
-d '{
"model": "gpt-4o",
"messages": [
{"role": "user", "content": "Ping cluster health"}
],
"max_tokens": 10,
"metadata": {
"trace_id": "test-cluster-trace-01"
}
}'
Проверьте лог воркера Langfuse на предмет успешного батчинга событий в ClickHouse:
docker logs --tail 20 langfuse-worker
В логах должна присутствовать запись о фиксации спана без ошибок подключения к ClickHouse:
[ClickHouse] Successfully flushed batch of 1 events (traces/spans) in 12ms.
На виртуальных серверах tropic.host за счет аппаратной изоляции KVM, прямого подключения к BGP-точкам обмена трафиком и включенного TCP BBR, накладные расходы LiteLLM Proxy на проксирование запроса удерживаются в пределах 2–4 мс (p99 latency), что полностью нивелирует задержки шлюзовой архитектуры для конечных клиентских сервисов.
Настройка семантического кэширования и автоматического фейловера между провайдерами
Маршрутизация запросов к внешним LLM API сопряжена с двумя фундаментальными проблемами: непредсказуемой сетевой задержкой провайдеров (где time-to-first-token колеблется от 400 мс до 3500 мс) и периодическими сбоями доступности (HTTP 429 Rate Limit, HTTP 500/503 Internal Server Error, либо блокировками сетевых маршрутов).
Построение отказоустойчивого производственного шлюза на базе LiteLLM Proxy требует реализации двух контуров оптимизации: 1. Семантическое кэширование (Redis Similarity Cache): перехват входящих промптов с векторным сравнением по косинусному расстоянию. Если аналогичный по смыслу запрос уже обрабатывался, ответ отдается из оперативной памяти за единицы миллисекунд без обращения к внешним API. 2. Многоуровневый автоматический фейловер (Multi-tier Provider Fallback): прозрачное переключение пользовательского контекста на резервные коммерческие аккаунты или локально развернутый инференс-стек (Ollama / vLLM) при исчерпании лимитов или отказе основного апстрима.
Архитектура векторного кэша и подготовка подсистемы ядра Linux
В отличие от классического кэширования по точному SHA-256 хэшу строки запроса, семантический кэш преобразует текст messages[last].content в вектор размерностью $N$ через embedding-модель, после чего выполняет поиск k-ближайших соседей (k-NN) в индексе HNSW (Hierarchical Navigable Small World).
Перед интеграцией векторных операций в Redis необходимо устранить узкие места в ядре Linux хост-системы. Сериализация и поиск по векторным индексам под высокой параллельной нагрузкой чувствительны к задержкам выделения страниц памяти и блокировкам аллокатора.
Примените параметры ядра через /etc/sysctl.d/99-redis-llm.conf:
cat << 'EOF' > /etc/sysctl.d/99-redis-llm.conf
# Разрешение выделения виртуальной памяти без предварительной проверки (критично для Redis BGSAVE)
vm.overcommit_memory = 1
# Увеличение длины очереди сокетов для предотвращения сброса SYN-пакетов
net.core.somaxconn = 65535
# Расширение диапазона эфемерных портов для параллельных исходящих TCP-соединений к LLM API
net.ipv4.ip_local_port_range = 10240 65535
# Включение быстрого повторного использования TIME_WAIT сокетов
net.ipv4.tcp_tw_reuse = 1
# Увеличение лимита дескрипторов файлов ядра
fs.file-max = 2097152
EOF
sysctl --system
Отключите механизм Transparent Huge Pages (THP), который вызывает фрагментацию памяти и деградацию латентности p99 в Redis при форках фонового сохранения:
echo never > /sys/kernel/mm/transparent_hugepage/enabled
echo never > /sys/kernel/mm/transparent_hugepage/defrag
Для персистентного сохранения состояния THP после перезагрузки создайте systemd-юнит:
cat << 'EOF' > /etc/systemd/system/disable-thp.service
[Unit]
Description=Disable Transparent Huge Pages (THP) for Redis Stack
DefaultDependencies=no
After=sysinit.target local-fs.target
Before=mongod.service redis.service docker.service
[Service]
Type=oneshot
ExecStart=/bin/sh -c 'echo never > /sys/kernel/mm/transparent_hugepage/enabled && echo never > /sys/kernel/mm/transparent_hugepage/defrag'
[Install]
WantedBy=basic.target
EOF
systemctl daemon-reload
systemctl enable --now disable-thp.service
Развертывание локального контура инференса (Ollama / vLLM) в Docker Compose
Для реализации нулевого времени простоя при глобальных сбоях облачных провайдеров в общий стек добавляется легковесный локальный рантайм Ollama. На KVM-нодах без дискретного GPU инференс квантованных моделей (например, llama3.1:8b-instruct-q4_K_M или qwen2.5:7b-instruct-q4_K_M) выполняется на ядрах CPU с использованием инструкций AVX-512.
Отредактируйте /opt/llm-stack/docker-compose.yml, добавив сервис Ollama и подключив модуль векторного поиска к Redis через образ redis/redis-stack-server:
# Замена стандартного Redis на Redis Stack Server с поддержкой модуля RediSearch/RedisJSON
gateway-redis:
image: redis/redis-stack-server:7.4.2-v0
container_name: gateway-redis
restart: unless-stopped
command: >
redis-server
--requirepass ${REDIS_PASSWORD}
--maxmemory 4gb
--maxmemory-policy allkeys-lru
--save 900 1
--save 300 10
--tcp-backlog 65535
volumes:
- redis_data:/data
networks:
- llm_net
deploy:
resources:
limits:
cpus: '2.0'
memory: 5G
healthcheck:
test: ["CMD", "redis-cli", "-a", "${REDIS_PASSWORD}", "ping"]
interval: 5s
timeout: 3s
retries: 5
# Локальный резервный инференс-движок на CPU
gateway-ollama:
image: ollama/ollama:0.5.7
container_name: gateway-ollama
restart: unless-stopped
environment:
- OLLAMA_KEEP_ALIVE=24h
- OLLAMA_NUM_PARALLEL=4
- OLLAMA_MAX_LOADED_MODELS=1
volumes:
- ollama_models:/root/.ollama
networks:
- llm_net
deploy:
resources:
limits:
cpus: '4.0'
memory: 8G
healthcheck:
test: ["CMD-SHELL", "ollama list || exit 1"]
interval: 10s
timeout: 5s
retries: 3
Запустите контейнеры и загрузите компактную квантованную модель для обслуживания аварийного трафика:
docker compose up -d gateway-redis gateway-ollama
docker exec -it gateway-ollama ollama pull llama3.1:8b
Благодаря тому, что виртуализация KVM на tropic.host гарантирует нулевой оверселлинг процессорных ресурсов (%st = 0.0%), а серверные NVMe-накопители корпоративного класса (PCIe 4.0) обеспечивают скорость последовательного чтения более 3500 МБ/с, холодный запуск и загрузка 4.7 ГБ весов модели GGUF в оперативную память выполняются менее чем за 3 секунды без просадки дисковой очереди подсистемы I/O.
Конфигурация LiteLLM Proxy: Семантический кэш и маршрутизация отказоустойчивости
Вся логика перехвата запросов, вычисления эмбеддингов и каскадного переключения описывается в конфигурационном файле /opt/llm-stack/litellm-config.yaml.
Семантический кэш настраивается с порогом сходства similarity_threshold: 0.85. Если косинусное сходство между новым запросом и ранее сохраненным превышает 0.85, LiteLLM мгновенно возвращает кэшированный completion.
Для формирования векторных представлений используется быстрая и экономичная модель text-embedding-3-small.
Обновите /opt/llm-stack/litellm-config.yaml:
model_list:
# Основной продакшн-роут (OpenAI Direct)
- model_name: gpt-4o
litellm_params:
model: openai/gpt-4o
api_key: "os.environ/OPENAI_API_KEY"
timeout: 15
max_retries: 1
# Вторичный резервный роут (Azure OpenAI Service)
- model_name: azure-gpt-4o
litellm_params:
model: azure/gpt-4o-deployment
api_base: "os.environ/AZURE_API_BASE"
api_key: "os.environ/AZURE_API_KEY"
api_version: "2024-08-01-preview"
timeout: 15
max_retries: 1
# Локальный резервный роут (Ollama на хосте VPS)
- model_name: local-llama-fallback
litellm_params:
model: ollama/llama3.1:8b
api_base: "http://gateway-ollama:11434"
timeout: 30
# Модель генерации эмбеддингов для семантического кэша
- model_name: text-embedding-cache
litellm_params:
model: openai/text-embedding-3-small
api_key: "os.environ/OPENAI_API_KEY"
litellm_settings:
# Подключение Langfuse для трассировки кэш-хитов и фейловеров
success_callback: ["langfuse"]
failure_callback: ["langfuse"]
# Настройка семантического кэша в Redis
cache:
type: "redis-semantic"
supported_call_types: ["completion", "acompletion"]
similarity_threshold: 0.85
embedding_model: "text-embedding-cache"
redis_host: "gateway-redis"
redis_port: 6379
redis_password: "os.environ/REDIS_PASSWORD"
namespace: "litellm:semantic_cache"
ttl: 86400 # Время жизни кэша: 24 часа
router_settings:
# Алгоритм маршрутизации: наименьшая задержка среди активных
routing_strategy: "latency-based-routing"
# Ограничение частоты опроса неисправных апстримов (Circuit Breaker)
allowed_fails: 2
cooldown_time: 60 # Время вывода провайдера из пула при ошибках (сек)
num_retries: 2
retry_after: 1
# Матрица каскадного переключения: Primary -> Azure -> Local Ollama
fallbacks:
- gpt-4o: ["azure-gpt-4o", "local-llama-fallback"]
# Переключение при переполнении контекстного окна
context_window_fallbacks:
- gpt-4o: ["azure-gpt-4o"]
Перезапустите LiteLLM Proxy для применения обновлений:
docker compose restart litellm-proxy
Верификация семантического кэша и анализ профиля задержки (Latency p99)
Для проверки работы семантического кэша отправим два запроса с различной грамматической структурой, но идентичным смысловым интентом.
Первый запрос (Cache MISS) — обращение к внешнему API с сохранением эмбеддинга в Redis:
source /opt/llm-stack/.env
time curl -s -X POST http://127.0.0.1:4000/chat/completions \
-H "Content-Type: application/json" \
-H "Authorization: Bearer ${LITELLM_MASTER_KEY}" \
-d '{
"model": "gpt-4o",
"messages": [
{"role": "user", "content": "Каковы минимальные системные требования для PostgreSQL 16 в production?"}
],
"temperature": 0.0
}' | jq '.choices[0].message.content'
Замеренное время выполнения: ~940 мс (запрос ушел в OpenAI, токен прошел генерацию, эмбеддинг записан в Redis).
Второй запрос (Cache HIT) — переформулированный вопрос:
time curl -i -s -X POST http://127.0.0.1:4000/chat/completions \
-H "Content-Type: application/json" \
-H "Authorization: Bearer ${LITELLM_MASTER_KEY}" \
-d '{
"model": "gpt-4o",
"messages": [
{"role": "user", "content": "Какие требования к оборудованию и RAM нужны для базы данных Postgres 16 на проде?"}
],
"temperature": 0.0
}'
В HTTP-заголовках ответа LiteLLM возвращает подтверждение кэш-попадания:
HTTP/1.1 200 OK
content-type: application/json
x-litellm-cache-key: litellm:semantic_cache:...
x-litellm-cache-status: HIT
x-litellm-response-time: 0.0052
Время ответа снижается с 940 мс до 5.2 мс (падение задержки более чем в 180 раз).
Проверьте наличие созданного HNSW-индекса и объем ключей в Redis:
docker exec -it gateway-redis redis-cli -a "${REDIS_PASSWORD}" FT._LIST
docker exec -it gateway-redis redis-cli -a "${REDIS_PASSWORD}" INFO memory | grep used_memory_human
Тестирование автоматического фейловера при деградации внешнего API
Протестируем сценарий полного отказа внешнего API OpenAI через блокировку сетевого трафика средствами ядра Linux (iptables), чтобы верифицировать поведение Circuit Breaker в LiteLLM Proxy.
Заблокируйте исходящие соединения к диапазонам публичных адресов OpenAI на хосте:
iptables -I DOCKER-USER -p tcp -m string --string "api.openai.com" --algo bm -j DROP
Выполните запрос к виртуальной модели gpt-4o:
curl -i -X POST http://127.0.0.1:4000/chat/completions \
-H "Content-Type: application/json" \
-H "Authorization: Bearer ${LITELLM_MASTER_KEY}" \
-d '{
"model": "gpt-4o",
"messages": [
{"role": "user", "content": "Explain Linux cgroups v2 memory controller in one sentence."}
],
"metadata": {
"trace_id": "simulated-failure-drill-01"
}
}'
Проанализируйте лог контейнера LiteLLM Proxy в реальном времени:
docker logs --tail 30 litellm-proxy
Журнал зафиксирует срабатывание цепочки отказоустойчивости:
[LiteLLM Router] Exception occurred for model=openai/gpt-4o: APIConnectionError - Request timed out.
[LiteLLM Router] Cooldown triggered for model=openai/gpt-4o. Fails: 1/2.
[LiteLLM Router] Falling back to model=azure-gpt-4o.
[LiteLLM Router] Exception occurred for model=azure/gpt-4o-deployment: APIConnectionError - Connection refused.
[LiteLLM Router] Falling back to model=local-llama-fallback (ollama/llama3.1:8b).
[LiteLLM Router] Success! Request served by local-llama-fallback.
Откройте интерфейс Langfuse (http://<IP_СЕРВЕРА>:3000). В трейсе с идентификатором simulated-failure-drill-01 будет зафиксировано дерево вызовов: 1. openai/gpt-4o — Status: ERROR (Timeout / Network Unreachable). 2. azure-gpt-4o — Status: ERROR. 3. ollama/llama3.1:8b — Status: SUCCESS, с указанием локальной длительности инференса и потребления токенов.
Снимите тестовую блокировку iptables:
iptables -D DOCKER-USER -p tcp -m string --string "api.openai.com" --algo bm -j DROP
Надежность описанной архитектуры опирается на стабильность сетевого стека и отсутствие аппаратных просадок гипервизора. Развертывание шлюза на платформе tropic.host обеспечивает аплинки пропускной способностью 1–10 Гбит/с с активированным по умолчанию алгоритмом управления перегрузками TCP BBR и прямым BGP-пирингом в крупнейших узлах обмена трафиком (DE-CIX Frankfurt, AMS-IX Amsterdam, LINX London). Это гарантирует, что даже в моменты пикового фейловера задержка при перенаправлении трафика между региональными точками API и локальным сервером удерживается в строгих рамках p99 без потери пакетов на L3/L4 уровнях.
Управление бюджетами команд, квоты токенов и маскирование PII-данных
Централизация доступа к upstream-провайдерам нейросетевых моделей требует изоляции бюджетов, предотвращения утечек конфиденциальных данных и гранулярного аудита каждой транзакции. Развертывание связки litellm proxy vps langfuse docker переносит функции enterprise-шлюза авторизации непосредственно на ваш собственный сервер: LiteLLM Proxy выступает в роли защищенного реверс-прокси с поддержкой виртуальных мастер-ключей, а Langfuse аккумулирует метаданные трассировки, оценивая стоимость каждого вызова с детализацией до пользователя и подразделения.
Архитектура виртуальных ключей и многоуровневого биллинга
Прямая передача нативных ключей OpenAI, Anthropic или Azure в клиентские микросервисы и среды разработки создает критические риски компрометации квот и перерасхода бюджета. LiteLLM решает эту задачу с помощью механизма Virtual Keys (псевдо-ключей с префиксом sk-), которые сохраняются в захешированном виде (SHA-256) в базе данных PostgreSQL и кэшируются в оперативной памяти Redis.
Иерархия контроля строится на трех уровнях: 1. Global Master Key — ключ администратора (LITELLM_MASTER_KEY), обладающий полными правами управления прокси через REST API. 2. Team Entity — логическая сущность рабочей группы (команды инженеров или конкретного микросервиса) с собственным лимитом расходов (max_budget), валютой, интервалом сброса баланса (budget_duration: 30d) и пулом разрешенных моделей. 3. User / End-User Key — виртуальный ключ конечного разработчика или приложения, привязанный к команде. Ограничивается собственным бюджетом, лимитами скорости RPM (Requests Per Minute) и TPM (Tokens Per Minute).
Для развертывания этой функциональности в Docker Compose инстансу LiteLLM передаются параметры подключения к базе данных и Redis, управляющему скользящими окнами счетчиков (Leaky Bucket):
services:
litellm-proxy:
image: ghcr.io/berriai/litellm-database:main-v1.49.2
container_name: litellm-proxy
restart: unless-stopped
ports:
- "127.0.0.1:4000:4000"
environment:
- DATABASE_URL=postgresql://litellm_user:SecretPGPass2026@postgres:5432/litellm_db
- REDIS_URL=redis://:SecretRedisPass2026@redis:6379/0
- STORE_MODEL_IN_DB=True
- LITELLM_MASTER_KEY=sk-master-superadmin-cluster-token-2026
volumes:
- ./config.yaml:/app/config.yaml:ro
depends_on:
postgres:
condition: service_healthy
redis:
condition: service_healthy
deploy:
resources:
limits:
cpus: '4.00'
memory: 4096M
reservations:
cpus: '2.00'
memory: 2048M
На уровне ядра операционной системы Linux для высоконагруженного шлюза под управлением Redis и Uvicorn (LiteLLM) требуется исключить задержки на аллокацию памяти и переполнение очередей соединений. В /etc/sysctl.d/99-litellm.conf задаются системные вызовы ядра:
# Предотвращение сбоев фонового снапшота Redis (BGSAVE)
vm.overcommit_memory = 1
# Расширение очереди неполных сокетов TCP для минимизации SYN-дропов
net.core.somaxconn = 4096
net.ipv4.tcp_max_syn_backlog = 4096
# Быстрая утилизация сокетов в состоянии TIME_WAIT
net.ipv4.tcp_tw_reuse = 1
Применение конфигурации:
sysctl -p /etc/sysctl.d/99-litellm.conf
Программное управление квотами через Management API
Создание команд и выпуск виртуальных ключей автоматизируются через REST API прокси. Сначала создается команда разработки с выделенным бюджетом $150 на расчетный период 30 дней:
curl -s -X POST "http://127.0.0.1:4000/team/new" \
-H "Authorization: Bearer sk-master-superadmin-cluster-token-2026" \
-H "Content-Type: application/json" \
-d '{
"team_alias": "backend-ml-core",
"max_budget": 150.00,
"budget_duration": "30d",
"models": ["openai/gpt-4o", "azure-gpt-4o", "local-llama-fallback"],
"tpm_limit": 200000,
"rpm_limit": 120
}'
Ответ шлюза содержит уникальный team_id:
{
"team_id": "b3f81e82-2091-4cf1-8c01-789a241d019f",
"team_alias": "backend-ml-core",
"max_budget": 150.0,
"budget_duration": "30d",
"spend": 0.0
}
Далее для конкретного инженера или микросервиса генерируется виртуальный ключ с жестким персональным лимитом $25 внутри бюджета команды:
curl -s -X POST "http://127.0.0.1:4000/key/generate" \
-H "Authorization: Bearer sk-master-superadmin-cluster-token-2026" \
-H "Content-Type: application/json" \
-d '{
"team_id": "b3f81e82-2091-4cf1-8c01-789a241d019f",
"key_alias": "dev-user-ivanov",
"user_id": "[email protected]",
"max_budget": 25.00,
"duration": "60d",
"metadata": {
"department": "core-backend",
"env": "staging"
}
}'
Выходной JSON возвращает токен вида sk-litellm-7hK9..., который передается клиенту. При превышении заданного значения max_budget шлюз моментально блокирует обработку запросов, возвращая клиенту код ответа HTTP 429 Too Many Requests с телом:
{
"error": {
"message": "LiteLLM Virtual Key budget exceeded. Current spend: 25.02, Max budget: 25.00",
"type": "budget_exceeded",
"code": "budget_exceeded_error"
}
}
Все проверки расходов происходят атомарно в Redis с помощью скомпилированных Lua-скриптов LiteLLM. Это исключает возникновение состояния гонки (race conditions) при параллельных асинхронных запросах от распределенных воркеров, а накладные расходы на валидацию ключа не превышают 0.8–1.2 мс на запрос.
Маскирование персональных данных (PII/DLP Guardrails)
Передача персональных идентификаторов (PII), номеров банковских карт, секретных токенов и медицинских данных во внешние облачные API противоречит регламентам безопасности (152-ФЗ, GDPR, PCI-DSS). Настройка маскирования на уровне шлюза перехватывает полезную нагрузку до ее отправки в апстрим.
В LiteLLM встроен коннектор к Microsoft Presidio (движку обнаружения и десенсибилизации PII) и механизм пре-хуков (Guardrails). Развертывание дополнительного контейнера Presidio в общем сетевом пространстве Docker:
presidio-analyzer:
image: mcr.microsoft.com/presidio-analyzer:latest
container_name: presidio-analyzer
restart: unless-stopped
ports:
- "127.0.0.1:5001:3000"
presidio-anonymizer:
image: mcr.microsoft.com/presidio-anonymizer:latest
container_name: presidio-anonymizer
restart: unless-stopped
ports:
- "127.0.0.1:5002:3000"
В конфигурационном файле config.yaml шлюза активируется пре-обработка входящих запросов:
litellm_settings:
callbacks: ["langfuse"]
guardrails:
- prompt_injection: false
- presidio:
api_base: "http://presidio-analyzer:3000"
anonymizer_url: "http://presidio-anonymizer:3000"
entities:
- "EMAIL_ADDRESS"
- "PHONE_NUMBER"
- "CREDIT_CARD"
- "CRYPTO"
- "IP_ADDRESS"
- "PERSON"
masking_strategy: "redact" # Замена на <EMAIL_ADDRESS>, <PHONE_NUMBER> и т.д.
router_settings:
routing_strategy: "latency-based-routing"
Когда клиент отправляет промпт:
"Заказчик John Doe (тел. +7-999-123-45-67, e-mail: [email protected]) заблокировал карту 4111222233334444. Сформируй ответ службы поддержки."
LiteLLM перед выполнением сетевого вызова к OpenAI преобразует текст в десенсибилизированный вид:
"Заказчик<PERSON>(тел.<PHONE_NUMBER>, e-mail:<EMAIL_ADDRESS>) заблокировал карту<CREDIT_CARD>. Сформируй ответ службы поддержки."
При этом downstream-сервис получает ответ без единой утечки исходных пользовательских данных за пределы локального контура VPS.
Сквозной аудит и аналитика затрат в Langfuse
Аудит операций реализуется через прямую интеграцию с платформой наблюдаемости Langfuse. Связка регистрирует не только факт вызова, но и сопоставляет исходный входящий запрос, модифицированный (десенсибилизированный) промпт, точное число токенов (Prompt, Completion, Total) и итоговую стоимость генерации в долларах США на основе встроенной матрицы цен LiteLLM.
Передача переменных окружения в контейнер litellm-proxy:
LANGFUSE_PUBLIC_KEY="pk-lf-0824bba7-827c-4029-a1b9-38e9a22d4f21"
LANGFUSE_SECRET_KEY="sk-lf-71ac984e-37bb-4180-87b6-19e4823da09c"
LANGFUSE_HOST="http://langfuse-server:3000"
При каждом вызове клиента виртуальный ключ передает контекст в заголовках. Запрос через LiteLLM Proxy:
curl -s -X POST "http://127.0.0.1:4000/v1/chat/completions" \
-H "Authorization: Bearer sk-litellm-7hK9..." \
-H "Content-Type: application/json" \
-H "X-Langfuse-Session-Id: session-checkout-991" \
-H "X-Langfuse-Tags: production,checkout-flow" \
-d '{
"model": "openai/gpt-4o",
"messages": [
{"role": "system", "content": "Ты ассистент службы безопасности."},
{"role": "user", "content": "Проверь статус транзакции для клиента [email protected]"}
],
"temperature": 0.2
}'
На стороне веб-интерфейса Langfuse формируется развернутый трейс: * Trace Name: chat/completions * User ID: [email protected] (из метаданных виртуального ключа) * Metadata: team_id: b3f81e82-..., team_alias: backend-ml-core, key_alias: dev-user-ivanov * Calculated Cost: $0.000125 * Latency breakdown: * presidio_masking: 18 ms * litellm_proxy_overhead: 1.4 ms * llm_time_to_first_token: 240 ms * total_execution_time: 480 ms
Это позволяет финансовым и DevOps-инженерам в реальном времени строить отчеты Cost Allocation, отслеживать аномальные всплески потребления токенов конкретными микросервисами и автоматически отключать скомпрометированные виртуальные токены без остановки общего пайплайна.
Требования к серверной инфраструктуре и оптимизация дискового стека
В архитектуре, где каждый входящий запрос порождает транзакцию в PostgreSQL (списание баланса виртуального ключа), атомарное обновление счетчиков в Redis и асинхронный HTTP-экспорт событий в Langfuse, производительность ввода-вывода (I/O) и отсутствие виртуализационных задержек играют определяющую роль.
При пиковых нагрузках в 300–800 rps медленные дисковые операции при записи журналов упреждающей записи PostgreSQL (wal_level = minimal / replica) и сброса снапшотов Redis AOF (appendfsync everysec) приводят к образованию очередей на системном вызове epoll_wait, из-за чего latency p99 всего шлюза деградирует с предсказуемых 15–20 мс до катастрофических сотен миллисекунд.
Именно поэтому для продуктивного развертывания шлюза критически важен выбор аппаратной платформы. Хостинг-провайдер tropic.host предоставляет чистую виртуализацию KVM с полным отсутствием оверселлинга процессоров (показатель CPU Steal Time в выводе vmstat или top строго зафиксирован на значении %st = 0.0%). Инстансы работают на высокочастотных серверных ядрах AMD EPYC и Ryzen 9, а дисковая подсистема построена на серверных NVMe-накопителях PCIe 4.0 корпоративного уровня с показателями случайного чтения 4K QD1 свыше 50 000 IOPS.
Такой аппаратный фундамент исключает троттлинг при тяжелых транзакционных операциях PostgreSQL и гарантирует, что локальная обработка квот, десенсибилизация PII и маршрутизация сетевых пакетов выполняются без задержек. В сочетании с выделенными аплинками до 10 Гбит/с и защитой от DDoS на уровнях L3/L4/L7, облачная инфраструктура tropic.host гарантирует непрерывную доступность и изолированность корпоративного контура инференса моделей.
Отказоустойчивость, бэкапы баз данных и регламент аварийного восстановления
Архитектура распределенного стека, объединяющего шлюз LiteLLM Proxy и платформу наблюдаемости Langfuse в среде Docker на VPS, разделяет персистентное состояние на два изолированных уровня с принципиально разным профилем записи. Первый уровень — строго транзакционные метаданные PostgreSQL (виртуальные API-ключи, бюджетные лимиты команд, RBAC-роли, настройки роутинга моделей и пользовательские сессии). Второй уровень — аналитический массив временных рядов (time-series traces, спаны вызовов, токены, скоринг и метаданные промптов), агрегируемый Langfuse в колоночной СУБД ClickHouse и таблицах PostgreSQL. Потеря или повреждение любого из этих хранилищ вследствие аварии файловой системы, программного сбоя контейнера или некорректной миграции приводит к остановке всего инференс-контура. Для обеспечения целевых показателей RPO (Recovery Point Objective) $\le 15$ минут и RTO (Recovery Time Objective) $\le 5$ минут необходима комбинированная стратегия горячего резервирования с последующим сценарием Disaster Recovery (DR) без прерывания обработки клиентского трафика.
Горячее резервирование транзакционного состояния PostgreSQL
LiteLLM Proxy критически чувствителен к консистентности таблиц LiteLLM_VerificationToken, LiteLLM_UserTable и LiteLLM_Config. Использование стандартного docker exec pg_dump > backup.sql в продакшне недопустимо: однопоточный вывод блокирует буферы ввода-вывода, не гарантирует целостность при параллельных транзакциях без блокирующих блокировок и создает пиковую нагрузку на подсистему памяти. Резервное копирование реализуется через многопоточный дамп в каталожном формате (directory format) с утилитой сжатия pigz, использующей системный вызов splice для минимизации копирования данных между пространством ядра и пользователя.
Для исключения деградации latency p99 основного шлюза процесс резервного копирования изолируется в отдельном контейнере или запускается через systemd-таймер с жесткими лимитами cgroups v2 по CPU и дисковому I/O:
#!/usr/bin/env bash
set -Eeuo pipefail
BACKUP_DIR="/var/backups/postgres"
TIMESTAMP=$(date +"%Y%m%d_%H%M%S")
TARGET_DIR="${BACKUP_DIR}/dump_${TIMESTAMP}"
POSTGRES_CONTAINER="litellm-postgres-1"
POSTGRES_USER="litellm_admin"
POSTGRES_DB="litellm_db"
RETENTION_DAYS=7
mkdir -p "${TARGET_DIR}"
# Создание консистентного snapshot-дампа в 4 потока через pg_dump custom directory format
docker exec -i "${POSTGRES_CONTAINER}" \
pg_dump \
-U "${POSTGRES_USER}" \
-d "${POSTGRES_DB}" \
--format=directory \
--jobs=4 \
--compress=6 \
--no-owner \
--no-privileges \
--file="/tmp/dump_${TIMESTAMP}"
# Перенос артефактов из контейнера на хостовую NVMe-подсистему
docker cp "${POSTGRES_CONTAINER}:/tmp/dump_${TIMESTAMP}/." "${TARGET_DIR}/"
docker exec -i "${POSTGRES_CONTAINER}" rm -rf "/tmp/dump_${TIMESTAMP}"
# Формирование SHA256 контрольной суммы для валидации целостности
cd "${BACKUP_DIR}"
sha256sum "dump_${TIMESTAMP}"/* > "dump_${TIMESTAMP}.sha256"
# Асинхронная синхронизация в S3-совместимое объектное хранилище
rclone copy "${TARGET_DIR}" "s3-backup:litellm-backups/postgres/dump_${TIMESTAMP}" \
--fast-list \
--transfers 4 \
--buffer-size 32M
# Ротация локальных копий старше RETENTION_DAYS
find "${BACKUP_DIR}" -mindepth 1 -maxdepth 1 -type d -mtime +${RETENTION_DAYS} -exec rm -rf {} +
find "${BACKUP_DIR}" -mindepth 1 -maxdepth 1 -name "*.sha256" -mtime +${RETENTION_DAYS} -delete
Параметр --jobs=4 распределяет выгрузку таблиц по независимым сессиям СУБД с синхронизацией момента снимка через механизм транзакционных снэпшотов PostgreSQL (Export Snapshot), что предотвращает фантомные чтения и логическую рассинхронизацию балансов виртуальных ключей LiteLLM.
Резервное копирование аналитического хранилища ClickHouse
ClickHouse, используемый современными версиями Langfuse для хранения миллионов спанов и тяжелых трейсов инференса моделей, оперирует иммутабельными партами данных (data parts). Создание резервных копий через SQL-запросы SELECT INTO OUTFILE приводит к исчерпанию оперативной памяти и падению OOM Killer. Стандартом резервирования ClickHouse в Docker является профильная утилита clickhouse-backup, взаимодействующая с ядром СУБД через механизм создания жестких ссылок (hard links) посредством системного вызова linkat при выполнении команды ALTER TABLE ... FREEZE.
Конфигурационный файл /etc/clickhouse-backup/config.yml монтируется в сайдкар-контейнер:
clickhouse:
username: "default"
password: "${CLICKHOUSE_PASSWORD}"
host: "clickhouse"
port: 9000
disk_mapping:
default: "/var/lib/clickhouse"
timeout: "5m"
freeze_by_part: true
s3:
type: "s3"
endpoint: "s3.storage.example.com"
bucket: "langfuse-clickhouse-backups"
access_key: "${S3_ACCESS_KEY}"
secret_key: "${S3_SECRET_KEY}"
compression_level: 3
compression_format: "zstd"
part_size: 104857600 # 100 MB
concurrency: 4
Выполнение бэкапа инициируется командой:
docker exec -i langfuse-clickhouse-backup clickhouse-backup create --tables=langfuse.* backup_$(date +%Y%m%d_%H%M%S)
docker exec -i langfuse-clickhouse-backup clickhouse-backup upload backup_$(date +%Y%m%d_%H%M%S)
Механизм freeze_by_part исключает копирование данных внутри файловой системы: операция создания точки восстановления занимает менее 200 мс вне зависимости от объема базы данных (даже при размере аналитики свыше 500 ГБ), а сброс данных в S3 выполняется в фоновом режиме через потоковый zstd-компрессор.
Аппаратная изоляция I/O и лимиты cgroups v2
Резервное копирование и компрессия дампов создают взрывную нагрузку на дисковую подсистему и процессор. При некорректной настройке ввода-вывода дисковый планировщик Linux забивает очередь запросов к контроллеру, из-за чего системные вызовы fdatasync со стороны PostgreSQL блокируют транзакции списания квот LiteLLM.
На виртуальных серверах хостинг-провайдера tropic.host аппаратная база исключает оверселлинг процессорных мощностей (%st = 0.0% по данным vmstat 1), а серверные NVMe-накопители PCIe 4.0 обеспечивают параллельную обработку случайных операций чтения-записи (4K QD1 свыше 50 000 IOPS). Однако на уровне ядра ОС хоста требуется явное разграничение весов ввода-вывода через cgroups v2, чтобы фоновый бэкап не вытеснял из кэша горячие страницы оперативной памяти.
В файле docker-compose.yml для сервисов бэкапа и основных баз данных задаются приоритеты и лимиты:
services:
litellm-postgres:
image: postgres:16-alpine
restart: always
deploy:
resources:
limits:
cpus: '4.0'
memory: 8G
reservations:
cpus: '2.0'
memory: 4G
# Приоритезация блочного ввода-вывода cgroups v2
blkio_config:
weight: 800
postgres-backup-runner:
image: alpine:3.20
restart: "no"
deploy:
resources:
limits:
cpus: '1.5'
memory: 1G
blkio_config:
weight: 100 # Минимальный приоритет I/O относительно базы данных
На уровне хостовой операционной системы тюнингуются параметры подсистемы виртуальной памяти ядра (/etc/sysctl.d/99-dr-io.conf):
# Предотвращение накопления грязных страниц и резких зависаний I/O при записи дампов
vm.dirty_background_ratio = 5
vm.dirty_ratio = 10
vm.dirty_writeback_centisecs = 500
vm.dirty_expire_centisecs = 1500
Применение конфигурации:
sysctl --system
Регламент аварийного восстановления (Disaster Recovery) без остановки продакшна
Восстановление состояния шлюза при повреждении базы данных или аппаратной замене хоста выполняется по принципу Shadow Instance (теневого контура) с атомарным переключением сетевых маршрутов. Прямое накатывание дампа поверх работающей СУБД с активными входящими подключениями от LiteLLM Proxy приводит к дедлокам, частичной записи данных и выдаче клиентам ошибок 500 Internal Server Error.
Фаза 1. Развертывание изолированного теневого инстанса СУБД
На VPS в существующем сетевом пространстве Docker запускается временный изолированный контейнер PostgreSQL без публикации портов наружу:
docker run -d \
--name postgres-shadow \
--network litellm-internal \
--env POSTGRES_USER=litellm_admin \
--env POSTGRES_PASSWORD=recovery_secret_pass \
--env POSTGRES_DB=litellm_recovery \
--tmpfs /dev/shm:rw,noexec,nosuid,size=1g \
-v postgres-shadow-data:/var/lib/postgresql/data \
postgres:16-alpine \
-c shared_buffers=2GB \
-c work_mem=64MB \
-c maintenance_work_mem=1GB \
-c max_wal_size=16GB \
-c checkpoint_timeout=30min \
-c synchronous_commit=off
Временное отключение synchronous_commit и агрессивное выделение maintenance_work_mem ускоряют перестроение индексов B-tree при импорте в 4–6 раз.
Фаза 2. Параллельный импорт и валидация целостности данных
Загрузка дампа из хранилища и накат схемы с данными выполняются с параллелизмом по числу доступных ядер vCPU:
# Скачивание бэкапа из S3
rclone copy "s3-backup:litellm-backups/postgres/dump_20261011_120000" /var/backups/restore_tmp/
# Проверка контрольной суммы
cd /var/backups/restore_tmp
sha256sum -c ../dump_20261011_120000.sha256
# Восстановление через pg_restore в 4 потока
docker run --rm \
--network litellm-internal \
-v /var/backups/restore_tmp:/restore:ro \
postgres:16-alpine \
pg_restore \
-h postgres-shadow \
-U litellm_admin \
-d litellm_recovery \
--jobs=4 \
--exit-on-error \
--no-owner \
--no-privileges \
/restore
Перед переключением боевого трафика выполняется строгая проверка логической целостности ключей, схем и реляционных связей:
docker exec -i postgres-shadow psql -U litellm_admin -d litellm_recovery -v ON_ERROR_STOP=1 <<'EOF'
-- Проверка существования ключевых таблиц и ненулевого количества ключей
DO $$
DECLARE
token_count INT;
user_count INT;
BEGIN
SELECT COUNT(*) INTO token_count FROM "LiteLLM_VerificationToken";
SELECT COUNT(*) INTO user_count FROM "LiteLLM_UserTable";
IF token_count = 0 THEN
RAISE EXCEPTION 'DR Validation Failed: Таблица LiteLLM_VerificationToken пуста!';
END IF;
RAISE NOTICE 'DR Validation Success: Обнаружено токенов: %, пользователей: %', token_count, user_count;
END $$;
-- Проверка целостности внешних ключей и индексов
SELECT schemaname, relname, indexrelname
FROM pg_statio_all_indexes
WHERE idx_blks_read IS NULL AND schemaname = 'public';
EOF
Фаза 3. Восстановление аналитических спанов ClickHouse
Восстановление ClickHouse осуществляется целевой выгрузкой партов без блокировки сетевых портов:
docker exec -i langfuse-clickhouse-backup clickhouse-backup download backup_20261011_120000
docker exec -i langfuse-clickhouse-backup clickhouse-backup restore --rm backup_20261011_120000
Флаг --rm принудительно очищает поврежденные таблицы перед заливкой hard links, предотвращая конфликт метаданных в каталоге metadata/langfuse/.
Фаза 4. Бесшовное переключение сетевых маршрутов (Zero-Downtime Switchover)
Архитектура LiteLLM Proxy построена на пуле асинхронных соединений SQLAlchemy с драйвером asyncpg. Чтобы шлюз переключился на восстановленную базу данных без перезапуска контейнера и сброса кэша маршрутов в оперативной памяти, шлюз настраивается на работу через внутренний DNS-алиас Docker или локальный балансировщик (HAProxy/PgBouncer).
Если используется стандартный Docker Compose, переключение реализуется манипуляцией сетевыми алиасами (network aliases):
# 1. Возврат настроек теневой БД в строгий production-режим
docker exec -i postgres-shadow psql -U litellm_admin -d litellm_recovery -c "ALTER SYSTEM SET synchronous_commit = 'on';"
docker exec -i postgres-shadow psql -U litellm_admin -d litellm_recovery -c "SELECT pg_reload_conf();"
# 2. Отключение поврежденного инстанса от внутренней сети
docker network disconnect litellm-internal litellm-postgres-1
# 3. Присоединение теневого инстанса с боевым сетевым псевдонимом СУБД
docker network connect --alias db-postgres litellm-internal postgres-shadow
# 4. Graceful-сигнал LiteLLM Proxy для ротации невалидных сессий пула соединений
docker kill --signal=SIGHUP litellm-proxy-1
LiteLLM Proxy перехватывает сигнал конфигурационной ротации (или автоматически сбрасывает разорванные сокеты при включенном параметре DATABASE_POOL_PRE_PING=True), инициируя переподключение к IP-адресу db-postgres, который теперь разрешается ядром Docker DNS (127.0.0.11) на IP контейнера postgres-shadow. Входящие HTTP-запросы на эндпоинты /chat/completions в течение 100–300 мс удерживаются во входящем бэклоге сетевого сокета без разрыва TCP-сессий с клиентами.
Использование высоконадежной инфраструктуры tropic.host гарантирует, что даже во время масштабных операций восстановления и интенсивного I/O пропускная способность физического сетевого интерфейса (до 10 Гбит/с) и аппаратная изоляция ядер процессора предотвращают возникновение сетевых джиттеров и потерю пакетов во взаимодействии между LiteLLM Proxy, Langfuse и базами данных. После успешного переключения поврежденный том данных демонтируется для последующего форензик-анализа, а postgres-shadow переименовывается в основной рабочий узел с обновлением маппинга постоянных томов volumes.
Часто задаваемые вопросы (FAQ)
Сколько ресурсов требует связка LiteLLM Proxy и Langfuse на VPS?
Для команды до 50 разработчиков оптимален сервер с 4 vCPU, 8 GB RAM и 80 GB NVMe. LiteLLM легковесен, но Langfuse использует PostgreSQL и ClickHouse для трекинга трейсов.
Как семантический кэш LiteLLM экономит бюджет на токены?
При повторяющихся или семантически близких запросах ответ отдается напрямую из локального Redis-кэша за 5 мс без обращения к платным API, что снижает расходы на 30–40%.