Vector.dev: как упростить подсчет метрик по логам

Преобразование логов в метрики может быть утомительным

Преобразование логов в метрики может быть утомительным

Меня зовут Дима Синявский, я SRE-инженер в Ви.Tech — это IT-дочка ВсеИнструменты.ру. В этой статье я расскажу вам о нашем опыте работы с vector.dev, как превращать логи в метрики обычным способом и как это можно автоматизировать до такой степени, что вам понадобиться только yaml-developer.

Наш сайт создает более 100 тысяч заказов каждый день, и чтобы все это успешно работало у нас много сервисов, которые пишут много логов по ним часто нужно считать метрики.

Если у вас в эксплуатации уже есть коммерческие функциональностью подсчета логов по метрикам, то, думаю, ваши потребности им уже закрыты. Однако, тем кто строит свои системы с применением открытого ПО — это может быть полезно.

Предыстория

Vector.dev начали использовать за год до моего прихода в компанию. Когда я пришел, через него уже обрабатывались логи и метрики как-то уже считались по ним. Коллегами уже был сделан первый подход к облегчению работы с преобразованием логов в метики — часть конфигураций была шаблонизирована, некоторые параметры вынесены в yaml файлы, а генерация и развертывание выполнялись через ansible. Мой приход ознаменовался испытательным сроком, а значит и испытаниями.

Прохожу посвящение

Посвящение в подводники - поцеловать качающуюся кувалду

Посвящение в подводники — поцеловать качающуюся кувалду

У нас в компании Ви.Tech, так получилось, что испытание — это погружение в vector.dev. Это тот инструмент должны знать все SR-инженеры, потому что это наш основной сервис для обработки логов в компании. 

Моей первой задачей было обеспечить сбор метрик по логам для подсчета SLI/SLO. Итак, передо мной логи доступа http-сервера nginx в виде JSON. JSON это уже хорошо, так как это структурированные логи.

 Пример лога от Nginx в формате JSON

 Пример лога от Nginx в формате JSON

Осталось малость:

  • Изучить Vector Remap Language

  • Понять существующий код преобразования метрик в логи

  • Добавить обработчики логов для новых метрик

С первым я справился достаточно быстро, потому что у vector.dev хорошая документация и даже есть online VRL playground, где можно потренироваться работать с языком VRL без установки vector себе локально.

Прочитав наше руководство по добавлению метрик я попробовал на одном сервисе добавить по нему метрики из логов. 

Как мы добавляли подсчет метрик по логам раньше

 Посмотрел я наш код…

 Посмотрел я наш код…

Чтобы добавить подсчет метрики для одного сервиса нужно:

  1. Скопировать кусок из нашего кода, вставить, поправить. Пример куска для копирования — новый трансформ mysvc-accesslog-metrics-prepare

    Часть toml конфига трансформа mysvc-accesslog-metrics-prepare

    Часть toml конфига трансформа mysvc-accesslog-metrics-prepare

    Это такой фильтр по path и нужен для того, чтобы отобрать подходящие пути из URL path и только эти логи считать в метрику. При этом видно, что приемник «inputs» определенный и он не един для всех, его надо менять каждый раз.

  2. Найти и дописать где-то в эти файлы или новый создать. Ищем или существующий файл для сервиса. Если сервис добавляется впервые, то нужно кроме создания файла под работу с метриками, заранее еще проделать часть работы по настройке для него фильтров событий, но это показывать не будем, там еще на целый экран кода…

    Заносим код трансформа mysvc-accesslog-metrics-prepare в новый файл

    Заносим код трансформа mysvc-accesslog-metrics-prepare в новый файл

  3. Еще докинуть строчку в общий файл. Все метрики публикуются через экспортер Prometheus в эндпоинт /metrics. Трансформ access_log_to_metrics служит источником всех метрик для экспортера, потому нам нужно подключать в него новый источник метрик mysvc-accesslog-metrics-prepare. Забудешь добавить — метрик новых не будет.

    Указываем наш новый трансформ в список источников для экспорта метрик

    Указываем наш новый трансформ в список источников для экспорта метрик

  4. А еще и тесты добавить и отладить это все… и так для каждого сервиса.

В итоге мог уйти весь день на написание кода и отладку.

Мне предстояло сделать такое для десятка сервисов. 

Что было у меня в голове, когда я смотрел уже существующий код подсчета метрик для логов

Что было у меня в голове, когда я смотрел уже существующий код подсчета метрик для логов

Тяжко. Лениво. Что делать? Переделать!

Хватит пасты! Рефакторинг — переделываю подсчет метрик по логам запросов http-сервера. 

Что переделал

  1. Выделил нужные в метрике http-запросов метки. При изучении логов оказалось, что набор данных почти всегда одинаков по структуре, и нам для метрик из него надо лишь:

  1. Создал новый yml-файл описания метрик (у нас был и раньше подобный, но гораздо менее гибко настраиваемый). Часть файла представлена ниже

    Часть файла с описанием метрик и условиями подсчета - metrics-catalog.yml

    Часть файла с описанием метрик и условиями подсчета — metrics-catalog.yml

  2. Создал JSON-схему для упрощения написания yaml и его валидации. Если использовать Visual Studio Code, то вы получите выпадающие подсказки и проверку yml на соответствие схеме на лету. Часть ее представлена ниже

    json-schema для metrics-catalog.yml (часть)

    json-schema для metrics-catalog.yml (часть)

    В текущей версии схемы поддерживается только определение метрик типа счетчик (metric_type: counter) и гистограмма (metric_type: histogram), просто потому, что другие типы нам пока не были нужны.

  3. Создал новые обработчики (трансформы) для vector.dev с использованием шаблонов jinja. Часть кода с шаблонами ниже

    Часть кода шаблона jinja для создания конфигурации vector.dev для подсчета метрик по логам

    Часть кода шаблона jinja для создания конфигурации vector.dev для подсчета метрик по логам

  4. Добавил также и кодогенерацию тестов. Часть кода теста приведена ниже

    Часть кода шаблона jinja для создания тестов vector.dev к коду трансформов для подсчета метрик

    Часть кода шаблона jinja для создания тестов vector.dev к коду трансформов для подсчета метрик

Защищайтесь! — Взрыв кардинальности

Как ощущается взрыв кардинальности метрик

Как ощущается взрыв кардинальности метрик

Когда у вас слишком много значений в метке, например, если в path попадает url вида /products/product-name, где product-name может быть любым — это значит возможен взрыв кардинальности. У нас на сайте vseinstrumenti.ru более 1 миллиона товаров, это значит миллион уникальных product-name. Когда к нам приходит поисковый робот он обходит быстро эти страницы, тут мы и получаем взрыв кардинальности — на выходе метрик становится десятки тысяч строк, а объем ответа с эндпоинта /metrics может расти до десяток в сотен мегабайт. При этом коллектор метрик просто не успеет их собрать и вы перестанете получать метрики. 

Для решения этой проблемы мы ранее использовали отдельный файл с описаниями замен через regexp. Это было неудобно — приходилось дублировать условие совпадения path в двух местах: в файле описания условия отбора и файле описания замен path по regexp. После переделки стало удобнее. 

Теперь для таких случаев мы задаем условие по совпадению регулярного выражения (поле re в условии отбора логов в metrics-catalog.yml) и сразу указываем значение для замены в label_override — это то, что будет в метке path в итоговой метрике. 

Посмотрите на пример metrics-catalog.yml

Конфигурация фильтра metrics-catalog.yml для высококардинальных значений

Конфигурация фильтра metrics-catalog.yml для высококардинальных значений

В итоге мы увидим такую серию метрики типа counter
vi_http_requests_total{path=”/products/:name”, method=”GET”, status=”200”, cluster_name=”k8s_с1”, container_name=”site-app”, service_name=”site” } 

В этом случае все разнородные адреса в метке path будут заменены на /products/:name и вместо десятков тысяч строк будут лишь сотни, как мы ожидаем.

Идея для развития

Если присмотреться к нашим регулярным выражениям в label_override, то можно увидеть избыточность, а именно часть (?P(тут само выражение)). Так мы даем имя одной из групп регулярного выражения, но зачем?  

А затем, что у нас была идея автоматизировать установку значения label_override. Написать инструмент, который бы посмотрел в re: ^/product/(?P([^/]+-\\d+/))$, нашел имя name внутри (?P(тут само выражение)) и сконструировал бы значение вида /products/:name для label_override

Сейчас JSON-схема не требует обязательного наличия записи label_override. Возможно, нам стоит сделать его обязательным для блока re, чтобы не допускать возможности создания наборов значений с высокой кардинальностью для метки.

Дополнительная защита от взрыва кардинальности

Замена через label_override — это прекрасно, но иногда можно и проглядеть это или сразу не понять, что тут может быть слишком много значений для меток. Это значит риск взрыва кардинальности метрик сохраняется. 

Для защиты от этого vector.dev предлагает компонент tag_cardinality_limit. Он следит за количеством значений в метках и итоговым количеством метрик. Если кардинальность переваливает за заданный предел, то он может отбрасывать часть меток метрики или отбрасывать ее целиком. 

Мы используем этот компонент с такими настройками:

Настройки трансформа tag_cardinality_limit в vector.dev

Настройки трансформа tag_cardinality_limit в vector.dev

Режим probabilistic менее требователен к памяти, но может иногда пропускать немного больше метрик даже если предел по числу значений value_limit уже достигнут. Это происходит из-за вероятностного характера его работы. Но для нас это не существенно, так как нам лишь важно обнаружить такое поведение. 

Мы собираем внутренние метрики компонентов vector — это дает возможность мониторить его работу в нужных нам частях. В этом случае нам достаточно настроить алерт на рост метрики component_discarded_events_total для компонента с именем «metrics-cardinality-limiter», что позволит нам обратить внимание на проблему и перейти к устранению причины — найти высоко кардинальную метрику. Если вы используете Victoria Metrics для хранения метрик, как и мы, то можете проверить наличие высоко кардинальных метрик в Cardinality Explorer.

Как добавляем метрики теперь

  1. Определяем метрику и условия отбора логов в метрику в файле metrics-catalog.yml

    Пример условия отбора для логов в metrics-catalog.yml

    Пример условия отбора для логов в metrics-catalog.yml

    Кроме фильтров отбора метрик мы добавили еще поддержку исключения из подсчета. Например, нужно исключить из подсчета все запросы с method=«OPTION» (cм. в metrics-catalog секцию exclude_events).

  2. Генерируем новый конфиг и выкатываем через CI/CD

    Наш CI/CD для vector.dev

    Наш CI/CD для vector.dev

  3. Готово. Метрики уже считаются!

Что дала нам переделка подсчета метрик

  1. Экономию времени инженеров — вместо 3–5 часов кодинга на VRL с отладкой и борьбы с toml файлами, теперь достаточно 20–30 минут.

  2. Упрощение поддержки — теперь не нужно глубоко разбираться в vector, достаточно описать метрики в yml, т.е. достаточно yaml-девелопера.

На самом деле на этом мы не остановились. Позже, с изобретением нами Unified Log Pipeline, это решение было для него адаптировано и сделало его еще более гибким — оно теперь подходит для любых логов.

Как это может быть полезно вам

Вы можете ознакомиться с нашим открытым кодом к vector.dev для преобразования логов в метрики тут https://github.com/vseinstrumentiru/vector.dev-metrics-to-logs-helper.
В нем представлены:

  • базовый набор файлов для ansible и vector

  • metrics-catalog.yml

  • JSON-схема для metrics-catalog.yml

  • пример логов

  • Makefile для запуска задач

  • Readme как это все запустить

Используйте идеи из данного кода для реализации своего решения, адаптируйте его под себя.

Если появятся вопросы по коду или как его завести — прошу писать мне тут в личные сообщения или на почту. Актуальные контакты есть в https://github.com/r3code .

А если у вас есть вопросы по самому vector.dev, то предлагаю вам заглянуть в группу русскоговорящего сообщества Vector в Telegram https://t.me/vectordev_ru

© Habrahabr.ru