Сбор и анализ логов с Fluentd

fluentd

Любому системному администратору в своей повседневной деятельности приходится иметь дело со сбором и анализом логов. Собранные логи нужно хранить — они могут понадобиться для самых разных целей: для отладки программ, для разбора инцидентов, в качестве подспорья для службы техподдержки и т.п. Кроме того, необходимо обеспечить возможность поиска по всему массиву данных.

Организация сбора и анализа логов — дело не такое простое, как может показаться на первый взгляд. Начнём с того, что приходится агрегировать логи разных систем, которые между собой могут не иметь вообще ничего общего. Собранные данные также очень желательно привязать к единой временной шкале, чтобы отслеживать связи между событиями. Реализация поиска по логам представляет собой отдельную и сложную проблему.

В течение последних нескольких лет появились интересные программные инструменты позволяющие решать описанные выше проблемы. Всё большую популярность обретают решения, позволяющие хранить и обрабатывать логи онлайн: Splunk,Loggly, Papertrail, Logentries и другие. В числе несомненных плюсов этих сервисов следует назвать удобный интерфейс и низкую стоимость использования (да и в рамках базовых бесплатных тарифов они предоставляют весьма неплохие возможности). Но по при работе с большими количествами логов они зачастую не справляются с возлагаемыми на них задачами. Кроме того, их использование для работы с большими количествами информации нередко оказывается невыгодным с чисто финансовой точки зрения.

Гораздо более предпочтительным вариантом является развёртывание самостоятельного решения. Мы задумались над этим вопросом, когда перед нами встала необходимось собирать и анализировать логи облачного хранилища.

Мы начали искать подходящее решение и остановили свой выбор на Fluentd — небезынтересном инструменте с достаточно широкой функциональностью, о котором почти нет подробных публикаций на русском языке. О возможностях Fluentd мы подробно расскажем в этой статье.

Общая информация

Fluend был разработан Садаюки Фурухаси, сооснователем компании Treasure Data(она является одним из спонсоров проекта), в 2011 году. Он написан на Ruby. Fluentd активно развивается и совершенствуется (см. репозиторий на GitHub, где обновления стабильно появляются раз в несколько дней).

В числе пользователей Fluentd — такие известные компании, как Nintendo, Amazon, Slideshare и другие.
Fluentd собирает логи из различных источников и передаёт их другим приложениям для дальнейшей обработки. В схематичном виде процесс сбора и анализа логов с помощью Fluentd можно представить так:

Fluentd

В качестве основных преимуществ Fluentd можно выделить следующие:

  • Низкие требования к системным ресурсам. Для нормальной работы Fluentd вполне достаточно 30 — 40 Мб оперативной памяти; скорость обработки при этом составляет 13 000 событий в секунду.
  • Использование унифицированного формата логгирования. Данные, полученные из разных источников, Fluentd переводит в формат JSON. Это помогает решить проблему сбора логов из различных систем и открывает широкие возможности для интеграции с другими программными решениями.
  • Удобная архитектура. Архитектура Fluentd позволяет расширять имеющийся набор функций с помощью многочисленных плагинов (на сегодняшний день их создано более 300). С помощью плагинов можно подключать новые источники данных и выводить данные в различных форматах.
  • Возможность интеграции с различными языками программирования. Fluentd может принимать логи из приложений на Python, Ruby, PHP, Perl, Node.JS, Java, Scala.

Fluentd распространяется бесплатно под лицензией Apache 2.0. Проект достаточно подробно документирован; на официальном сайте и в блоге опубликовано немало полезных обучающих материалов.

Установка

В рамках этой статьи мы описываем процедуру установки для ОС Ubuntu 14.04. С инструкциями по установке для других операционных систем можно ознакомиться здесь.

Установка и первоначальная настройка Fluentd осуществляются с помощью специального скрипта. Выполним команды:

$ wget http://packages.treasuredata.com/2/ubuntu/trusty/pool/contrib/t/td-agent/td-agent_2.0.4-0_amd64.deb

$ sudo dpkg -i td-agent_2.0.4-0_amd64.deb

По завершении установки запустим Fluentd:

$ /etc/init.d/td-agent restart

Конфигурирование

Принцип работы Fluentd заключается в следующем: он собирает данные из различных источников, проверяет на соответствие определённым критериям, а далее переправляет в указанные локации для хранения и дальнейшей обработки. Наглядно всё это можно представить в виде следующей схемы:

Fluentd

Настройки fluentd (какие данные и откуда брать, каким критериям они должны соответствовать, куда их переправлять) прописываются в конфигурационном файле /etc/td-agent/td-agent.conf, который строится из следующих блоков:

  • source — содержит информацию об источнике данных;
  • match — содержит информацию о том, куда нужно передавать полученные данные;
  • include — содержит информацию о типах файлов;
  • system — содержит настройки системы.

Рассмотрим структуру и содержание этих блоков более подробно.

Source: откуда брать данные

В блоке source содержится информация о том, откуда нужно брать данные. Fluentd может принимать данные из различных источников: это логи приложений на различных языках программирования (Python, PHP, Ruby, Scala, Go, Perl, Java), логи баз данных, логи с различных аппаратных устройств, данные утилит мониторинга…. С полным списком возможных источников данных можно ознакомиться здесь. Для подключение источников используются специализированные плагины.
В число стандартных плагинов вхoдят http (используется для приёма HTTP-сообщений) и forward (используется для приёма TCP-пакетов). Можно использовать оба этих плагина одновременно.
Пример:

# Принимаем события с порта 24224/tcp
<source>
  type forward
  port 24224
</source>

# http://this.host:9880/myapp.access?json={"event":"data"}
<source>
  type http
  port 9880
</source>

Как видно из приведённого примера, тип плагина указывается в директиве type, а номер порта — в директиве port.
Количество источников данных ничем не ограничено. Каждый источник данных описывается в отдельном блоке <source>.
Все события, принятые из источников, передаются маршрутизатору сообщений. Каждое событие имеет три атрибута: tag, time и record. На основании атрибута tag принимается решение о том, куда должны быть перенаправлены события (подробнее об этом пойдёт речь ниже). В атрибуте time указывается время (это делается автоматически), а в атрибуте record — данные в формате JSON.
Приведём пример описания события:

# generated by http://this.host:9880/myapp.access?json={"event":"data"}
tag: myapp.access
time: (current time)
record: {"event":"data"}

Match: что делать с данными

В секции Match указывается, по каком признаку будут отбираться события для последующей обработки. Для этого используются специализированные плагины.

К стандартным плагинам вывода относятся match и forward:

#  Получаем события с порта 24224
<source>
  type forward
  port 24224
</source>

# http://this.host:9880/myapp.access?json={"event":"data"}
<source>
  type http
  port 9880
</source>

#Берём события, помеченные тэгами "myapp.access" 
#и сохраняем их в файле/var/log/fluent/access.%Y-%m-%d
#данные можно разбивать на порции с помощью опции time_slice_format.

<match myapp.access>
  type file
  path /var/log/fluent/access
</match>

В приведённом фрагменте указано, что все события, отмеченные тэгами myapp и access, нужно сохранять в файле, путь к которому указывается в директиве path. Обратим внимание на то, что события, которые помимо тэгов myapp и access отмечены ещё и другими тэгами, в файл отправлены не будут.
Кратко рассмотрим особенности синтаксиса директивы match:

  • символ * означает соответствие любой части тэга (если указать <match a.*>, то a.b будет соответствовать заданному условию, а a.b.c — нет);
  • ** означает соответствие любому тэгу (если указать <match **>, то заданному условию будут соответствовать и a, и a.b., и a.b.c);
  • {x, y, z} означает соответствие по крайней мере одному из тэгов, указанных в фигурных скобках (если указать <match {a, b}>, то а и b будут соответствовать заданному условию, а с — нет);
  • фигурные скобки можно использоовать в сочетании с символами * и **, например: <match {a, b}. c.*> или <match {a.*, b}.c>*;
  • <match a b> означает соответствие тэгам a и b одновременно;
  • <match a.** b.*> означает соответствие тэгам a, a.b и a.b.c (первая часть) и b.d (вторая часть).

Fluentd проверяет события на соответствие тэгам в том порядке, в котором блоки match следуют друг за другом в конфигурационном файле. Сначала указываются соответствия частного характера, а затем — более общие соответствия. Если это правило нарушено, Fluentd корректно работать не будет. Так, фрагмент вида

<match **>
  type blackhole_plugin
</match>

<match myapp.access>
  type file
  path /var/log/fluent/access
</match>

содержит ошибку: сначала в нём указаны предельно общие совпадения (<match **> означает, что в файл нужно записывать события, отмеченные любым тэгом), а затем — частные. Допущенная ошибка приведёт к тому, что события с тэгами myapp и access записываться вообще не будут. Чтобы все работало так, как надо, фрагмент должен выглядеть так:

<match myapp.access>
  type file
  path /var/log/fluent/access
</match>

<match **>
  type blackhole_plugin
</match>

Include: объединяем конфигурационные файлы

Директивы можно импортировать из одного конфигурационного файла в другой и объединять. Эта операция осуществляется в блоке include:

include config.d/*.conf

В этой директиве можно указывать путь к одному или нескольким файлам с указанием маски или URL:

# абсолютный путь к файлу
include /path/to/config.conf

# можно указывать и относительный путь
include extra.conf

# маска
include config.d/*.conf

# http
include http://example.com/fluent.conf

System: устанавливаем дополнительные настройки

В блоке System можно установить дополнительные настройки, например, задать уровень логгирования (подробнее см. здесь), включить ии отключить опцию удаления повторяющихся записей из логов и т.п.

Поддерживаемые типы данных

Каждый плагин для Fluentd обладает определённым набором параметров. Каждый параметр в свою очередь ассоциируется с определенным типом данных.
Приведём список типов данных, которые поддерживаются в Fluentd:

  • string — строка;
  • integer — целое число;
  • float — число с плавающей точкой;
  • size — число байт; возможны следующие варианты записи:
    • <целое число>k — размер в килобайтах;
    • <целое число>g — размер в гигабайтах;
    • <целое число>t — размер терабайтах;
    • если никакой единицы измерения не указано, то значение в поле size будет воспринято как число байт.
  • time — время; возможны следующие варианты записи:
    • <целое число>s — время в секундах;
    • <целое число>m — время в минутах;
    • <целое число>h — время в часах;
    • <целое число>d — время в днях;
    • если никакой единицы измерения не указано, что значение в поле time будет воспринято как количество секунд.
  • array — массив JSON;
  • hash — объект JSON.

Плагины Fluentd: расширяем возможности

В Fluentd используется 5 типов плагинов: плагины вывода, плагины ввода, плагины буферизации, плагины форм и плагины парсинга.

Плагины ввода

Плагины ввода используются для получения догов из внешних источников. Обычно такой плагин создает потоковый сокет (thread socket) и прослушивающий сокет (listen socket). Можно также настроить плагин так, что он будет получен данные из внешнего источника с определённой периодичностью.
К плагинам ввода относятся:

  • in_forward — прослушивает TCP-сокет;
  • in_http — принимает сообщения, передаваемые в POST-запросах;
  • in_tail — считывает сообщения, записанные в последних строках текстовых файлов (работает так же, как команда tail -F);
  • in_exec — с помощью этого плагина можно запускать стороннюю программу и получать её лог событий; поддерживаются форматы JSON, TSV и MessagePack;
  • in_syslog — с помощью этого плагина можно принимать сообщения в формате syslog по протоколу UDP;
  • in_scribe — позволяет получает сообщения в формате Scribe (Scribe — это тоже коллектор логов, разработанный Facebook).

Плагины вывода

Плагины вывода делятся на три группы:

  • плагины без буферизации — не сохраняют результаты в буфере и моментально пишут их в стандартный вывод;
  • с буферизацией — делят события на группы и записывают поочередно; можно устанавливать лимиты на количество хранимых событий, а также на количество событий, помещаемых в очередь;
  • с разделением по временным интервалам — эти плагины по сути являются разновидностью плагинов с буферизацией, только события делятся на группы по временным интервалам.

К плагинам без буферизации относятся:

  • out_copy — копирует события в указанное место (или несколько мест);
  • out_null — этот плагин просто выбрасывает пакеты;
  • out_roundrobin — записывает события в различные локации вывода, которые выбираются методом кругового перебора;
  • out_stdout — моментально записывает события в стандартный вывод (или в файл лога, если он запущен в режиме демона).

В число плагинов с буферизацией входят:

  • out_exec_filter — запускает внешнюю программу и считывает событие из её вывода;
  • out_forward — передаёт события на другие узлы fluentd;
  • out_mongo (или out_mongo_replset) — передаёт записи в БД MongoDB.

Плагины буферизации

Плагины буферизации используются в качестве вспомогательных для плагинов вывода, использующих буфер. К этой группе плагинов относятся:

  • buf_memory — изменяет объём памяти, используемой для хранения буферизованных данных (подробнее см. в официальной документации);
  • buf_file — даёт возможность хранить содержимое буфера в файле на жёстком диске.

Более подробно о том, как работают плагины буферизации, можно прочитать здесь.

Плагины форматирования

С помощью плагинов форматирования можно изменять формат данных, полученных из логов. К стандартным плагинам этой группы относятся:

  • out_file — позволяет кастомизировать данные, представленные в виде «время — тэг — запись в формате json»;
  • json — убирает из записи в формате json поле «время» или «тэг»;
  • ltsv — преобразует запись в формат LTSV;
  • single_value — выводит значение одного поля вместо целой записи;
  • csv — выводит запись в формате CSV/TSV.

Более подробно о плагинах форматирования можно прочитать здесь.

Плагины парсинга

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

Естественно, что все плагины fluentd в рамках обзорной статьи описать невозможно — это тема для отдельной публикации. С полным списком всех существующих на сегодняшний день плагинов можно ознакомиться на этой странице.

Общие параметры для всех плагинов

Для всех плагинов указываются также следующие параметры:

  • type — тип;
  • id — идентификационный номер;
  • log_level — уровень логгирования (см. выше).

На официальном сайте fluentd выложены готовые конфигурационные файлы, адаптированные под различные сценарии использования (см. здесь).

Вывод данных и интеграция с другими решениями

Собранные с помощью Fluentd могут быть переданы для хранения и дальнейшей обработки в базы данных (MySQL, PostgreSQL, CouchBase, CouchDB, MongoDB, OpenTSDB, InfluxDB) распределенные файловые системы (HDFS — см. также статью об интеграции Fluentd c Hadoop) облачные сервисы (AWS, Google BigQuery), поисковые инструменты (Elasticsearch, Splunk, Loggly).
Для обеспечения возможности поиска и визуализации данных довольно часто используется комбинация Fluentd+Elasticsearc+Kibana (подробную инструкцию по установке и настройке см., например, здесь).

Полный список сервисов и инструментов, которым Fluentd может передавать данные, размещён здесь. На официальном сайте опубликованы также инструкции по использованию Fluentd в связке с другими решениями.

Заключение

В этой статье мы представили обзор возможностей сборщика логов Fluentd, которым мы пользуемся для решения собственных задач. Если вы заинтересовались и у вас возникло желание попробовать Fluentd на практике — мы подготовили роль Ansible, которая поможет упростить процесс установки и развёртывания.

С проблемой сбора и анализа логов, наверное, сталкивались многие из вас. Было бы интересно узнать, какими инструментами вы пользуетесь для её решения и почему вы выбрали именно их. А если вы используете Fluentd — поделитесь опытом в комментариях.