Любому системному администратору в своей повседневной деятельности приходится иметь дело со сбором и анализом логов. Собранные логи нужно хранить — они могут понадобиться для самых разных целей: для отладки программ, для разбора инцидентов, в качестве подспорья для службы техподдержки и т.п. Кроме того, необходимо обеспечить возможность поиска по всему массиву данных.
Организация сбора и анализа логов — дело не такое простое, как может показаться на первый взгляд. Начнём с того, что приходится агрегировать логи разных систем, которые между собой могут не иметь вообще ничего общего. Собранные данные также очень желательно привязать к единой временной шкале, чтобы отслеживать связи между событиями. Реализация поиска по логам представляет собой отдельную и сложную проблему.
В течение последних нескольких лет появились интересные программные инструменты позволяющие решать описанные выше проблемы. Всё большую популярность обретают решения, позволяющие хранить и обрабатывать логи онлайн: Splunk,Loggly, Papertrail, Logentries и другие. В числе несомненных плюсов этих сервисов следует назвать удобный интерфейс и низкую стоимость использования (да и в рамках базовых бесплатных тарифов они предоставляют весьма неплохие возможности). Но по при работе с большими количествами логов они зачастую не справляются с возлагаемыми на них задачами. Кроме того, их использование для работы с большими количествами информации нередко оказывается невыгодным с чисто финансовой точки зрения.
Гораздо более предпочтительным вариантом является развёртывание самостоятельного решения. Мы задумались над этим вопросом, когда перед нами встала необходимось собирать и анализировать логи облачного хранилища.
Мы начали искать подходящее решение и остановили свой выбор на Fluentd — небезынтересном инструменте с достаточно широкой функциональностью, о котором почти нет подробных публикаций на русском языке. О возможностях Fluentd мы подробно расскажем в этой статье.
Общая информация
Fluend был разработан Садаюки Фурухаси, сооснователем компании Treasure Data(она является одним из спонсоров проекта), в 2011 году. Он написан на Ruby. Fluentd активно развивается и совершенствуется (см. репозиторий на GitHub, где обновления стабильно появляются раз в несколько дней).
В числе пользователей Fluentd — такие известные компании, как Nintendo, Amazon, Slideshare и другие.
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 (какие данные и откуда брать, каким критериям они должны соответствовать, куда их переправлять) прописываются в конфигурационном файле /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 — поделитесь опытом в комментариях.
Если вы по тем или иным причинам не может оставлять комментарии здесь — добро пожаловать в наш блог.
Автор: AndreiYemelianov