Консолидация логов Storm (Storm — Rsyslog — Logstash — Elasticsearch)

storm-logsТут коротко описаны мои изыскания касательно сбора информацией из текстовых логов компонентов кластера Twitter Storm средствами Rsyslog с последующей передачей оной через Logstash в базу Elasticsearch c возможностью анализировать её через Kibana.

Отмазка

Данная статья не является точной инструкцией, следуя которой можно получить заранее известный результат. То что получилось «завести» у автора, возможно, в силу различных обстоятельств (отличия версий ПО), не получится у читателей. Цель статьи: описать идею, коротко — её реализацию, чуть подробнее — моменты, вызвавших некие трудности у автора. В общем, не удивляйтесь, что про Elasticsearch практически ничего в статье нет.

Проблема. Формулировка задачи.

Имеем вычислительный кластер CentOS 6 серверов на основе Storm (v.0.9.2), установленный из пакетов acromusashi / storm-installer. Состоящий из:

  • Сервера с установленным на него Нимбусом
  • Трёх серверов с супервизорами

Проблема заключается в том, что по умолчанию компоненты Twitter Storm пишут свои логи в файлы на тех серверах, на которых установлены. И это совсем неудобно. Хотелось бы работать с логами в режиме «одно окно» и, желательно, с красивым графическим интерфейсом.

Краткое описание технического решения

storm-logsИзображено (сверху вниз):

  • Сервер с Супервизором (тут два)
    • Службы Storm пишут свои логи в один файл (aggregate.log)
    • rsyslog посредством модуля imfile читает aggregate.log и пересылает его содержимое на сервер с Нимбусом
  • Сервер с Нимбусом
    • rsyslog:
      • получает логи с внешних серверов на local6, пишет их в storm.log — лог-файл всего кластера
      • читает через модуль imfile локальный лог Storm’а в local5, пишет его в общий storm.log
    • logstash:
      • вычитывает содержимое storm.log
      • отправляет его на внешний сервер с elasticsearch
  • Сервер с Логами

Пояснения:

  • «Лить» ли логи из приложения напрямую на сборщик логов на Нимбусе или вначале собирать локально? Решено вначале складывать логи локально, это позволит совсем не остаться без логов, если в какой-то момент связь с rsyslog на сборщике будет нарушена — логи не потеряются.
  • Записи логов многострочные (исключения Java), где-то после перевода строки есть отступ, где-то нет. Как с этим быть? Решено использовать в качестве разделителя отдельных записей логов пустую строку. С пустой строкой, как разделителем, хорошо работает logback, imfile модуль rsyslog’а и multiline фильтр logstash’a.

Конфигурация Storm

В рассматриваемой версии Storm используется logback в качестве системы логирования. И её конфигурацию можно обнаружить в файле:

/opt/storm-0.9.2incubating/logback/cluster.xml

Перенаправим все потоки логов в один файл, для этого опишем новый appender:

добавляем только что описанный appender в корневой элемент:

Пояснения:

  • %n%n — мы хотим, чтобы каждую запись в логе отделяла от другой пустая строка, поэтому в конце добавляем лишний перевод строки (%n). Подобный разделитель потребуется нам для того, чтобы в дальнейшем Rsyslog мог связать между собой строки одной записи лога.
  • %xEx — явно указав исключение (exception) в шаблоне, мы гарантируем, что оно отобразится в логах именно так, как нам надо, а именно: между сообщением об ошибке (%m) и исключением (%xEx) будет ровно один перевод строки — исключение будет на следующей строке после сообщения об ошибке. В противном случае logback отобразит исключение как отдельную запись, отделив её от остальных пустыми строками.

Подробнее про шаблоны logback’а тут.

Конфигурация Rsyslog

Для импорта логов Storm’а в rsyslog используется модуль imfile. Флаг (escapeLF) для отмены экранирования переводов строк и пр. табов в этом модуле стал доступен только с версии Rsyslog 7+. В CentOS 6 же доступен только Rsyslog 5.8, потому поставили последнюю стабильную версию (8.4) из репозитария вендора.

Для простоты будем считать, что модифицируются файлы:

/etc/rsyslog.conf

В ходе отладки целесообразно запустить Rsyslog-сервер в консоли:

На серверах с супервизорами

Добавим в конец файла следующие строки:

Пояснения:

  • escapeLF=»off» — отключаем маппирование переводов строк в #012 и пр., ибо хотим работать с многострочными логами
  • Facility=»local6″ — определяем источник, из которого будем принимать логи Storm’а
  • ReadMode=»1″ — инструктируем Rsyslog считать всё что находится между двумя пустыми строками одной записью лога
  • string=»<%pri%> %msg%\n\n» — тут определяем формат строк, которые полетят на сборщик логов (сервер с Нимбусом)
    • %pri% — тут подставится магическая цифра, благодаря которой внешний Rsyslog-сервер узнает про установленный нами local6
    • %msg% —  строка лога Storm’а именно в том формате, в каком мы его определили ранее в файле cluster.xml
    • \n\n — добавляем лишний перевод строки, чтобы между соседними записями лога была пустая строка. Подобный разделитель потребуется нам для того, чтобы в дальнейшем Logstash мог связать между собой строки одной записи лога.

На сервере с нимбусом

В первую очередь раскомментируем строки, разрешающие приём логов с внешних Rsyslog-серверов

 

Направляем логи с внешних серверов в общий файл логов для Storm-кластера посредством local6:

Направляем логи от локальных сервисов Storm в общий файл логов для Storm-кластера посредством local5:

Пояснения:

  • local5, local6 — поскольку к логам с внешних серверов необходимо применить шаблон («%msg:2:$%\n») отличный от шаблона для логов из локального файла («%msg%\n»), пришлось воспользоваться двумя источниками:
    • local5 — источник для локального файла
    • local6 — источник для внешних серверов
  • «%msg:2:$%\n» — по сложно объяснимым на пальцах причинам при поступлении записей логов с внешнего сервера строке «%msg%» соответствует исходная запись лога плюс лишний пробел вначале. Собственно предложенной конструкцией мы этот пробел обрезаем.

Конфигурация Logstash

Logstash устанавливается и запускается на том же сервере, куда собираются все логи Rsyslog’а. В нашем случае это сервер с Nimbus’ом.

Конфигурация Logstash

/etc/logstash/conf.d/nimbus.conf

Самая сложная часть в этом файле — конфигурация grok-фильтра, который анализирует строку лога и разбивает её на составные части.

К примеру, указанным выше образом сконфигурированный сервис преобразует строку вида:

перед отправкой в Elasticsearch во что-то подобное:

Отдельно стоит пояснить фильтр multiline. Настройки

инструктируют Logstash считать всё, что идёт после пустой строки и до следующей, одной записью лога.

Для отладки работы Logstash можно сначала запустить сервер из консоли:

Конфигурация Elasticsearch

Тут требуется минимальная конфигурация, а именно: убедится в существовании индекса эквивалентного, указанному в настройках Logstashlogs.

Если у нас где-то есть развёрнутая Kibana, в итоге получаем все логи кластера в «одном окне»

Storm логи в Kibana

Замечания

  • В ходе изысканий SELinux был выключен, ибо препятствовал чтению файла модулем imfile rsyslog’а

Полезные ссылки