Перейти к основному содержимому

Чтобы получить чистую Markdown-версию этой страницы, добавьте .md к этому URL. Полный индекс документации см. в https://docs.nvidia.com/dynamo/llms.txt. Полное содержимое, включая справочник API и примеры SDK, см. в https://docs.nvidia.com/dynamo/llms-full.txt.

Эксплуатация Router

На этой странице рассматриваются эксплуатационные вопросы для развертываний router на этапе day-2. Сведения о флагах и настройке см. в Конфигурация и настройка.

Обслуживание нескольких реплик Router

Для повышения отказоустойчивости можно запускать несколько реплик frontend-plus-router. Если несколько процессов dynamo.frontend работают на одном хосте или в одном сетевом namespace, назначьте каждому экземпляру отдельный HTTP-порт. В Kubernetes или на отдельных хостах реплики обычно могут использовать один и тот же контейнерный порт. В качестве альтернативы можно развернуть router отдельно как автономный сервис python -m dynamo.router.

Управление состоянием Router

KV router поддерживает две независимые группы состояний с разным поведением синхронизации, сохранения и восстановления:

  1. Состояние prefix cache: глобальное представление о кэшированных KV prefix blocks на workers. Это состояние определяет оценку cache-overlap.
  2. Состояние active block: представление router о KV blocks, которые сейчас назначены выполняющимся запросам. Это состояние определяет балансировку active-load.

Архитектура, лежащая в основе этих состояний, описана в Проектирование Router.

Состояние prefix cache

Состояние prefix cache поддерживается KV indexer в каждом router или frontend. В режиме event-driven workers публикуют KV-события Stored и Removed, а каждая реплика router потребляет эти события, чтобы обновлять свое radix tree. Поскольку KV-события распространяются через event plane, несколько реплик router естественным образом получают одни и те же обновления prefix cache; для prefix blocks им не нужна синхронизация между router.

Когда используется --no-router-kv-events, router не потребляет KV-события workers. Вместо этого он прогнозирует состояние cache на основе собственных решений маршрутизации и истекает predicted blocks с помощью --router-ttl-secs. Этот приблизительный режим полезен для разработки или для backend, у которых KV-события еще недостаточно надежны, но для production он не рекомендуется.

Сохранение и восстановление prefix cache

Восстановление prefix cache важно, потому что устаревшее или отсутствующее prefix state напрямую влияет на решения маршрутизации по cache-hit. Dynamo поддерживает две стратегии восстановления.

Режим NATS Core / Event Plane с локальным indexer
  • Prefix state сохраняется на workers. События отправляются по схеме fire-and-forget, но workers сохраняют состояние своего local indexer.
  • При запуске каждый router запрашивает local indexer каждого worker, чтобы восстановить prefix state.
  • Восстановление зависит от доступности workers. Если worker недоступен, его blocks нельзя восстановить, пока он не вернется.
  • Этот режим упрощает инфраструктуру, потому что JetStream не требуется.

Подробнее о detection of gaps и replay см. в KV Event Replay — Dynamo vs vLLM.

Режим JetStream

Режим JetStream требует --router-durable-kv-events и на frontend, и на workers.

  • Prefix blocks хранятся в NATS JetStream с retention на 1 час.
  • Snapshots сохраняются в NATS object store при настраиваемых порогах.
  • Новые реплики автоматически восстанавливают это состояние при запуске.
  • Можно запустить третью реплику router, даже если первые две недоступны, и она восстановит полное prefix state.
python -m dynamo.frontend \
--router-mode kv \
--http-port 8002 \
--router-durable-kv-events

Если нужно начать с чистого состояния в режиме JetStream, есть два варианта:

  1. Использовать другой namespace или component, чтобы создать новый stream и путь в NATS object store.
  2. Запустить router с --router-reset-states, который очищает весь stream и radix snapshot. Делайте это только при запуске первой реплики router в component, потому что иначе можно привести существующие реплики к несогласованному состоянию.

Состояние active block

Состояние active block отслеживает нагрузку по выполняющимся запросам. Оно выводится из жизненного цикла запроса: router регистрирует запрос, когда назначает его worker, обновляет завершение prefill и, при необходимости, рост output-block по мере поступления ответов и освобождает запрос после завершения.

Это состояние намеренно временное. Если реплика router перезапускается, она начинает без знаний о active-block. Обычно это приемлемо для fault tolerance, потому что активные запросы живут недолго по сравнению с prefix cache state: старые active blocks покидают систему по мере завершения запросов, а представление router снова становится точным, когда он обрабатывает новые запросы.

Эксплуатационный вопрос здесь — синхронизация реплик. Active blocks отслеживаются локально тем router, который маршрутизировал запрос, поэтому несколько реплик frontend или router автоматически не разделяют одно и то же представление active-load.

Синхронизация реплик Active Block

Для active blocks есть два режима работы:

  • Локальное отслеживание: оставить реплики несинхронизированными. Каждый router балансирует нагрузку, используя только подмножество активных запросов, которые он маршрутизировал сам. Это проще и может быть приемлемо, когда трафик и так хорошо распределен между репликами или когда точность active-load менее важна.
  • Синхронизация реплик: включить --router-replica-sync, чтобы реплики публиковали и подписывались на lifecycle events active-sequence через NATS core messaging. Это дает каждой реплике более полное представление о active-load по всему fleet router.
# Router replica 1
python -m dynamo.frontend --router-mode kv --http-port 8000 --router-replica-sync

# Router replica 2
python -m dynamo.frontend --router-mode kv --http-port 8001 --router-replica-sync

При включенном replica sync новый router по-прежнему стартует без знаний о active-block, но затем сходится к актуальному состоянию за счет обработки живых запросов и событий active-sequence от других реплик. Без этого каждая реплика хранит изолированное представление active-block, что может приводить к неоптимальному load balancing.

Удаленный indexer в Dynamo-native

В развертываниях Dynamo-native remote indexer обслуживается dynamo.frontend или dynamo.router, а не dynamo.indexer.

  • Используйте --serve-indexer на репликах router или frontend, которые должны предоставлять kv_indexer_query из worker component.
  • Используйте --use-remote-indexer на consumer router или frontend, которые должны опрашивать этот опубликованный endpoint вместо ведения локального overlap indexer.
  • dynamo.indexer остается автономным HTTP+ZMQ microservice для non-Dynamo или direct-ZMQ развертываний.

Пример для frontend:

# Serving anchors
python -m dynamo.frontend --router-mode kv --serve-indexer

# Consumer frontend
python -m dynamo.frontend --router-mode kv --use-remote-indexer

Опубликованный сервис работает только на уровне request plane. Каждый serving router или frontend сохраняет обычный локальный прием KV-событий, detection of gaps и путь recovery через worker-query; remote consumers только выполняют hash-based overlap queries.

Приблизительный режим (--no-router-kv-events) для remote serving допускает только singleton: для данного worker component может существовать только одна реплика --serve-indexer. Event-driven mode позволяет несколько serving replicas за одним и тем же worker component.

graph TD
subgraph "Workers"
W1["Worker 1"]
W2["Worker 2"]
end

subgraph "Event Plane"
EP["KV Events"]
end

subgraph "Serving Routers / Frontends"
S1["Router / Frontend A<br/>--serve-indexer"]
S2["Router / Frontend B<br/>--serve-indexer"]
I1["Local Indexer"]
I2["Local Indexer"]
end

subgraph "Request Plane"
RP["backend.kv_indexer_query"]
end

C["Consumer Router / Frontend<br/>--use-remote-indexer"]

W1 --> EP
W2 --> EP
EP --> S1
EP --> S2
S1 --> I1
S2 --> I2
C --> RP
RP --> S1
RP --> S2

Дополнительные заметки

Транспорт request plane не зависит от transport KV events. Request plane (DYN_REQUEST_PLANE or --request-plane) управляет тем, как requests доходят до workers. KV events используют NATS в режимах JetStream или NATS Core, либо ZMQ, когда задан --event-plane zmq. При --event-plane zmq и --discovery-backend file или mem router может работать без etcd или NATS. При использовании NATS-based event plane NATS инициализируется автоматически; задайте NATS_SERVER=nats://..., чтобы переопределить значение по умолчанию localhost:4222.

Когда --router-kv-overlap-score-credit установлен в 0, KV indexer не создается и prefix matching отключается. Когда задан --no-router-kv-events, KV indexer все еще создается, но подписчик событий не запускается; router прогнозирует cache state на основе собственных routing decisions с TTL-based expiration.

Публикация KV events на backend не зависит от флага frontend --no-router-kv-events. Флаг frontend управляет тем, потребляет ли router события; флаги backend управляют тем, публикуют ли их workers. Если router не потребляет события, workers, которые все равно публикуют их, будут тратить ресурсы, но не причинят вреда.

  • vLLM: Передайте --kv-events-config '{"enable_kv_cache_events": false}', чтобы отключить, или '{"enable_kv_cache_events": true, "publisher": "zmq", "endpoint": "tcp://*:5557"}', чтобы включить.
  • SGLang: Передайте --kv-events-config с JSON-конфигурацией, чтобы включить, или не указывайте его, чтобы оставить публикацию отключенной.
  • TRT-LLM: Передайте --publish-events-and-metrics, чтобы включить, или не указывайте его, чтобы оставить публикацию отключенной.

Аргумент CLI --router-ttl-secs управляет временем жизни local cache prediction, когда router работает без получения событий от workers. Когда workers настроены на публикацию KV events, router полагается на worker-side eviction events, и этот параметр игнорируется.

--router-queue-threshold и busy thresholds (--active-decode-blocks-threshold, --active-prefill-tokens-threshold, --active-prefill-tokens-threshold-frac) служат разным целям. Busy thresholds полностью исключают worker из набора кандидатов, когда он превышает предел utilization. В отличие от них, --router-queue-threshold откладывает все routing decision, пока хотя бы у одного worker не появится capacity, чтобы запрос был маршрутизирован с самыми свежими метриками нагрузки. Busy thresholds можно обновлять во время работы без перезапуска frontend через HTTP endpoint /busy_threshold. О различии между eligibility и backpressure см. Router Filtering. Подробности о поведении при rejection см. в Request Rejection.