Elastic для маленьких. Оптимизируем для небольшой компании.

В сети можно найти много ресурсов о том, как оптимизировать крупные кластеры Elasticsearch. И ничего нет об оптимизации маленьких кластеров, тем более совсем никаких не кластеров, а просто одного узла. Тем не менее, в конфигурации «по умолчанию» ваш узел Elasticsearch продержится не долго. Думаю, и неделю не простоит. Надо менять параметры конфигурации. К сожалению, менять их «тупо» не получится. Надо понять, как работает вся конструкция. Это довольно просто.

Индексы и шарды

Когда приложение отправляет данные в Elastic, оно сообщает ему в какой индекс следует писать данные. Чтобы не создавать слишком большие индексы, в которых потом невозможно будет ничего найти, принято разумное решение разбивать один по сути индекс на несколько по параметру @timestamp, например, создавать новый индекс каждый день. Ежедневный индекс — это самое разумное решение для технических журналов. Но можно создавать каждый час или каждый месяц. Шаблон для создания индексов указывается так:

output {
    if "winlogbeat" in [tags] {
        elasticsearch {
             hosts => ["127.0.0.1:9200"]
             index => "winlogbeat-%{+YYYY.MM.dd}"
             document_type => "winlogbeat"
             template => "/etc/logstash/winlogbeat.template-es2x.json"
             template_name => "winlogbeat"
             template_overwrite => true
        }

    }
}

Нужное написано в строке index => "winlogbeat-%{+YYYY.MM.dd}". На самом деле Elastic не занимается разбиением индексов по датам, об этом должно заботиться приложение, которое отправляет ему данные. В данном случае Logstash. Но и приложение может об этом не париться. В приведенном примере всего лишь написано, что в название индекса должна быть включена строка сгенерированная по значению поля @timestamp в документе. Кстати, если там нет такого поля, будет сюрприз. А дальше используется тот простой факт, что в настройке Elastic по умолчанию включен параметр автоматического создания индекса, если такого индекса нет. Свойство документа @timestamp не является «данным свыше», что туда писать, определяет разработчик. Если уж мы начали рассматривать Winlogbeat, то на нем и продолжим. Здесь в @timestamp пишется время создания события в журнале windows на хосте. На практике это означает, что при первом запуске агента winlogbeat на машине, он начнет перегонять вам весь event-log за всю историю существования этой машины. В зависимости от настроек аудита на конкретной машине, у вас могут начать создаваться индексы на каждый день многих прошлых лет. И ещё. Каждому солдату известен вопрос: «Когда в карауле наступает темное время суток?». Так вот, в Elasticsearch новые сутки наступают в ноль часов по UTC, а вовсе не по локальному времени.

Кроме того, вы можете разбить индексы, например, по именам хостов, с которых приходят события. Вот так index => "winlogbeat-%{[beat][name]}-%{+YYYY.MM.dd}". Только помните, что в имени индекса не должно быть заглавных букв, шаблоны имени, включающие строки из значений полей документа, используйте с осторожностью. На практике, лучше этого не делать.

Разбивка по индексам — важный вопрос производительности системы. Слишком большие индексы — плохо, поскольку внутри них трудно будет искать. Слишком маленькие — тоже плохо, поскольку при поиске в большом объеме данных с фильтрацией и агрегированием нужно будет лопатить много индексов, конечно, это будет делаться «параллельно», но потом нужно будет «склеивать» куски. Хорошо, когда индекс таков, что большинство нужных поисковых запросов делается в пределах одного индекса, и он целиком помещается в оперативную память. Точнее говоря, в память должен помещаться шард. О шардах чуть ниже. На практике, 2GB — вполне хороший размер даже без разбивки на шарды. Если говорить о журналах Windows и сборе их агентом Winlogbeat, то доменная сеть из 150 рабочих станций и хреновой тучи серверов сделает за сутки примерно 1,5 миллионов записей, размер суточного индекса будет около 1,7GB. Нормально. Если вы собираете логи агентом OSSEC, размер суточного индекса будет меньше, OSSEC не передает «чистый» лог, он сам предварительно обрабатывает и фильтрует записи.

На самом деле, индекс — это не та «единица», с которой оперирует Elastic. Индекс разбивается на фрагменты — шарды (shards). Вот это и есть настоящая единица данных. Каждый шард аллокируется в файловой системе на каком-либо узле кластера. При поиске в индексе, каждый его шард обрабатывается отдельно. Шард загружается в оперативную память на том узле, где он физически хранится и обрабатывается там. Это дает возможность параллельной обработки данных на нескольких узлах кластера. Для больших это хорошо. А для маленьких — бесполезно. Узлы кластера стоят денег. У нас денег нет, но премьер министр велел держаться. Значит будем держаться на «кластере» из одного узла. Благодаря многопоточности, шарды могут параллельно обрабатываться и на одном узле. Я говорю «могут», поскольку не знаю, делается ли это при операциях поиска. При вводе данных, точно делается. Есть в сети отчеты об экспериментах, которые говорят, что при больших входных потоках (около 10 000 записей в секунду на один узел) увеличение количества шардов в индексе увеличивает пропускную способность системы, она не захлебывается. Но, как там обстоят дела с поиском, я не знаю. Надо смотреть исходный код. Пока туда не лазил.

Нам, маленьким, 10 000 записей в секунду не грозят. Параллельная работа нескольких узлов нам тоже не по карману. Разбиение индексов на шарды нам никаких ощутимых преимуществ не дает. А вот неприятности мы получить можем. Например, когда попробуем перезапустить Elastic. Он начнет процесс «приписывания» шардов к узлу. Для каждого шарда ему необходимо будет выяснить, на каком узле он размещен, открыть его и проверить целостность. Это довольно длительная процедура. Хуже того, с «рекомендованными» в документации настройками, больше 10 000 шардов узел не откроет, ему просто не хватит файловых дескрипторов. Там в конфиге написано 65 536 открытых файлов, а нужно больше, гораздо больше. Кластер вообще не поднимется.

А что такое 10 000 шардов применительно к настройкам по умолчанию? Пустое место. На каждый индекс создается 5 первичных шардов и 5 реплик, чтобы кластер запустился необходимо открыть только первичные шарды, реплики не обязательно. Значит вы можете открыть менее 2000 индексов. Если у вас 10 типов источников событий и вы под каждый создаете суточный индекс, то через пол года у вас будет традиционный русский белый пушистый зверек.

На практике, чем меньше шардов, тем лучше. Главное, чтобы шарды свободно помещались в оперативной памяти и не толкались там задницами. У маленьких не много источников логов с большим количеством записей в сутки. Скорее всего ни одного. У меня Elastic получает 8GB оперативной памяти (по умолчанию 2GB, ниже напишу, где менять настройку). Все индексы имеют один первичный шард и не имеют реплик. Понятно, что реплик нет, поскольку узел один. Elastic, кстати не станет хранить две копии шарда на одном узле, поэтому требовать создания реплики в случае единственного узла бессмысленно.

А как сказать сколько нужно шардов для индекса? Это можно сказать только при создании индекса, после создания переиграть уже нельзя. Индексы у нас создаются ежедневно. В приведенном выше фрагменте файла конфигурации Logstash есть такие строчки:

             template => "/etc/logstash/winlogbeat.template-es2x.json"
             template_name => "winlogbeat"
             template_overwrite => true

Первая задает путь к файлу шаблона индекса, именно в этом шаблоне и указано сколько делать шардов. Последняя задает требования перезагрузки шаблона в Elastic при каждом перезапуске Logstash. Это для удобства, чтобы можно было внести изменения в шаблон просто поправив этот файл в редакторе. Шаблон уже созданного индекса переопределить нельзя. Можно, но это требует переиндексации, то есть фактически повторной загрузки данных. Измененный шаблон применится только к новым индексам. А вот уже внутри файла с шаблоном у нас есть строчки:

"settings": {
    "index.refresh_interval": "5s",
    "number_of_shards" : 1,
    "number_of_replicas" : 0
  }

Поскольку количество шардов определяется на уровне индекса, то мы можем для каждого источника логов отдельно определять на сколько шардов разбивать индекс. Хороший критерий здесь — количество оперативной памяти. Об этом я написал выше.

Разобравшись с индексами и шардами можем приступать к конфигурации машины с Elasticsearsch.

Примерная конфигурация

Начать нужно с того, что установить лимиты для юзера elasticsearch в системе. У меня Debian. Лимиты задаются в файле /etc/security/limits.conf вот так:

elasticsearch soft memlock unlimited
elasticsearch hard memlock unlimited
elasticsearch soft nofile 500000
elasticsearch hard nofile 500000

Теперь надо сказать java машине, что ей нужно забрать под «кучу» 8GB памяти. Для разных версий Elastic это делается по-разному. В 5.х нужно править файл конфигурации /etc/elasticsearch/jvm.options:

-Xms8g
-Xmx8g

По умолчанию там написано 2g. Если версия 2.х, то нужно вносить несколько правок в разных местах. Во-первых, определить переменную окружения ES_HEAP_SIZE в файле /etc/default/elasticsearch строкой:

ES_HEAP_SIZE=8g

Во-вторых, в том же файле нужно поправить строку:

MAX_LOCKED_MEMORY=unlimited

Там же поставьте сразу:

MAX_OPEN_FILES=500000

Теперь откройте файл /usr/lib/systemd/system/elasticsearch.service и в нем поправьте строки:

LimitNOFILE=500000
LimitMEMLOCK=infinity

И наконец в файле /etc/elasticsearch/elasticsearch.yml раскомментируйте строку:

bootstrap.memory_lock: true

Что касается лимитов на открытие файлов, их надо переопределять и для версии 5.х в тех же файлах (строго говоря только в /usr/lib/systemd/system/elasticsearch.service, если вы используете systemd для запуска сервиса, но сделайте в обоих, это не повредит). А вот параметр bootstrap.memory_lock в файле /etc/elasticsearch/elasticsearch.yml в версии 5.х не нужен. Более того, он будет считаться ошибкой. В этом же файле можно дописать (изначально его там нет) еще один полезный параметр:

cluster.routing.allocation.node_initial_primaries_recoveries: 10

Он задает количество одновременных задач аллокации и проверки шардов при запуске кластера. Это может сократить время запуска, впрочем, не сильно. Если конечно у вас не SSD в качестве дисков. Тогда смело ставьте 1000. По умолчанию elastic запускает 4 таска одновременно.

И на последок несколько полезных консольных команд, которые всегда должны быть под рукой:

curl -XGET 'http://localhost:9200/_cluster/stats?human&pretty'

выдаст вам всю информацию о состоянии кластера: сколько индексов, сколько шардов, как расходуется оперативная память, сколько есть места на диске и т.п.

curl -s localhost:9200/_cat/shards?v

Выдаст информацию по шардам каждого индекса: где аллокирована, сколько занимает места на диске.

curl http://localhost:9200/_cluster/health/

компактный вывод информации о «здоровье» кластера.
А еще может пригодиться вот такая:

curl -XPUT 'localhost:9200/_settings' -d '
    {
        "index" : {
            "number_of_replicas" : 0
        }
    }'

Это переопределит настройку количества реплик в 0 для всех шардов всех индексов, в том числе уже созданных (количество реплик, в отличии от количества первичных шардов, можно переопределять «на лету»).

В документации рекомендуется отдавать Elastic половину оперативной памяти машины, при условии, что он живет на машине один. Хороший совет, но мы маленькие, у нас денег нет. Я отдаю 8 гиг из 12-и, при том, что у меня на этой же машине еще Logstash и Kibana. Нормально всё работает.

В следующей заметке напишу о работе с Elasticsearch из питона.

Реклама

Elastic для маленьких. Оптимизируем для небольшой компании.: 3 комментария

  1. Уведомление: ELK Отслеживание процессов Windows во всей сети | ESGUARDIAN

  2. Огромное спасибо за статью. Действительно тяжело найти материал на русском, да еще и для ELK стека небольших сетей.

    Нравится

Добавить комментарий

Заполните поля или щелкните по значку, чтобы оставить свой комментарий:

Логотип WordPress.com

Для комментария используется ваша учётная запись WordPress.com. Выход /  Изменить )

Google+ photo

Для комментария используется ваша учётная запись Google+. Выход /  Изменить )

Фотография Twitter

Для комментария используется ваша учётная запись Twitter. Выход /  Изменить )

Фотография Facebook

Для комментария используется ваша учётная запись Facebook. Выход /  Изменить )

w

Connecting to %s