Введение в ELK: собираем, фильтруем и анализируем большие данные

Illustration of a person sitting on the floor leaning against a wall while reading a book, with a stylized shadow casting over part of the image. They are wearing a scarf, a jacket, and orange sneakers.
Обновлено: | Опубликовано:
Illustration of a person sitting on the floor leaning against a wall while reading a book, with a stylized shadow casting over part of the image. They are wearing a scarf, a jacket, and orange sneakers.

Внимание! Статье уже четыре года и многое в используемых технологиях могло измениться. Не забываем консультироваться с офф. документацией.

Что, если я скажу вам, что логи могут быть не только полезными и содержать тонну важной информации, но и что работа с ними может быть классной, интересной и увлекательной? Настолько же увлекательной, интересной и классной, как кликанье через удобный интерфейс в браузере, позволяющий в считанные секунды вывести любой график и сравнить его с другим графиком? Да чего там: что, если я скажу вам, что после прочтения этой статьи вы сможете развернуть полноценный аналог 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. Буду рад ответить на любые вопросы в комментариях.

Subscribe to our Newsletter

Let us send you the best of what we've discovered in DevOps, Cloud and Kubernetes, as well us occasional event announcements.

We are also preparing some ways to learn together: weekly challenges, free courses and more. Subscribe now to be the first to get those.