Введение в ELK: собираем, фильтруем и анализируем большие данные
Внимание! Статье уже четыре года и многое в используемых технологиях могло измениться. Не забываем консультироваться с офф. документацией.
Что, если я скажу вам, что логи могут быть не только полезными и содержать тонну важной информации, но и что работа с ними может быть классной, интересной и увлекательной? Настолько же увлекательной, интересной и классной, как кликанье через удобный интерфейс в браузере, позволяющий в считанные секунды вывести любой график и сравнить его с другим графиком? Да чего там: что, если я скажу вам, что после прочтения этой статьи вы сможете развернуть полноценный аналог Google Analytics для ваших логов? А я это и скажу. Точнее, я это уже сказал. Погнали!
Подготовим vagrant-коробку
Прежде чем перейдём к самой сути, нам необходимо провести небольшую подготовительную работу. Возможно, вы уже использовали Vagrant. Если нет – обязательно узнайте что это такое и начните использовать. Знание Vagrant не нужно для этой статьи, но будет классно (и позволит избежать возможных несоответствий в результате), если вы будете запускать примеры кода ниже используя такой же как у меня конфиг vagrant-бокса.
Итак, создаём новую папку, выполняем в ней vagrant init
, открываем Vagrantfile
и помещаем туда этот конфиг:
# Vagrantfile API/syntax version. Don't touch unless you know what you're doing!
VAGRANTFILE_API_VERSION = "2"
Vagrant.configure(VAGRANTFILE_API_VERSION) do |config|
config.vm.box = "hashicorp/precise64"
config.ssh.forward_agent = true
config.vm.network "forwarded_port", guest: 9200, host: 9200
config.vm.network "forwarded_port", guest: 9292, host: 9292
config.vm.network "forwarded_port", guest: 5601, host: 5601
end
Затем выполняем vagrant up
и, пока наша виртуальная машинка создаётся, настраивается и запускается, читаем дальше.
ELK stack
ELK расшифровывается как elasticsearch, logstash и kibana. Раньше это были три самостоятельных продукта, но в какой-то момент они стали принадлежать одной компании и развиваться в одном направлении. Каждый из этих инструментов (с небольшими оговорками ниже) является полноценным независимым open source продуктом, а все вместе они составляют мощное решение для широкого спектра задач сбора, хранения и анализа данных. Теперь по порядку о каждом из них.
logstash
logstash – это утилита для сборки, фильтрации и последующего перенаправления в конечное хранилище данных. Вы могли слышать о fluentd – logstash решает ту же самую задачу, но написан на jruby и чуть более лучше дружит с elasticsearch (потому что теперь это продукт одной и той же компании, помните?).
Типичная конфигурация logstash представляет из себя несколько входящих потоков информации (input), несколько фильтров для этой информации (filter) и несколько исходящих потоков (output). Выглядит это как один конфигурационный файл, который в простейшем варианте (который не делает вообще ничего) выглядит вот так:
input {
}
filter {
}
output {
}
Не волнуйтесь, мы скоро перейдём к настоящим примерам.
– Так, так, стоп, Кирилл. Чё ещё за входящие потоки, какие ещё фильтры? Может пример какой приведёшь для этих терминов?
Привожу: допустим, у вас есть лог веб-сервера (nginx). Это входящий поток информации, input. Допустим, вы хотите каждую запись лога не только превратить в json-объект, но ещё и добавить гео-информацию о ней, основываясь на ip. Это фильтр, filter. А после того, как запись лога обработана и обогащена гео-данными, вы хотите отправить её в elasticsearch (скоро поймём почему). Это исходящий поток информации, output.
При этом у вас может быть сколько захочется input'ов, сколько приспичит фильтров (но не забудьте, что чем больше фильтров, тем больше ресурсов понадобится на обработку каждой записи) и сколько душе угодно output'ов. Logstash предоставляет из коробки внушительный набор готовых решений, поэтому вам не придётся писать свой фильтр для гео-данных, например. Он уже есть из коробки. Это, кстати, выгодно отличает logstash от fluentd.
elasticsearch
Изначально, elasticsearch – это решение для полнотекстового поиска, построенное поверх Apache Lucene, но с дополнительными удобствами, типа лёгкого масштабирования, репликации и прочих радостей, которые сделали elasticsearch очень удобным и хорошим решением для высоконагруженных проектов с большими объёмами данных.
Особенно доставляет в elasticsearch его простота и работоспособность из коробки. Конфигурация по-умолчанию скорее всего будет работать как надо для проектов средней и относительно высокой нагруженности. При этом вокруг ES сложилось отличное сообщество, которое всегда подскажет, как правильно настроить ваш ES-кластер для вашей конкретной задачи.
В какой-то момент elasticsearch стал настолько хорош, что использовать его только для поиска по товарам в интернет-магазинах (ну или там поиску по Basecamp) стало глупо и множество компаний начали основывать на ES свои решения по централизованному хранению логов и различной аналитики.
kibana
И вот у нас есть logstash, который собирает и обрабатывает данные со всех ваших тысяч серверов и elasticsearch, который изо всех сил эти данные хранит и позволяет искать по ним. Чего не хватает? Верно, не хватает Angular.js.
Поэтому в какой-то момент товарищ Rashid Khan написал для elasticsearch красивое Angular.js приложение kibana, позволяющее брать\искать данные по elasticsearch и строить множество красивых графиков. Ребята из elasticsearch не дураки, поэтому, увидев всё удобство этого решения, они забрали разработчика kibana к себе на борт.
Помните, я сказал, что все элементы ELK – независимые продукты? На самом деле, kibana бесполезна без elasticsearch. Это очень (очень) удобный интерфейс, позволяющий любому в вашей компании построить себе красивую панельку, на которую выводить аналитику всего, что logstash отправил в elasticsearch.
Пора практиковаться!
У меня возникла проблема: в какой-то момент в mkdev.me произошла какая-то беда, и чтобы разобраться в ней мне нужно было внимательно изучить что-то вроде сотни с лишним мегабайт логов. Удовольствие от использования grep
для этой задачи сомнительное, поэтому я решил взять лог с сервера, залить его в elasticsearch при помощи logstash и спокойно проанализировать проблему через браузер при помощи kibana.
Внимание! Возможно, у вас нет пары сотен с лишним мегабайт логов production приложения и вы скажете "э! а мне то чего пихать в elasticsearch"? Честно – не знаю. Но я почти уверен, что у вас локально лежит какое нибудь Ruby on Rails приложение, над которым вы работаете. А значит в папке log
этого приложения есть необходимый вам файлик. Вот его и возьмите. А если нет такого файла, то можете попробовать найти в интернете чужие логи.
Не отдам же я вам логи mkdev, в самом-то деле.
Заходим в виртуалку: vagrant ssh
.
Установка logstash
Я использовал logstash 2.2.0, документация по его установке валяется на оф. сайте. Прежде чем перейти к установке необходимо дополнительно установить на машине java. Выполняем одно за другим:
sudo apt-get update
sudo apt-get install openjdk-7-jdk curl
curl -O https://download.elasticsearch.org/logstash/logstash/logstash-2.2.0.tar.gz
tar zxvf logstash-2.2.0.tar.gz
cd logstash-2.2.0
Всё, готово. Установили.
Установка elasticsearch
Версия elasticsearch тоже 2.2.0
sudo apt-get install unzip
curl -L -O https://download.elasticsearch.org/elasticsearch/elasticsearch/elasticsearch-2.2.0.zip
unzip elasticsearch-2.2.0.zip
cd elasticsearch-2.2.0
Установка kibana
Используемая версия Kibana: 4.4.2. В реальных условиях, конечно, kibana должна быть отделена от logstash и elasticsearch и крутиться на отдельном сервере.
curl -O https://download.elastic.co/kibana/kibana/kibana-4.4.2-linux-x64.tar.gz
tar zxvf kibana-4.4.2-linux-x64.tar.gz
Конфигурируем logstash
У нас будет всего один input – stdin, стандартный ввод. Он позволит нам загрузить все данные из нашего лог-файла стандартной программой cat
. Обратите внимание на type
– к каждой итоговой записи, попадающей в output будет добавлено это поле.
input {
stdin {
type => "rails"
}
}
Самая сложная часть – фильтры. Я использую multiline, потому что каждая запись в логах rails занимает несколько строчек. Опция pattern
указывает на начало каждой записи.
filter {
multiline {
pattern => "(?m)Started"
negate => true
what => "previous"
}
# ...
grok – это регулярные выражения на стероидах. Позволяет определять конкретные шаблоны регулярных выражений, которые могут содержать в себе другие шаблоны. В logstash куча встроенных шаблонов grok. К сожалению, для rails там встроенного шаблона нет. После недолгих поисков в интернете я нашёл почти готовый шаблон, немного отредактировал его и он отлично сработал для логов mkdev.me (см. чуть ниже).
# ...
grok {
match => [
"message", "%{RAILS}"
]
}
if "_grokparsefailure" in [tags] {
drop { }
}
# ...
Найдите в папке logstash-2.2.0 папку patterns
, закиньте туда файл rails
со следующим содержанием:
RCONTROLLER (?<controller>[^#]+)#(?<action>\w+)
RAILS (?m)Started %{WORD:verb} \"%{URIPATHPARAM:request}\" for %{IPORHOST:clientip} at (?<timestamp>%{YEAR}-%{MONTHNUM}-%{MONTHDAY} %{HOUR}:%{MINUTE}:%{SECOND} %{ISO8601_TIMEZONE}).*Processing by %{RCONTROLLER} as (?<format>\S+)(?:\W*Parameters: {%{DATA:params}}\W*)?
Объяснение структуры этого файла не входит в мои планы, но обратите внимание на такие вещи, как, например, IPORHOST:clientip
– встроенный шаблон для распознавания ip-адресов. Выглядят grok-шаблоны уродливо, но с ними приходит великая сила. Есть отдельный сервис, на котором можно тестировать свои шаблоны.
Если какая-то запись не соответствует шаблону grok, то logstash добавит к ней тег _grokparsefailure
. Если у записи есть такой тег, то мы её просто выкидываем. Заметьте, что фильтры выполняются сверху вниз.
Последний фильтр – date. Парсит дату, записывает её в поле @timestamp
(формат, понятный kibana), а затем выкидывает оригинальное поле timestamp
(чтобы избежать дубликации). Без этого фильтра все записи будут иметь сегодняшнюю дату, а не дату события из лога.
# ...
date {
match => [ "timestamp", "YYYY-MM-dd HH:mm:ss Z"]
target => "@timestamp"
remove_field => "timestamp"
}
}
Целиком секция с фильтрами выглядит следующим образом:
filter {
multiline {
pattern => "(?m)Started"
negate => true
what => "previous"
}
grok {
match => [
"message", "%{RAILS}"
]
}
if "_grokparsefailure" in [tags] {
drop { }
}
date {
match => [ "timestamp", "YYYY-MM-dd HH:mm:ss Z"]
target => "@timestamp"
remove_field => "timestamp"
}
}
output у нас один – elasticsearch.
output {
elasticsearch_http {
host => "localhost"
}
}
Конфигурируем elastisearch
Прежде чем мы перейдём к настоящим чудесам, нам необходимо добавить кое-что в конфиг elasticsearch. Открываем elastichsearch-2.2.0/config/elasticsearch.yml
и добавляем в самый конец строчку http.cors.enabled: true
. Это позволит проводить CORS-запросы из kibana в ES.
Заливаем данные в elasticsearch
Переходим непосредственно к насыщенному экшену. Сначала запустим (из папки elasticsearch-2.2.0, созданной ранее при разархивировании) elasticsearch командой bin/elasticsearch --config=./config/elasticsearch.yml
. Затем заходим в папку с logstash и выполняем:
cat production.log | nice bin/logstash -f logstash.conf
Вместо production.log
подставьте путь к своему лог-файлу. На обработку и сохранение данных уйдёт какое-то время, особенно с учётом того, что мы всё это дело запускаем в виртуалке и того, что и logstash и elasticsearch любят брать много памяти (java же всё таки).
Смотрим данные
После того как обработка данных закончена мы можем их смотреть и анализировать. Это самая приятная часть.
Сначала покажем Kibana где у нас Elasticsearch:
config/kibana.yml
elasticsearch.url: "http://localhost:9200”
Затем запускаем kibana командой bin/kibana
. И открываем в браузере localhost:5601
. Не выключайте elasticsearch, так как kibana берёт все данные оттуда.
Интерфейс kibana должен быть относительно простым и интуитивным. Можно добавлять много разных панелей и проводить любые поддерживаемые elasticsearch поисковые запросы. Немного поигравшись, я построил вот такую панель, выводящую распределение запросов содержащих dashboard
и admin
по времени, а так же табличку с наиболее популярными контроллерами. Стоит ещё сказать, что это kibana 3. На момент написания статьи kibana 4 ещё не вышла из беты.
Замечания
Естественно, приведённая выше конфигурация не является идеальной и не рекомендуется для production окружения. grok-шаблон, например, распознает далеко не все записи из рельсовых логов. Тем не менее, принцип работы остаётся тем же: logstash берёт данные, обрабатывает их и посылает в elasticsearch, к которому затем цепляется kibana.
Если вас интересует как развернуть и автоматизировать production-ready elasticsearch кластер со всеми сопутствующими сервисами вокруг него и при помощи chef – обращайтесь ко мне на почту.
Круто?!
Теперь, дорогие читатели, вы можете стать настоящими властелинами данных. Области применения ELK стэка ограничены лишь вашей фантазией. Централизованное хранилище логов – лишь простейший пример. Другая популярная задача – сбор различных бизнес-событий из приложений и их аналитика. В конце концов, лог – это любая строчка текста, к которой прикреплены дата и время.
Не стоит забывать, что использовать ELK для небольших проектов не имеет особого смысла и затратно. Разворачивать на единственном маленьком сервере помимо самого приложение ещё и elasticsearch с logstash точно не стоит – как минимум память закончится очень быстро.
Настоятельно рекомендую попробовать разные типы input'ов, фильтров и output'ов logstash. Буду рад ответить на любые вопросы в комментариях.