Настроить 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 + PromQL | Docker-образ grafana/grafana |
| Генератор тестовых метрик с high-cardinality | Собственный Python-скрипт (см. Этап 3) |
| Нагрузочный тест (опционально) | vegeta или hey для симуляции запросов |
Если нет реального Prometheus — симулируем:
- Развернуть
prom/prometheusсnode_exporterи собственным test-exporter, который генерирует метрикуllm_request_totalс лейбламиuser_id(от 1000 до 50000) иprompt_hash(от 10000 до 100000). Код экспортёра — Python Flask-приложение. - Запустить генерацию в течение 5–10 минут, чтобы
prometheusначал страдать от cardinality (можно отследить spike памяти). - Затем переключить
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 мин)
Действия
- Создать директорию проекта
vm-highcard. - Написать
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).
- Запустить
docker-compose up -d. - Проверить работоспособность:
curl http://localhost:8428/metrics # метрики самой VM curl http://localhost:8428/api/v1/query?query=up
Ожидаемый результат этапа VictoriaMetrics доступна, Grafana запущена, можно заходить admin:admin.
Этап 2: Настройка сбора метрик (45 мин)
Действия
- Поднять
prometheusв том же docker-compose (optional — для теста remote write), либо сразу настроитьvmagentкак агент. - Написать конфиг для
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 # опционально: настройка очереди, ретраев - Запустить
vmagent(добавить сервис в docker-compose). - Убедиться, что данные пишутся в VM:
Ответ должен содержатьcurl 'http://localhost:8428/api/v1/query?query=llm_request_total'data.resultс разными лейблами.
Ожидаемый результат этапа Метрики от симулятора появляются в VM.
Этап 3: Генерация high-cardinality метрик (30 мин)
Действия
- Написать
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)
- Добавить сервис
test-exporterв docker-compose. - Запустить, проверить в VM количество уникальных серий:
Ожидать >50k серий (включить downsampling для уменьшения).curl -s 'http://localhost:8428/api/v1/series/count' | jq - (Опционально) Увеличить
-search.maxUniqueTimeseriesв VM, если лимит мал.
Ожидаемый результат этапа VM стабильно хранит миллионы временных рядов без деградации памяти (сравнить с Prometheus).
Этап 4: Оптимизация и downsampling (30 мин)
Действия
- Настроить downsampling (уже задано на Этапе 1, проверить):
--downsampling.period=5m:1m,1h:5m,30d:1h.- Проверить, что retention для raw данных короткий (например, 7d), а для агрегированных — дольше.
- Включить inverted index для ускорения запросов по лейблам:
--search.treatDotsAsRegexInRegexp=false(по умолчанию).- Рекомендуется проверить
-search.maxTS=0(неограниченно).
- Настроить ретention на уровне
storageDataPath, убедиться, что данные не дублируются. - Проверить компрессию:
Посмотретьcurl -s 'http://localhost:8428/metrics' | grep vm_allowed_metricsvm_rows_merged_total— показатель эффективности downsampling. - Настроить
vmagentна фильтрацию высококардинальных лейблов (если нужно — убратьuser_idиз некоторых метрик). Использоватьrelabel_configs. - Создать Grafana dashboard:
Ожидаемый результат этапа Дашборд показывает метрики с high cardinality, запросы выполняются <1с.
Этап 5: Нагрузочное тестирование и проверка (30 мин)
Действия
- Установить
vegeta(илиhey). - Направить нагрузку на
test-exporter(/metrics), чтобы генерировать больше уникальных серий. - Наблюдать в Grafana за memory usage VM (метрика
process_resident_memory_bytes). - Запустить запросы с фильтрацией по user_id:
Убедиться, что ответ быстрый.curl 'http://localhost:8428/api/v1/query?query=llm_request_total{user_id="user_12345"}' - Измерить query latency через
vm_request_duration_seconds. - Проверить compression ratio:
vm_fs_data_size_bytesvsvm_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: Развёртывание VM | 30 мин |
| Этап 2: Настройка сбора метрик | 45 мин |
| Этап 3: Генерация high-cardinality | 30 мин |
| Этап 4: Оптимизация и дашборд | 30 мин |
| Этап 5: Нагрузочное тестирование | 30 мин |
| Итого | 2 ч 45 мин |
Примечание В первый раз возможно увеличение на 30–60 мин из-за отладки параметров VM.
9. Связанные вопросы из базы знаний
| Вопрос | Тема |
|---|---|
| 22 | High-cardinality metrics |
| 45 | Prometheus remote write |
| 58 | Downsampling стратегии |
| 121 | VictoriaMetrics single vs cluster |
| 203 | Настройка retention в TSDB |
| 267 | Оптимизация inverted index |
| 314 | Мониторинг LLM приложений |
| 401 | vmagent vs prometheus agent |
| 518 | Grafana переменные для 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.