rsyslog + Elasticsearch: 50 серверов, централизованные логи, поиск за секунды
Elasticsearch 0.16 вышел свежо, мы пробуем прогнать syslog через rsyslog с JSON-выводом прямо в индекс. Grep по tar-архивам больше не единственный вариант.
Elasticsearch 0.16 вышел в 2011, появляются первые попытки строить централизованный поиск по логам на базе Lucene-движка
Когда серверов двадцать - grep по syslog терпим. Когда их пятьдесят, и инцидент случился в четыре утра, а логи раскиданы по /var/log на каждой машине плюс часть архивирована в tar.gz с ротацией - начинается настоящее удовольствие. Разбираем, что случилось, час-полтора, потому что половину времени тратим не на анализ, а на то, чтобы вообще найти нужную строчку.
Задача, которую мы поставили себе в марте: централизовать логи и сделать по ним полнотекстовый поиск. Не просто syslog-коллектор с записью в файл на центральный сервер - это не сильно лучше исходной ситуации. Нужен именно поиск: по ключевому слову, по хосту, по временному диапазону, за минуты, а не за час.
Elasticsearch на этом фоне выглядел интересно. Версия 0.16, REST API, схема без жёсткой декларации - то есть можно слать JSON-документы с произвольными полями, и движок сам разберётся с маппингом. Для логов это удобно: не нужно заранее объявлять, какие поля будут в syslog-сообщении от каждого приложения.
Цепочка
Схема, которую мы собрали, выглядит так:
rsyslog на каждом хосте
-> omfwd (UDP 514 или TCP) ->
rsyslog-коллектор (один сервер)
-> omelasticsearch (модуль) ->
Elasticsearch (localhost)
-> REST API / веб-интерфейс
rsyslog мы использовали уже везде - он стандартный в CentOS 6 и Debian 6. В нём появился модуль omelasticsearch, который умеет слать сообщения прямо в ES через HTTP. Это и есть ключевой кусок.
На центральном коллекторе конфиг rsyslog выглядит примерно так:
module(load="omelasticsearch")
template(name="es-json" type="list") {
constant(value="{")
constant(value="\"@timestamp\":\"") property(name="timereported" dateFormat="rfc3339")
constant(value="\",\"host\":\"") property(name="hostname")
constant(value="\",\"severity\":\"") property(name="syslogseverity-text")
constant(value="\",\"facility\":\"") property(name="syslogfacility-text")
constant(value="\",\"message\":\"") property(name="msg" format="json")
constant(value="\"}")
}
*.* action(
type="omelasticsearch"
server="localhost"
serverport="9200"
template="es-json"
searchIndex="syslog-%$year%-%$month%"
dynSearchIndex="on"
)
Индекс назван по месяцу (syslog-2011-04), чтобы старые данные можно было удалять без реиндексации. Это не архивный подход - просто разумная практика, когда объём неизвестен и диск не бесконечный.
Что работает хорошо
Поиск. REST API Elasticsearch принимает запросы в JSON, и даже простой curl с полнотекстовым запросом находит нужные строки по всем хостам за секунды. Найти все события с kernel: EXT3-fs error за последние трое суток - это один запрос с фильтром по @timestamp. Раньше это был цикл по пятидесяти хостам с ssh и grep, плюс разархивирование ротированных файлов там, где инцидент выходил за пределы текущего лога.
Поля из JSON. Если приложение пишет структурированные логи (у нас несколько самописных сервисов пишут JSON в syslog), Elasticsearch разбирает их автоматически и делает поля доступными для поиска. То есть можно искать не по подстроке, а по конкретному полю: user_id:12345 или status:500.
Нет агентов. rsyslog уже стоит на всех хостах, дополнительного процесса на каждой машине ставить не нужно. Только поправить конфиг и добавить пересылку на коллектор.
Что пока трудно
omelasticsearch в боевых условиях. Модуль новый. При перезапуске Elasticsearch или сети коллектор теряет сообщения - буферизации, которая бы пережидала недоступность ES и потом досылала, в текущей версии нет или она работает не так, как ожидаешь. Мы потеряли несколько минут логов при перезапуске ES для обновления конфига.
JSON-шаблоны - это ручная работа. Если в syslog-сообщении есть кавычки или обратный слеш, JSON ломается, и документ не индексируется. Флаг format="json" в rsyslog должен это эскейпить, но мы поймали несколько случаев, где оно вело себя не идеально. Скорее всего специфика конкретной версии модуля, но нервирует.
Elasticsearch и RAM. На выделенном сервере с 4 ГБ JVM ест половину с первых часов работы - это норма для Lucene, но сервер нужно выделять под это специально, не подселять.
Веб-интерфейс
Elasticsearch сам по себе - только API. Для просмотра логов через браузер мы прикрутили Kibana - это простой веб-фронтенд на JavaScript, который умеет строить запросы к ES и показывать результаты таблицей с пагинацией. Dashboards там пока очень простые, но искать по полям и временному диапазону удобно даже без настройки.
Где сейчас
Система работает на парке под управлением третью неделю. Инциденты, которые раньше разбирались час-полтора, теперь диагностируются в части «найти логи» за пять минут. Это ощутимо.
Остаётся вопрос надёжности пайплайна - потеря логов при недоступности ES неприятна, особенно если именно в этот момент что-то сломалось. Смотрим в сторону промежуточной очереди (в голове вертится идея про отдельный буферный файл на коллекторе как fallback), но это следующий шаг. Пока радуемся, что grep по пятидесяти хостам ушёл в прошлое.