Настроить high-cardinality metrics в VictoriaMetrics

ТЕХНИЧЕСКОЕ ЗАДАНИЕ: Настроить high-cardinality metrics в VictoriaMetrics

1. Цель задачи

Научиться проектировать и развёртывать систему сбора метрик, способную обрабатывать высококардинальные лейблы (например, user_id, prompt_hash), с которыми стандартный Prometheus не справляется из-за роста памяти и производительности. Выполнить переход с Prometheus на VictoriaMetrics (single-node или cluster), настроить сбор, хранение и визуализацию таких метрик.

Ключевой результат Рабочая конфигурация VictoriaMetrics, принимающая метрики с лейблами user_id (уникальных >10k) и prompt_hash (>100k), с дашбордом в Grafana для мониторинга cardinality и query latency.


2. Исходные данные

Что нужноОткуда взять
Docker-окружение (Linux/MacOS)Локальная машина или виртуалка
Prometheus (уже развёрнут или симуляция)docker-compose.yml с Prometheus + node_exporter (пример из репозитория)
VictoriaMetrics (single-node)Официальный Docker-образ victoriametrics/victoria-metrics
vmagent (опционально)victoriametrics/vmagent
Grafana + PromQLDocker-образ grafana/grafana
Генератор тестовых метрик с high-cardinalityСобственный Python-скрипт (см. Этап 3)
Нагрузочный тест (опционально)vegeta или hey для симуляции запросов

Если нет реального Prometheus — симулируем:

  1. Развернуть prom/prometheus с node_exporter и собственным test-exporter, который генерирует метрику llm_request_total с лейблами user_id (от 1000 до 50000) и prompt_hash (от 10000 до 100000). Код экспортёра — Python Flask-приложение.
  2. Запустить генерацию в течение 5–10 минут, чтобы prometheus начал страдать от cardinality (можно отследить spike памяти).
  3. Затем переключить remote_write на VictoriaMetrics.

3. Технологический стек

КомпонентИнструментыНазначение
МониторингVictoriaMetrics (single-node)Хранение high-cardinality метрик, downsampling, retention
Сбор метрикPrometheus + vmagentАгент с фильтрацией/ремаппингом лейблов
Симулятор метрикPython + Flask + prometheus_clientГенерация тестовых high-cardinality метрик
ВизуализацияGrafana 10+Дашборды cardinality, query latency, compression ratio
Нагрузочное тестированиеvegeta, wrkПроверка производительности VM под нагрузкой
Инструментарийcurl, jq, docker-composeУправление и дебаг

4. Этапы выполнения

Этап 1: Развёртывание VictoriaMetrics (30 мин)

Действия

  1. Создать директорию проекта vm-highcard.
  2. Написать docker-compose.yml с сервисами:
    • victoriametrics (образ victoriametrics/victoria-metrics:latest), порты 8428 (HTTP API), 2003 (Carbon), настроить флаги:
      - '--storageDataPath=/storage'
      - '--retentionPeriod=30d'
      - '--downsampling.period=5m:1m,1h:5m,30d:1h'
      
    • grafana (образ grafana/grafana), порт 3000.
    • test-exporter (соберём на Этапе 3).
  3. Запустить docker-compose up -d.
  4. Проверить работоспособность:
    curl http://localhost:8428/metrics  # метрики самой VM
    curl http://localhost:8428/api/v1/query?query=up
    

Ожидаемый результат этапа VictoriaMetrics доступна, Grafana запущена, можно заходить admin:admin.


Этап 2: Настройка сбора метрик (45 мин)

Действия

  1. Поднять prometheus в том же docker-compose (optional — для теста remote write), либо сразу настроить vmagent как агент.
  2. Написать конфиг для vmagent (vmagent.yml):
    scrape_configs:
      - job_name: 'test-exporter'
        scrape_interval: 10s
        static_configs:
          - targets: ['test-exporter:8000']
    remote_write:
      - url: http://victoriametrics:8428/api/v1/write
        # опционально: настройка очереди, ретраев
    
  3. Запустить vmagent (добавить сервис в docker-compose).
  4. Убедиться, что данные пишутся в VM:
    curl 'http://localhost:8428/api/v1/query?query=llm_request_total'
    
    Ответ должен содержать data.result с разными лейблами.

Ожидаемый результат этапа Метрики от симулятора появляются в VM.


Этап 3: Генерация high-cardinality метрик (30 мин)

Действия

  1. Написать test-exporter.py с использованием prometheus_client:
from prometheus_client import start_http_server, Counter
import random
import time
import string

c = Counter('llm_request_total', 'Total LLM requests', ['user_id', 'prompt_hash'])

def random_string(length=8):
    return ''.join(random.choices(string.ascii_lowercase + string.digits, k=length))

if __name__ == '__main__':
    start_http_server(8000)
    while True:
        user_id = f'user_{random.randint(1, 50000)}'
        prompt_hash = f'ph_{random_string(10)}'
        c.labels(user_id=user_id, prompt_hash=prompt_hash).inc(random.randint(1,10))
        time.sleep(0.1)
  1. Добавить сервис test-exporter в docker-compose.
  2. Запустить, проверить в VM количество уникальных серий:
    curl -s 'http://localhost:8428/api/v1/series/count' | jq
    
    Ожидать >50k серий (включить downsampling для уменьшения).
  3. (Опционально) Увеличить -search.maxUniqueTimeseries в VM, если лимит мал.

Ожидаемый результат этапа VM стабильно хранит миллионы временных рядов без деградации памяти (сравнить с Prometheus).


Этап 4: Оптимизация и downsampling (30 мин)

Действия

  1. Настроить downsampling (уже задано на Этапе 1, проверить):
    • --downsampling.period=5m:1m,1h:5m,30d:1h.
    • Проверить, что retention для raw данных короткий (например, 7d), а для агрегированных — дольше.
  2. Включить inverted index для ускорения запросов по лейблам:
    • --search.treatDotsAsRegexInRegexp=false (по умолчанию).
    • Рекомендуется проверить -search.maxTS=0 (неограниченно).
  3. Настроить ретention на уровне storageDataPath, убедиться, что данные не дублируются.
  4. Проверить компрессию:
    curl -s 'http://localhost:8428/metrics' | grep vm_allowed_metrics
    
    Посмотреть vm_rows_merged_total — показатель эффективности downsampling.
  5. Настроить vmagent на фильтрацию высококардинальных лейблов (если нужно — убрать user_id из некоторых метрик). Использовать relabel_configs.
  6. Создать Grafana dashboard:
    • График: llm_request_total с группировкой по user_id (top10).
    • График: количество уникальных prompt_hash за последние 5m.
    • График: latency query VM (из vm_request_duration_seconds).

Ожидаемый результат этапа Дашборд показывает метрики с high cardinality, запросы выполняются <1с.


Этап 5: Нагрузочное тестирование и проверка (30 мин)

Действия

  1. Установить vegeta (или hey).
  2. Направить нагрузку на test-exporter (/metrics), чтобы генерировать больше уникальных серий.
  3. Наблюдать в Grafana за memory usage VM (метрика process_resident_memory_bytes).
  4. Запустить запросы с фильтрацией по user_id:
    curl 'http://localhost:8428/api/v1/query?query=llm_request_total{user_id="user_12345"}'
    
    Убедиться, что ответ быстрый.
  5. Измерить query latency через vm_request_duration_seconds.
  6. Проверить compression ratio: vm_fs_data_size_bytes vs vm_rows_added_total.

Ожидаемый результат этапа VM выдерживает >10k уникальных user_id и >100k prompt_hash без OOM, query latency < 500ms.


5. Критерии приемки (Definition of Done)

  • VictoriaMetrics развёрнута (single-node), storageDataPath смонтирован.
  • Метрики llm_request_total с лейблами user_id (уникальных >10k) и prompt_hash (>100k) собираются и хранятся.
  • Дашборд Grafana отображает top-10 user_id по числу запросов.
  • Среднее время выполнения PromQL-запроса с фильтром по user_id < 1s.
  • Compressed ratio > 10x (сравнить raw size vs stored).
  • Retention настроен: raw data 7d, downsampled 30d.
  • Ни один запрос не приводит к timeout или error из-за high cardinality.
  • Настроен алерт на превышение vm_tsdb_unique_series свыше 80% от лимита (или динамический).

6. Ожидаемый результат

Основной артефакт Каталог vm-highcard/ с:

  • docker-compose.yml — полный compose с VM, vmagent, grafana, test-exporter.
  • test-exporter.py — скрипт генератора high-cardinality метрик.
  • vmagent.yml — конфиг агента с remote write.
  • grafana/dashboards/ — экспортированный JSON дашборда.

Содержимое дашборда графики количества уникальных user_id, prompt_hash, запросов в секунду, использования памяти, latency.

Дополнительный результат Скриншоты Grafana с метриками, демонстрирующие работу.


7. Возможные сложности и их решение

СложностьРешение
VM упирается в лимит -search.maxUniqueTimeseriesУвеличить флаг в docker-compose (например, -search.maxUniqueTimeseries=2000000)
vmagent не успевает отправлять данные (queue full)Увеличить capacity, добавить maxShards, уменьшить scrape_interval
Дашборд Grafana медленно грузится из-за большого числа серийИспользовать topk в PromQL, агрегировать по user_id в момент запроса
Downsampling не срабатывает для новых данныхЗадать корректные retention period для исходных и агрегированных данных (использовать -downsampling.period несколько раз)
Память в VM растёт быстроПроверить vmagent — включить streaming aggregation или уменьшить число лейблов через relabel_configs

8. Бюджет времени (оценка)

ЭтапВремя
Этап 1: Развёртывание VM30 мин
Этап 2: Настройка сбора метрик45 мин
Этап 3: Генерация high-cardinality30 мин
Этап 4: Оптимизация и дашборд30 мин
Этап 5: Нагрузочное тестирование30 мин
Итого2 ч 45 мин

Примечание В первый раз возможно увеличение на 30–60 мин из-за отладки параметров VM.


9. Связанные вопросы из базы знаний

ВопросТема
22High-cardinality metrics
45Prometheus remote write
58Downsampling стратегии
121VictoriaMetrics single vs cluster
203Настройка retention в TSDB
267Оптимизация inverted index
314Мониторинг LLM приложений
401vmagent vs prometheus agent
518Grafana переменные для high-cardinality
633Алертинг на cardinality

10. Чек-лист самопроверки

  • Я развернул VM и убедился, что она принимает метрики по /api/v1/write.
  • Я написал и запустил генератор с лейблами user_id (50k вариантов) и prompt_hash (100k+).
  • Я настроил vmagent на remote write и проверил, что серии появляются.
  • Я создал Grafana дашборд с двумя панелями: top-10 user_id и уникальные prompt_hash за 5 минут.
  • Я проверил, что запрос с фильтром {user_id="user_123"} выполняется быстрее 1 секунды.
  • Я измерил compressed ratio (raw bytes / stored bytes) — он больше 10.
  • Я настроил downsampling и retention, проверил, что старые raw данные удаляются.
  • Я задокументировал все изменения в файле README.md.