Fluent bit что это
Fluent bit что это
Содержание
Архитектура и принцип действия
Fluent Bit собирает, парсит и фильтрует сообщения из различных источников ввода и сохраняет их в хранилище (в памяти или файловой системе). После успешного сохранения, сообщения поступают из хранилища на вход в маршрутизатор, который определяет в какой выход оно будет отправлено.
С точки зрения производительности Fluent Bit имеет лучшие показатели в производительности и использовании ОЗУ. Исследование AWS показало что Fluent Bit в
5 раз меньше утлизировал процессор и память чем Fluentd.
Плагины ввода
Позволяют получать сообщения из различных источников. К основным можно отнести:
Обработка сообщения
Под обработкой сообщения подразумевается парсинг и фильтрация/модификация сообщений. К основным можно отнести:
Хранение сообщений
Хранение сообщений позволяет решить классическую проблему «Fast publisher, slow subscriber». С помощью хранилища организуется «обратное давление» на источник сообщений, вплоть до его полной остановки. Всего реализовано 2 типа хранилищ: в памяти и файловой системе.
Плагины вывода
Позволяют отправлять сообщения в различные источники. К основным можно отнести:
Установка в kubernetes
Существует несколько подходов к cluster-level журналированию в kubernetes. Fluent Bit можно применить в каждом из них, а так же он может быть использован совместно с Fluentd. Для упрощения примера далее будет использован подход журналирования на уровне сервера.
Подготовка манифестов и установка
Daemon set лучше всего подходит для журналирования на уровне сервера. Kubernetes сам запустит Fluent Bit на новых узлах.
Манифест основан на репозитории fluent/fluent-bit-kubernetes-logging. В нём дополнительно монтируется хост директория /run/log для интеграции с systemd-journald :
Конфигурация описывает одну цепочку обработки логов. Стандартный вывод docker.service дополняется мета-информацией из kubernetes api server и записываются в файл /var/log/k8s.log :
Установка и применение изменений производится с помощью команды kubectl apply :
После этого можно убедиться в успехе установки:
Выводы
Fluent Bit обладает схожей с rsyslog функциональностью. Различные плагины ввода и вывода позволяют встраивать его в различные схемы cluster level журналирования.
Разработка Fluent Bit ведется с пониманием того, что с высокой долей вероятности сервис будет запущен в контейнеризированном окружении. За счет этого интеграция в kubernetes состоит из нескольких шагов.
Пишем логи из Container Optimized Image в Yandex Cloud Logging при помощи Fluent Bit
В прошлом посте я рассказал, как доставить логи из systemd. Теперь давайте разберёмся, как доставлять логи контейнеризированного приложения.
Шаг 1. Пишем логи
Ну что ж, опять начнём с того, что набросаем демоприложение на питоне, которое будет для нас генерировать записи логов: logs.py.
В принципе, всё просто и я постарался оставить комментарии, но я пройдусь ещё раз по основным пунктам.
Логи Docker-контейнера — это просто вывод STDOUT и STDERR. В приложении я решил не делить вывод по двум этим потокам, так как далее в парсере Fluent Bit у нас будет возможность распарсить строчку лога и вычленить оттуда уровень, с которым была сделана запись.
Формат строчки лога я постарался не перегружать, вывел лишь базовые параметры типа кода ответа и id запроса.
Шаг 2. Настраиваем развёртывание COI VM
Так как нам нужно развернуть как минимум два контейнера (контейнер с нашим приложением и логер-агент Fluent Bit), воспользуемся возможностью COI работать со спецификацией Docker Compose. Опишем в файле наши контейнеры. Репозиторий с моим образом не публичный — используйте свой. Например, так как в spec.yml.
В контейнер Fluent Bit дополнительно передаём переменную окружения YC_GROUP_ID. Она понадобится нам для настройки плагина yc-logging, скажет ему, куда писать наши логи, и содержит id группы логирования.
Далее нам понадобится принести на ВМ конфиги. Сделаем это по инструкции, только сейчас всё проще и KMS нам не понадобится.
Шаг 3. Настраиваем чтение логов из контейнера
Теперь подробнее о том, зачем нам нужна каждая часть конфига user-data.yaml, представленного выше. В секции SERVICE указаны настройки самого приложения Fluent Bit (например, период, с которым оно отправляет логи). Подробнее можно прочитать в документации.
В секции INPUT описано, откуда и как забирать логи.
Для работы с логами в формате Fluentd и Fluent Bit нужно использовать инпут с типом forward. О других типах инпутов читайте в документации. А также у меня есть инструкция, как работать с systemd инпутом Fluent Bit.
Тут вроде всё понятно: Fluent Bit слушает логи на порту 24224. Именно туда мы велели Docker Compose отправлять логи нашего приложения в соответствующем конфиге выше.
Также в том конфиге мы сказали драйверу помечать все записи тегом app.logs. Именно на него мы можем ориентироваться, настраивая процессинг логов.
Для этого в файле parsers.conf мы опишем парсер.
Мы воспользуемся парсером regex. Для его конфигурирования зададим регулярное выражение, при помощи которого разберём строчки лога. Из каждой строки мы извлечём поля: req_id, в которое клали уникальный id запроса, severity — уровень логирования, code — HTTP-код ответа, text — весь остальной текст.
Теперь с помощью парсера преобразуем логи. Для этого в основном конфиге добавим секцию FILTER.
Тут мы говорим, что ищем только логи, тег которых совпадает с app.logs, берём из записи поле log, применяем к нему наш парсер app_log_parser, а все остальные поля лога сохраняем (Reserve_Data On).
Шаг 4. Отгружаем в Cloud Logging
Ну вот мы и подошли к отправке логов в облако. Так как мы разворачивали контейнер Fluent Bit из образа, куда уже добавлен плагин Yandex Cloud Logging, нужно лишь сконфигурировать секцию OUTPUT.
Для отправки логов нужна авторизация. Мы воспользуемся самым простым способом: привяжем к ВМ сервисный аккаунт, которому выдадим роль, необходимую для записи логов logging.writer, или выше.
Отлично, теперь создадим ВМ с этими конфигами.
Нам понадобится id image, из которого мы будем разворачивать ВМ:
Дальше подставим его в команду создания ВМ:
Когда ВМ развернётся и контейнеры на ней запустятся, можно перейти в Cloud Logging и посмотреть логи:
Если кликнуть на иконку глаза, можно увидеть дополнительные залогированные параметры:
По ним можно пофильтровать. Например, запрос json_payload.code >= 400 найдёт все строчки логов, связанные с ответами, содержавшими ошибки.
Также можно найти все сообщения, относившиеся к обработке одного запроса, если пофильтровать по json_payload.req_id.
Подробнее о языке запросов можно прочитать в документации. Весь код из этой статьи на GitHub.
Парсинг логов при помощи Fluent-bit
Не так давно передо мной встала задача организации логгирования сервисов, разворачиваемых с помощью docker контейнеров. В интернете нашел примеры простого логгирования контейнеров, однако хотелось большего. Изучив возможности Fluent-bit я собрал рабочий пайплайн трансформации логов. Что в сочетании с Elasticsearch и Kibana, позволило быстро искать и анализировать лог-сообщения.
Цель туториала: организовать логгирование docker контейнеров. Также необходимо структурировать записи логов, и обеспечить поиск и фильтрацию по их полям.
Кому интересно, добро пожаловать под кат)
Необходимы базовые знания bash, docker-compose, Elasticsearch и Kibana.
Обзор используемого стека
Тестовое приложение будем запускать с помощью docker-compose.
Для организации логгирования воспользуемся следующими технологиями:
На Хабре есть обзор стеков технологий, используемых для логгирования контейнеров. Прежде чем идти дальше предварительно можно с ней ознакомиться.
Подготовка тестового приложения
Для примера организуем логгирование веб-сервера Nginx.
Подготовка Nginx
Создадим директорию с проектом и добавим в нее docker-compose.yml, в котором будем задавать конфигурацию запуска контейнеров приложения.
Определим формат логов Nginx. Для этого создадим директорию nginx c файлом nginx.conf. В нем переопределим стандартный формат логов:
Добавим сервис web в docker-compose.yml:
Подготовка fluent-bit
Для начала организуем самый простой вариант логгирования. Создадим директорию fluent-bit c конфигурационным файлом fluent-bit.conf. Про формат и схему конфигурационного файла можно прочитать здесь.
Fluent-bit предоставляет большое количество плагинов для сбора лог-сообщений из различных источников. Полный список можно найти здесь. В нашем примере мы будем использовать плагин forward.
Плагин вывода stdout позволяет перенаправить лог-сообщения в стандартный вывод (standard output).
Добавим в docker-compose.yml сервис fluent-bit:
Добавим настройки логгирования для сервиса web:
Запустим тестовое приложение:
Сгенерируем лог-сообщение, откроем еще одну вкладку терминала и выполним команду:
Получим лог-сообщение в следующем формате:
Сообщение состоит из:
временной метки, добавляемой fluent-bit;
мета данных, добавляемых драйвером fluentd.
На этом подготовительный этап можно считать завершенным. На текущем этапе структура проекта выглядит следующим образом:
Кратко о маршрутизации лог-сообщиний в fluent-bit
Маршрутизация в fluent-bit позволяет направлять лог-сообщения через различные фильтры, для их преобразования, и в конечном итоге в один или несколько выходных интерфейсов. Для организации маршрутизации используется две основные концепции:
Выглядит все следующим образом:
Входной интерфейс присваивает лог-сообщению заданные тег.
В настройках фильтра или выходного интерфейса обязательно необходимо указать правило сопостовления, которое определяет выполнять обработку данного лог-сообщения или нет.
Подробнее можно прочитать в официальной документации.
Очистка лог-сообщений от мета данных.
Мета данные для нас не представляют интерес, и только загромождают лог сообщение. Давайте удалим их. Для этого воспользуемся фильтром record_modifier. Зададим его настройки в файле fluent-bit.conf:
Теперь лог-сообщение имеет вид:
Отделение логов запросов от логов ошибок
На текущий момент логи посылаемые Nginx можно разделить на две категории:
логи с предупреждениями, ошибками;
Давайте разделим логи на две группы и будем структурировать только логи запросов. Все логи-сообщения от Nginx помечаются тегом nginx.logs. Поменяем тег для лог-сообщений запросов на nginx.access. Для их идентификации мы заблаговременно добавили в начало сообщения префикс access_log.
Добавим новый фильтр rewrite_tag. Ниже приведена его конфигурация.
Теперь все лог-сообщения запросов будут помечены тегом nginx.access, что в будущем позволит нам выполнять фильтрацию логов описанным выше категориям.
Парсинг лог-сообщения
Давайте структурируем наше лог-сообщение. Для придания структуры лог-сообщению его необходимо распарсить. Это делается с помощью фильтра parser.
Лог-сообщение представляет собой строку. Воспользуемся парсером regex, который позволяет с помощью регулярных выражений определить пары ключ-значение для информации содержащейся в лог-сообщении. Зададим настройки парсера. Для этого в директории fluent-bit создадим файл parsers.conf и добавим в него следующее:
Обновим конфигурационный файл fluent-bit.conf. Подключим к нему файл с конфигурацией парсера и добавим фильтр parser.
Теперь необходимо добавить файл parsers.conf в контейнер, сделаем это путем добавления еще одного volume к сервису fluent-bit:
Перезапустим приложение, сгенерируем лог-сообщение запроса. Теперь оно имеет следующую структуру:
Сохранение лог-сообщений в elasticsearch
Теперь организуем отправку лог-сообщений на хранения в elasticsearch.
Добавим два выходных интерфейса в конфигурацию fluent-bit, один для лог-сообщений запросов, другой для лог-сообщений ошибок. Для этого воспользуемся плагином es.
Добавим в docker-compose.yml сервисы elasticsearch и kibana.
На текущем этапе структура проекта выглядит следующим образом:
Финальную версию проекта можно найти в репозитории.
Результаты
Благодаря структурированию лог-сообщений мы можем фильтровать их по различным полям, к примеру:
показать только лог-сообщения запросов;
показать лог-сообщения запросов с http статусом 404;
отображать не все поля лог-сообщения.
Пример фильтрации логов. Выполнена фильтрация по значению поля «status», так же выбраны только необходимые в данный момент поля.
Всем спасибо! Надеюсь туториал был полезен.
Fluentd: почему важно настроить выходной буфер
В наше время невозможно представить проект на базе Kubernetes без стека ELK, с помощью которого сохраняются логи как приложений, так и системных компонентов кластера. В своей практике мы используем стек EFK с Fluentd вместо Logstash.
Fluentd — это современный универсальный коллектор логов, набирающий всё большую популярность и присоединившийся к Cloud Native Computing Foundation, из-за чего вектор его разработки ориентирован на использование совместно с Kubernetes.
Факт использования Fluentd вместо Logstash не изменяет общую суть программного комплекса, однако, для Fluentd характерны свои специфические нюансы, следующие из его многофункциональности.
Например, начав использовать EFK в нагруженном проекте с высокой интенсивностью записи логов, мы столкнулись с тем, что в Kibana некоторые сообщения отображаются повторно по несколько раз. В данной статье мы расскажем вам, по какой причине происходит данное явление и как решить проблему.
Проблема дублирования документов
В наших проектах Fluentd развернут как DaemonSet (автоматически запускается в одном экземпляре на каждом узле кластера Kubernetes) и отслеживает stdout логи контейнеров в /var/log/containers. После сбора и обработки логи в виде JSON-документов поступают в ElasticSearch, поднятый в кластерном либо standalone виде, в зависимости от масштабов проекта и требований к производительности и отказоустойчивости. В качестве графического интерфейса используется Kibana.
При использовании Fluentd с буферизирующим плагином на выходе мы столкнулись с ситуацией, когда некоторые документы в ElasticSearch имеют полностью одинаковое содержание и отличаются лишь идентификатором. Убедиться, что это именно повтор сообщения можно на примере лога Nginx. В файле лога данное сообщение существует в единственном экземпляре:
Однако, в ElasticSearch существует несколько документов, содержащих данное сообщение:
Притом, повторов может быть больше двух.
Во время фиксации данной проблемы в логах Fluentd можно наблюдать большое количество предупреждений следующего содержания:
Данные предупреждения возникают когда ElasticSearch не может вернуть ответ на запрос за установленное параметром request_timeout время, из-за чего пересылаемый фрагмент буфера не может быть очищен. После этого Fluentd пытается отправить фрагмент буфера в ElasticSearch повторно и через произвольное число попыток операция завершается успешно:
Однако, ElasticSearch воспринимает каждый из пересланных фрагментов буфера как уникальный и присваивает им уникальные значения полей _id при индексации. Таким образом и появляются копии сообщений.
В Kibana это выглядит так:
Решение проблемы
Существует несколько вариантов решения данной проблемы. Один из них — встроенный в плагин fluent-plugin-elasticsearch механизм генерации уникального хеша для каждого документа. Если использовать данный механизм, ElasticSearch будет распознавать повторы на стадии пересылки и не допускать дублирования документов. Но нельзя не учитывать, что данный способ решения проблемы борется со следствием и не устраняет ошибку с нехваткой тайм-аута, поэтому мы отказались от его применения.
Мы используем буферизирующий плагин на выходе Fluentd, чтобы не допустить потери логов в случае кратковременных неполадок с сетью или возросшей интенсивности записи логов. Если по какой-либо причине ElasticSearch не может мгновенно записать документ в индекс, документ попадает в очередь, которая хранится на диске. Поэтому, в нашем случае, чтобы устранить источник проблемы, которая приводит к возникновению описанной выше ошибки, необходимо задать корректные значения параметров буферизации, при которых выходной буфер Fluentd будет достаточного объема и при этом успевать очищаться за отведенное время.
Стоит отметить, что значения параметров, речь о которых пойдет ниже, индивидуальны в каждом конкретном случае использования буферизации в выходных плагинах, так как зависят от множества факторов: интенсивности записи в лог сообщений сервисами, производительности дисковой системы, загруженности сетевого канала и его пропускной способности. Поэтому, чтобы получить подходящие для каждого отдельного случая, но не избыточные настройки буфера, избегая длительного перебора вслепую, можно воспользоваться отладочной информацией, которую пишет в свой лог Fluentd в процессе работы и сравнительно быстро получить корректные значения.
На момент фиксации проблемы конфигурация выглядела следующим образом:
В ходе решения проблемы вручную подбирались значения следующих параметров:
chunk_limit_size — размер чанков, на которые разбиваются сообщения в буфере.
Общий размер буфера можно вычислить, перемножив параметры queue_limit_length и chunk_limit_size, что можно толковать как «максимальное количество чанков в очереди, каждый из которых имеет заданный объем». При недостаточном размере буфера в логах будет появляться следующее предупреждение:
Оно означает, что буфер не успевает очиститься за отведенное время и данные, которые поступают в заполненный буфер, блокируются, что приведет к потере части логов.
Увеличить буфер можно двумя способами: увеличив либо размер каждого чанка в очереди, либо количество чанков, которые могут находиться в очереди.
Если установить размер чанка chunk_limit_size более 32 мегабайт, то ElasticSeacrh не примет его, так как входящий пакет получится слишком большим. Поэтому, если необходимо увеличить буфер дополнительно, лучше увеличивать максимальную длину очереди queue_limit_length.
Когда буфер перестанет переполняться и останется только сообщение о нехватке тайм-аута, можно приступить к увеличению параметра request_timeout. Однако, при установке значения больше 20 секунд, в логах Fluentd начнут появляться следующие предупреждения:
Данное сообщение никак не влияет на работу системы и означает, что время очистки буфера заняло больше, чем установлено параметром slow_flush_log_threshold. Это отладочная информация и мы используем её при подборе значения параметра request_timeout.
Обобщенный алгоритм подбора выглядит следующим образом:
Итоговые значения данных параметров, как было замечено ранее, получаются индивидуальными для каждого случая. Следуя приведенному выше алгоритму, мы гарантированно устраняем ошибку, приводящую к повтору сообщений.
В таблице ниже показано, как изменяется количество ошибок за сутки, приводящих к дублированию сообщений, в процессе подбора значений описанных выше параметров:
node-1 | node-2 | node-3 | node-4 | |
---|---|---|---|---|
До/После | До/После | До/После | До/После | |
failed to flush the buffer | 1749/2 | 694/2 | 47/0 | 1121/2 |
retry succeeded | 410/2 | 205/1 | 24/0 | 241/2 |
Стоит дополнительно отметить, что полученные настройки могут потерять свою актуальность в процессе роста проекта и, соответственно, увеличения количества логов. Первичным признаком нехватки установленного тайм-аута является возвращение в лог Fluentd сообщений о долгой очистке буфера, то есть превышение порога slow_flush_log_threshold. С этого момента есть ещё небольшой запас до превышения параметра request_timeout, поэтому необходимо своевременно отреагировать на данные сообщения и повторно выполнить процесс подбора оптимальных настроек, описанный выше.
Заключение
Тонкая настройка выходного буфера Fluentd является одним из главных этапов настройки EFK стека, определяющим стабильность его работы и корректность размещения документов в индексах. Ориентируясь на описанный алгоритм настройки, можно быть уверенным, что все логи будут записаны в индекс ElasticSearch в правильном порядке, без повторений и потерь.
Log Processor & Forwarder
Collect > Parse > Filter > Deliver
It has been released on Nov 19, 2021, check out the Release Notes, read the Updated Documentation or jump directly to the Downloads Section.
Fluent Bit is an open source Log Processor and Forwarder which allows you to collect any data like metrics and logs from different sources, enrich them with filters and send them to multiple destinations. It’s the preferred choice for containerized environments like Kubernetes.
Fluent Bit is designed with performance in mind: high throughput with low CPU and Memory usage. It’s written in C language and has a pluggable architecture supporting more than 70 extensions for inputs, filters and outputs.
Fluent Bit is a CNCF (Cloud Native Computing Foundation) subproject under the umbrella of Fluentd.
We are part of a wide community, no vendor lock-in.
Highlights
It has been designed as a lightweight solution with high performance in mind. From a design perspective, it’s fully asynchronous (event-driven) and take the most of the operating systems API for performance and reliability.
All inputs, filters and outputs features are implemented through the plugins interface. Extend the features with C, Lua (filters) or Golang (outputs).
Measuring is important. Fluent Bit comes with native plugins to gather metrics from your CPU, Memory, Disk I/O and Network usage on Linux systems. In addition, it can receive metrics from external services like Statsd and Collectd.
Features
Event Driven
Fluent Bit as a service is fully event-driven, it only use asynchronous operations to collect and deliver data.
Flexible Routing
The data that comes in the pipeline, can be routed to multiple places using custom routing rules. Ship your data to multiple places with zero-copy strategy.
Configuration
It configuration is very simple and human-readable, it allow to specify how it will behave, which features to enable and how Routing is performed. Optionally Fluent Bit can run from the command line without it.
I/O Handler
The Input/Output layer provides an abstraction that allow to perform read/write operations in an asynchronous way.
Upstream Manager
When dealing with upstream backends like remote network services, handling TCP connections can be challenging. The Upstream manager simplify the connection process and take care of timeout/network exceptions and Keepalive states.
Security & TLS
When delivering data to destinations, output connectors inherit full TLS capabilities in an abstracted way. Add your certificates as required.
Fluent Bit is an open source Log Processor and Forwarder. It’s a CNCF subproject under the umbrella of Fluentd