Привет. Меня зовут Григорий Рочев. В последнее время столкнулся по работе с Grafana agent. Начал разбираться с ним, как он работает под капотом и в чем его отличия от того, с чем я до этого работал (vmagent, netdata и prometheus). И снова на глаза попалась статья о том, нужен ли WAL для TSDB? И зародилась идея, рассмотреть подходы как агенты сбора метрик защищаются от потерь данных при работе с remote write протоколом на примере Grafana agent/Prometheus и VictoriaMetrics agent (vmagent)
Prometheus Agent и Grafana agent
Начнем издалека.
Чтобы разобраться, с базовыми понятия в Prometheus, такими как Metrics, Labels, Time Series, Scraping, советую заглянуть сюда.
Сэмплы (Samples) хранятся в сжатых юнитах, называемыми «чанками» (chunk). Когда сэмплы собираются Prometheus, они сохраняются в память, после чего, для фиксации сэмплов в чанках, делается запись в журнал упреждающей записи (WAL). По сути это запись в page cache и каждую минуту делается вызов fdatasync() для записи на диск содержимого значений page cache WAL. WAL необходим для того, чтобы можно было восстановить данные в память из него, даже если машина внезапно выйдет из строя. Но нужно учитывать, что в случае выхода из строя сервера (виртуальной машины), все записи, которые не успели записаться на диск - будут утеряны.
Как только чанк заполняется до 120 сэмплов или достигает chunkRange (по умолчанию составляет 2 часа), создается новый чанк, а старый чанк считается «заполненным».
Начиная с Prometheus v2.19.0, все чанки не хранятся в памяти. Как только создается новый чанк, заполненный чанк сбрасывается на диск и отображается в память с диска, при этом в памяти сохраняется только ссылка (вызов mmap). Что позволяет уменьшить потребление памяти на сервере.
Если интересно разобраться как это работает более подробно, советую посмотреть данное выступление (картинка была взята оттуда же).
![](https://habrastorage.org/getpro/habr/upload_files/310/933/509/310933509de92ed6ed0df7bfb671808d.png)
Так как Prometheus не разрабатывался в long-term storage (долгосрочного хранения), поэтому если нужен хранить метрики на долгий период времени, нужно собранные метрики как-то доставить до данного стораджа. Есть разные способы достичь этого, но в данной статья я буду говорить только про remote write способ. Если интересно более подробно прочитать, что такое remote write, советую посмотреть/почитать о нем здесь.
В Prometheus для каждого remote write создается in-memory очередь, в которую записываются samples из журнала (WAL). Данные в очереди хранятся в течение двух часов, и это поведение не изменить. Начиная с Prometheus v2.8.0, данные отправляются на удаленный сторадж сразу же, как будет сделана запись в WAL. В случае, если по какой-то причине удаленный сторадж будет недоступен, Prometheus будет ожидать до тех пор, пока не появится связанность с удаленных стораджем, чтобы начать отправку неотправленных данных. Но есть ограничение, те же злополучные 2 часа, если за это время удаленный хост не будет доступен - данные потеряются.
А начиная с Prometheus v2.32.0 появляется возможность использовать фичу agent mode. Это по-сути тюнинг Prometheus для remote write - отключая такие вещи как использование локального стораджа для хранения time series, поиск, алертинг. Его основная задача собирать и отправлять сэмплы.
Вот так мы приходим к Grafana Agent. По сути своей Prometheus agent mode - это как grafana agent работает со сбором и отправкой метрик (с некоторыми нюансами). Так как именно Grafana была инициатором и контрибьютором данной фичи. Все ограничения описанные выше при работе с метриками - также относятся и к grafana agent.
Вообще, grafana agent - это такой комбайн, который умеет собирать и метрики, и логи, и трейсы. Но в данной статье сконцентрируемся только на метриках.
Соответственно, чтобы не потерять данные, при падении экземпляра Grafana Agent/Prometheus, нужно иметь как минимум по два экземпляра на одни и теже таргеты.
А что насчет шардирования? Да оно есть, вот как предлагается сделать это в Prometheus operator , т.е. администратору/devops/sre/инженеру (нужное подчеркнуть), необходимо вручную определиться, по каким лейблам будут распредяляться шарды.
Grafana agent же предлагает три варианта горизонтального масштабирования: Host Filtering, Hashmod sharding и Scraping service.
Scraping service (он же Instance sharing) - это тот же вид шардирования, что и в Prometheus operator.
Hashmode sharding - сегментирование происходит с помощью пары правил hashmod/keep relabel. Эти правила будут хешировать адрес таргетов и объединять его по модулю с количеством запущенных сегментов агента. Но важно, он не консистентный алгоритм хэширования. Что означает, что изменение количества шардов может привести к перемещению любого количества таргетов на новый шард, вплоть до 100%. И при переходе на новый сегмент любые существующие данные в WAL со старого шарда будут отброшены.
Host Filtering - просто daemonset’ом запускаем экземпляр на каждой машине, и только на этой машине осуществляется сбор таргетов. Идея интересная. На сайте графаны из минусов пишут, что может таким способом нагрузить kube-apiserver и remote write storage. Но я вижу еще два минуса: нужно проставить priorityClassName, чтобы кубелет ненароком не прибил столь важный под на ноде и в случае если таки grafana agent по какой-то причине рестартанет/упадет, он не сможет собирать метрики с данной ноды пока агент не поднимется, не перечитает WAL и сохранит данные в память. В этотмомент мы не будем знать о сотоянии ноды и на графиках у нас будут "пропуски". Как долго он будет подниматься, зависит от количества time series.
VictoriaMetrics Agent (vmagent)
Важным отличием vmagent от Grafana Agent/Prometheus - отсутствие WAL. Почему разработчики VictoriaMetrics считают, что для современных time series database он не нужен можно прочитать здесь.
А как в итоге реализована персистентность данных, например для недоступности remote write storage или если процесс vmagent рестартанет? Для этого в vmagent есть PersistentQueue (file-based buffer size for unsent data). Сэмплы которые собираются с таргетов сохраняются в чанки, а те в очередь в памяти. После чего периодически сохраняются данные на диск, также периодически происходит корректировка слишком больших чанков. В случае если после падения сервера или процесса будут битые чанки - vmagent пропустит такие чанки и это не помешает стартовать процессу и работать в штатном режиме.
Т.е. как и в случае с Grafana Agent/Prometheus, при некорректном завершении работы сервера, vmagent тоже может потерять данные - так как не сразу происходит fsync данных на диск. Но, при этом, за счет того, что при старте vmagent нет необходимости перечитывать WAL - это позволяет vmagent’у стартовать быстрее и использовать меньше памяти.
А что насчет шардирования? Оно есть и работает в кластерном режиме vmagent, распределяя нагрузку между всеми экземплярами vmagent автоматически, без необходимости вмешательства администратора. Можно также разбивать по scraping service как это реализовано в Grafana Agent/Prometheus.
Если интересно узнать больше о vmagent, советую почитать/посмотреть данный доклад.
Заключение
В данной статье, лишь прошелся по тем опенсорсным решениям для работы с remote write протоколом поддерживающим API Prometheus, с которыми работал. Хотел бы указать на то, что наличие WAL - не гарантирует, что ваши данные при скрейпе не будут потеряны. Это стоит учитывать в работе и при выборе агента. Вообще, еще множество таких приложений, например, те же telegraf, netdata или vector.io, которые умеют работать с данным протоколом.