Сравнение коллекторов логов Kubernetes: vlagent, Vector, Fluent Bit, OpenTelemetry Collector и другие
В VictoriaMetrics мы разработали vlagent — высокопроизводительный коллектор логов для VictoriaLogs. Чтобы проверить его производительность и корректность работы под нагрузкой, приближённой к реальной, мы создали набор бенчмарков и запустили его против 8 популярных коллекторов логов. В этой статье описаны методология, результаты по пропускной способности, потребление ресурсов и корректность доставки.
Участники тестирования:
-
VictoriaLogs Agent (vlagent) v1.48.0
-
Vector v0.53.0
-
Promtail v3.5.1
-
Grafana Alloy v1.13.2
-
Grafana Agent v0.44.2
-
Fluent Bit v4.2.3
-
OpenTelemetry Collector v0.146.1
-
Filebeat v9.3.1
-
Fluentd v1.19.1
Promtail и Grafana Agent — предшественники Grafana Alloy. Они включены в тест, чтобы наглядно показать разницу в производительности между поколениями.
Все конфигурации и исходный код бенчмарка опубликованы в открытом доступе, чтобы вы могли самостоятельно воспроизвести и проверить результаты.
Исходный код бенчмарка доступен по адресу: https://github.com/VictoriaMetrics/log-collectors-benchmark
Структура бенчмарка
Бенчмарк состоит из четырёх компонентов.
log-generator — программа, развёртываемая в нескольких Pod-ах, каждый из которых пишет JSON-записи логов в stdout с заданной частотой. Каждая запись содержит sequence_id (монотонно возрастающее целое число), временну́ю метку и случайно выбранное подмножество полей, типичных для структурированных логов в приложениях Kubernetes: уровень логирования, имя компонента, HTTP-метод, статус и т.д. Средний размер записи — около 216 байт.
{"_time":"...","_msg":"API rate limit exceeded","sequence_id":50,"level":"DEBUG","status_code":404}
{"_time":"...","_msg":"Payment transaction initiated","sequence_id":51,"level":"INFO","component":"user-service","method":"GET","status_code":500,"duration_ms":209,"user_id":"user_5111","bytes_sent":3836,"region":"ap-southeast-1"}
{"_time":"...","_msg":"Service health check passed","sequence_id":52,"level":"ERROR","component":"notification-service","method":"PUT","status_code":503,"duration_ms":920,"user_id":"user_9899","bytes_sent":5601,"error_type":"AuthenticationError","trace_id":"trace-a1b2c3d4"}
{"_time":"...","_msg":"User authentication completed","sequence_id":53,"component":"payment-service","status_code":201,"duration_ms":322,"user_id":"user_9454"}
Коллектор логов — тестируемая система. Читает логи Pod-ов из /var/log/pods или /var/log/containers, разбирает JSON-содержимое, сгенерированное log-generator, и отправляет записи в log-verifier по протоколу, специфичному для каждого коллектора: JSON Lines (vlagent, Vector, Fluent Bit, Fluentd), Loki (Alloy, Grafana Agent, Promtail), Elasticsearch (Filebeat), OpenTelemetry (OpenTelemetry Collector).
log-verifier — принимает логи от коллекторов, поддерживая все перечисленные выше протоколы. Для каждой пары «коллектор + Pod» он отслеживает максимальное наблюдаемое значение sequence_id и общее количество полученных записей. Поскольку sequence_id начинается с 1 и строго увеличивается на 1, любое расхождение между этими двумя значениями свидетельствует о потере логов. Также отслеживается задержка доставки — время между генерацией записи и её поступлением в log-verifier — в виде гистограммы. Все метрики экспортируются через Prometheus.
VictoriaMetrics + Grafana — собирают и визуализируют метрики CPU/памяти контейнеров и вывод log-verifier.
Окружение
Бенчмарк выполнялся на виртуальной машине Google Cloud n2-highcpu-32 (32 vCPU, 32 ГиБ ОЗУ, локальный SSD-диск), где все компоненты — коллекторы логов, log-generator, log-verifier, VictoriaMetrics, vmagent и Grafana — работали в однонодовом кластере Kubernetes на базе kind.
Запуск всех коллекторов на одной ноде с общим локальным SSD сделан намеренно. Локальный SSD обеспечивает достаточную пропускную способность записи для хранения логов, генерируемых log-generator. Файлы логов, считываемые коллекторами, с высокой вероятностью кешируются в page cache операционной системы, поэтому дисковый ввод-вывод на чтение не должен стать узким местом. Что касается CPU и ОЗУ: в продакшене коллекторы обычно разделяют ноду с другими рабочими нагрузками — одновременный запуск всех коллекторов имитирует эту конкуренцию. Загрузка CPU ноды не превышала 50% на протяжении всего бенчмарка, так что ресурсов хватало всем участникам для честного соревнования без того, чтобы сама нода стала узким местом.
Каждый коллектор был развёрнут через официальный Helm-чарт с одинаковыми ограничениями ресурсов:
-
Лимит CPU: 1 ядро
-
Лимит памяти: 1 ГиБ
Никакой дополнительной настройки производительности не применялось: ни нестандартных размеров буферов, ни размеров батчей, ни интервалов сброса, ни количества рабочих потоков, ни параметров среды выполнения вроде настроек GC. Все конфигурации основаны на значениях по умолчанию официальных Helm-чартов с минимальными изменениями, необходимыми для интеграции с тестовым окружением.
Пропускная способность
На графике ниже показана скорость приёма логов с течением времени по всем коллекторам, работающим одновременно, при 100 независимых Pod-ах log-generator, каждый из которых пишет в свой отдельный файл логов:
vlagent продолжает линейно масштабироваться по мере роста нагрузки, тогда как остальные коллекторы упираются в потолок ниже 40 тыс. записей/сек. Занявший второе место Fluent Bit выходит на плато на уровне 31,3 тыс. — в 4,5 раза меньше пикового показателя vlagent.
Максимальная пропускная способность каждого коллектора в сценарии со 100 Pod-ами:
| Коллектор | Макс. пропускная способность (записей/сек) | Отставание от лидера |
|---|---|---|
vlagent |
143 000 |
1.0x |
Fluent Bit |
31 300 |
4.5x |
Vector |
25 000 |
5.7x |
OpenTelemetry Collector |
20 500 |
6.9x |
Alloy |
15 700 |
9.1x |
Grafana Agent |
14 800 |
9.7x |
promtail |
13 400 |
10.6x |
Filebeat |
5 250 |
27.2x |
fluentd |
5 100 |
28.0x |
Сценарий со 100 Pod-ами выбран в качестве основного эталона, поскольку Kubernetes рекомендует максимум 110 Pod-ов на ноду, что делает 100 Pod-ов реалистичной верхней границей для одноузлового развёртывания. Так как цель бенчмарка — нагрузить систему до предела, именно этот сценарий наиболее показателен. Дополнительные снимки для 1, 50, 150 и 200 Pod-ов приведены в разделе Подробные результаты для сравнения, хотя различия между сценариями минимальны.
Потребление CPU
Для честного сравнения эффективности по CPU мы смотрим на потребление ресурсов при ~10 тыс. записей/сек (2 Pod-а × 5000 записей/сек) — при такой нагрузке почти все коллекторы ещё работают без потерь. Filebeat и fluentd исключены из этого сравнения, так как они уже теряли логи при данном уровне нагрузки.
| Коллектор | CPU при 10 тыс. записей/сек | Отставание от лидера |
|---|---|---|
vlagent |
0.062 |
1.0x |
Fluent Bit |
0.260 |
4.2x |
Vector |
0.412 |
6.6x |
OpenTelemetry Collector |
0.491 |
7.9x |
Grafana Agent |
0.552 |
8.9x |
Alloy |
0.578 |
9.3x |
promtail |
0.655 |
10.5x |
Полные детали смотрите в полном снимке Grafana.
На графике ниже показано потребление CPU за всё время теста по мере роста нагрузки от нуля до предела каждого коллектора:
Пиковое потребление памяти Fluent Bit и Filebeat под нагрузкой превышает лимит контейнера в 1 ГиБ, из-за чего OOM killer завершает их контейнеры. Это проявляется как разрывы на графике.
Потребление памяти
Память измеряется при той же точке ~10 тыс. записей/сек, где почти все коллекторы работают без потерь. Filebeat и fluentd исключены, так как они уже теряли логи при этом уровне нагрузки.
| Коллектор | Среднее потребление памяти при 10 тыс. записей/сек | Отставание от лидера |
|---|---|---|
vlagent |
27.91 МиБ |
1.0x |
promtail |
63.00 МиБ |
2.2x |
Alloy |
66.44 МиБ |
2.4x |
Grafana Agent |
72.49 МиБ |
2.6x |
Fluent Bit |
78.10 МиБ |
2.8x |
OpenTelemetry Collector |
106.83 МиБ |
3.8x |
Vector |
153.50 МиБ |
5.5x |
Полные детали смотрите в полном снимке Grafana.
На графике ниже показано потребление памяти за всё время теста по мере роста нагрузки от нуля до предела каждого коллектора:
Пиковое потребление памяти Fluent Bit и Filebeat под нагрузкой превышает лимит контейнера в 1 ГиБ, из-за чего OOM killer завершает их контейнеры. Это проявляется как разрывы на графике.
Подробные результаты
Снимки ниже содержат полный набор метрик для каждого сценария: частота потери логов, пропускная способность, потребление CPU и памяти, сетевой трафик, перезапуски контейнеров и троттлинг CPU.
| Pod-ов | Снимок |
|---|---|
1 |
Тестирование за пределами 200 Pod-ов не проводится, поскольку Kubernetes рекомендует максимум 110 Pod-ов на ноду.
Корректность доставки: неполные записи логов (Fluent Bit и Vector)
И Fluent Bit, и Vector могут отправлять неполные записи логов при ротации файлов логов контейнера. Корневая причина — в том, как containerd обрабатывает запись во время ротации: при высокой нагрузке containerd может записать только часть записи лога в текущий файл до ротации, а оставшуюся часть — уже в новый файл.
В результате появляются неправильно сформированные JSON-записи с отсутствующими полями. Если ваш пайплайн опирается на конкретные поля для маршрутизации, фильтрации или трансформации, такие записи вызовут проблемы — они доставляются, но повреждёнными.
Эта проблема закономерна, когда у коллектора не хватает CPU, чтобы успевать за потоком логов: он пропустит момент ротации файла containerd-ом и не заметит, что запись оказалась разделена между двумя файлами. Столкнуться с этим может любой коллектор, включая vlagent. Особенность Fluent Bit и Vector состоит в том, что они генерировали разделённые записи даже при низком потреблении CPU. В отличие от других коллекторов, они не склеивают фрагменты из двух файлов в одну запись — каждая часть пересылается как отдельная запись лога.
Мы сообщили об этом команде Fluent Bit: https://github.com/fluent/fluent-bit/issues/11602
Vector тоже формировал разделённые записи в наших тестах, но значительно реже — 2 против 34 у Fluent Bit. Оба числа получены за 1 час тестирования под одинаковой нагрузкой: 10 тыс. записей/сек с 2 Pod-ов. Мы проводили этот тест многократно на протяжении многих часов — ни один другой коллектор ни разу не сформировал повреждённую запись.
Мы сообщили об этом команде Vector: https://github.com/vectordotdev/vector/issues/24981
Полные детали смотрите в полном снимке Grafana.
Потеря логов из-за настройки glob_minimum_cooldown_ms в Vector
В Vector есть параметр glob_minimum_cooldown_ms, управляющий тем, как часто он повторно сканирует файловую систему в поиске новых файлов логов. Значение по умолчанию — 60 секунд: за это время логи, записанные в новый файл логов Pod-а, могут быть потеряны, пока Vector не обнаружит этот файл. Решение — установить glob_minimum_cooldown_ms в меньшее значение. В нашем бенчмарке мы использовали 10 секунд, что устранило проблему.
У Fluent Bit есть аналогичная настройка, но по умолчанию она равна 10 секундам, поэтому проблема не возникает сразу из коробки.
Накопление файловых дескрипторов в Vector под высокой нагрузкой
При высокой нагрузке Vector ставит файлы логов в очередь быстрее, чем обрабатывает их, из-за чего количество файловых дескрипторов бесконтрольно растёт. Поскольку ОС не может удалить файл, пока какой-либо процесс держит его открытым, ротированные файлы логов накапливаются на диске и могут занять всё свободное место на ноде.
Если нагрузка упадёт, Vector в конечном счёте догонит бэклог и доставит всё. Но если сначала закончится место на диске или Vector перезапустится во время накопления бэклога, файловые дескрипторы будут освобождены, файлы удалены — и эти логи пропадут безвозвратно.
У Vector есть параметр rotate_wait_secs, определяющий, сколько времени он держит открытыми файловые дескрипторы ротированного файла логов. Значение по умолчанию — неограниченно, то есть Vector будет удерживать ротированные файлы открытыми столько, сколько существует бэклог. Установка меньшего значения заставит Vector раньше освобождать дескрипторы ротированных файлов, но логи, которые ещё не были прочитаны, будут потеряны, если файлы удалятся до того, как Vector до них доберётся.
Другие коллекторы не удерживают открытые файловые дескрипторы для ротированных файлов, поэтому у них не накапливаются дескрипторы. Обратная сторона — в тех же условиях перегрузки они просто потеряют непрочитанные логи после ротации и удаления файлов.
Чтобы избежать потери логов в других коллекторах при высокой нагрузке:
-
Увеличьте размер файла логов перед ротацией — чем больше файл, тем больше времени есть у коллектора, чтобы прочитать его до ротации.
-
Выделите коллектору больше CPU и памяти — коллектор с запасом ресурсов будет успевать читать файлы логов даже во время пиков нагрузки.
-
Ограничьте шумные логи на уровне источника — приложения нередко тысячи раз в секунду выбрасывают одну и ту же ошибку. Такие логи лучше отбрасывать как можно раньше. Большинство коллекторов поддерживают ограничение частоты (rate limiting) или сэмплирование на уровне контейнера или потока логов. Это освобождает ресурсы коллектора для логов, которые действительно важны.
Потеря метаданных Pod-ов в Vector
Когда Pod удаляется, пока у Vector ещё есть бэклог его логов, Vector теряет метаданные этого Pod-а — метки, аннотации и другие атрибуты Kubernetes, прикреплённые к записям логов. Логи в итоге доставляются, но без метаданных, что может нарушить фильтрацию и маршрутизацию на следующих этапах пайплайна.
Итоги
При одинаковых ограничениях ресурсов (1 CPU, 1 ГиБ ОЗУ) и без какой-либо дополнительной настройки:
-
vlagent доставляет 143 тыс. записей/сек на пике — в 4,5 раза больше, чем занявший второе место Fluent Bit (31,3 тыс.).
-
При 10 тыс. записей/сек vlagent потребляет в 4,2 раза меньше CPU, чем Fluent Bit, и в 10,5 раза меньше, чем Promtail.
-
У vlagent наименьший объём используемой памяти — 27 МиБ при 10 тыс. записей/сек.
-
fluentd и Filebeat начинают терять логи ещё до достижения 10 тыс. записей/сек.
-
Fluent Bit и Vector могут формировать неполные записи логов при ротации файлов, что способно сломать пайплайны, опирающиеся на конкретные поля для маршрутизации или трансформации.
Ограничения vlagent
vlagent пока не поддерживает склейку многострочных логов (например, Java-стектрейсов) и разбор нестандартных форматов (например, логов доступа nginx). Если ваш пайплайн опирается на эти возможности, пока продолжайте использовать более гибкий коллектор. Мы работаем над этим — запланированная реализация будет основана на языке запросов LogsQL. Следите за обновлениями в этом запросе на добавление функциональности.
Когда стоит перейти на vlagent
Если вам нужен простой, быстрый и надёжный коллектор логов, рассмотрите переход на vlagent:
-
Автоматический разбор JSON-логов и системных логов Kubernetes без каких-либо настроек. Например, строка вида
I1025 00:15:15.525108 1 controller_utils.go:116] "Pod status updated"автоматически разбирается в структурированный JSON-объект. -
Автоматическое обнаружение и сбор логов со всех контейнеров в кластере Kubernetes. Helm-чарт victoria-logs-collector предоставляет готовую конфигурацию для Kubernetes с минимальными усилиями по настройке.
-
Фильтрация логов по меткам или аннотациям Pod-ов/нод с помощью LogsQL — отбрасывайте шумные или нерелевантные логи до того, как они попадут в бэкенд.
-
Буферизация на диске при недоступности бэкенда — логи помещаются в постоянную очередь и доставляются после восстановления соединения; никакого Kafka и других шин данных не нужно.
-
Одновременная репликация логов на несколько получателей с независимой дисковой буферизацией для каждого.
-
Без потерь логов — vlagent спроектирован с гарантией доставки каждой записи как минимум один раз.
По умолчанию vlagent отправляет собранные логи в VictoriaLogs, но также поддерживает Fluent Bit, Vector и ClickHouse в качестве получателей. Подробности смотрите в документации.
vlagent можно развернуть параллельно с существующими коллекторами логов. Смотрите документацию, чтобы начать.