Предисловие
Собственно, из-за чего я все это затеял? На одном проекте был написан свой поиск на базе SQL с фильтрами и прочей лабудой. И пока было мало записей в БД все работало отлично, но в один прекрасный день мы сделали выгрузку в БД на 100000 уникальных записей, и что-то резко пошло не так... Запрос на выборку выполнялся вместо 0,01сек аж 15сек, естественно это не дело. Первое что сделали как временное решение проблемы, перекинули сервер на более мощное железо, и этим мы немного улучшили свое положение. Теперь время запроса было 0,5сек. Тоже так себе, но появилось время на подумать. И пошел мыслительный процесс... Решили использовать поисковой движок, и выбор был между ElasticSearch (Далее ES) и Sphinx. Наш выбор пал на ES из-за его простоты в масштабируемости в будущем.
Но в процессе развертывания стало понятно что, доки у ES километровые и нету более менее подробной инфы на русском, исходя из чего родилась эта статья.
Сразу скажу что я не эксперт в теме, и попрошу не закидывать помидорами если что-то не так, пишу все это для тех кто хочет по-быстрому все поставить и потыкать и увидеть как все это работает. А для более элегантной работы всего этого нужно более глубокие настройки всей системы.
Установка ES на сервер
Сразу скажу что для ES нужно мощное железо, тк он работает на JAVA и поднимает свой виртуальный сервер.
Требования к железу |
минимальные |
рекомендуемые |
CPU |
2 |
4+ |
Memory |
6 Gb |
8+ Gb |
Disk |
10 Gb |
10+ Gb |
Я использовал для теста всего этого чуда бесплатные сервера Ampere от Oracle (4core 24ram 45rom).
Итак начнем с установки. Я использовал образ Ubuntu 20.04 так что буду описывать процесс основываясь на нем.
Копируем себе публичный ключ репозитория:
# wget -qO - https://artifacts.elastic.co/GPG-KEY-elasticsearch | sudo apt-key add -
Если у вас нет пакета apt-transport-https, то надо установить:
# apt install apt-transport-https
Добавляем репозиторий Elasticsearch в систему:
# echo "deb https://artifacts.elastic.co/packages/7.x/apt stable main" | sudo tee /etc/apt/sources.list.d/elastic-7.x.list
Устанавливаем Elasticsearch :
# apt update && apt install elasticsearch
После установки добавляем elasticsearch в автозагрузку и запускаем.
# systemctl daemon-reload
# systemctl enable elasticsearch.service
# systemctl start elasticsearch.service
Проверяем, запустился ли он:
# systemctl status elasticsearch.service
Проверим теперь, что elasticsearch действительно нормально работает. Выполним к нему простой запрос о его статусе.
# curl 127.0.0.1:9200
{
"name" : "ubuntu10",
"cluster_name" : "elasticsearch",
"cluster_uuid" : "zsYgZQSeY6uvhWCkFJPiAA",
"version" : {
"number" : "7.15.1",
"build_flavor" : "default",
"build_type" : "deb",
"build_hash" : "8ced7813d6f16d2ef30792e2fcde3e755795ee04",
"build_date" : "2021-10-07T21:56:19.031608185Z",
"build_snapshot" : false,
"lucene_version" : "8.9.0",
"minimum_wire_compatibility_version" : "6.8.0",
"minimum_index_compatibility_version" : "6.0.0-beta1"
},
"tagline" : "You Know, for Search"
}
Если все в порядке, то переходим к настройке Elasticsearch.
Настройка Elasticsearch
Настройки Elasticsearch находятся в файле /etc/elasticsearch/elasticsearch.yml. На начальном этапе нас будут интересовать следующие параметры:
path.data: /var/lib/elasticsearch # директория для хранения данных
network.host: 127.0.0.1 # слушаем только локальный интерфейс
По умолчанию Elasticsearch слушает localhost. Нам это и нужно, так как данные в него будет передавать logstash, который будет установлен локально. Обращаю отдельное внимание на параметр для директории с данными. Чаще всего они будут занимать значительное место, иначе зачем нам Elasticsearch :) Подумайте заранее, где вы будете хранить логи. Все остальные настройки я оставляю дефолтными. После изменения настроек, надо перезапустить службу:
# systemctl restart elasticsearch.service
Смотрим, что получилось:
# netstat -tulnp | grep 9200
tcp6 0 0 127.0.0.1:9200 :::* LISTEN 19788/java
Elasticsearch повис на локальном интерфейсе. Причем я вижу, что он слушает ipv6, а про ipv4 ни слова. Но его он тоже слушает, так что все в порядке. Переходим к установке Kibana. Если вы хотите, чтобы elasticsearch слушал все сетевые интерфейсы, настройте параметр:
network.host: 0.0.0.0
Только не спешите сразу же запускать службу. Если запустите, получите ошибку
Чтобы ее избежать, дополнительно надо добавить еще один параметр:
discovery.seed_hosts: ["127.0.0.1", "[::1]"]
Этим мы указываем, что хосты кластера следует искать только локально.
Итоговый вид файла конфига
# Имя нашего кластера
cluster.name: cluster-app
# имя нашей ноды
node.name: node-1
# путь к фалам
path.data: /var/lib/elasticsearch
# путь к логам
path.logs: /var/log/elasticsearch
# интерфейс который слушает ES
network.host: 0.0.0.0
discovery.seed_hosts: ["127.0.0.1", "[::1]"]
Установка Kibana
Дальше устанавливаем web панель Kibana для визуализации данных, полученных из Elasticsearch. Можно и без нее , но с ней удобнее, по крайней мере мне))
Подключаем репозиторий и ставим из deb пакета. Добавляем публичный ключ:
# wget -qO - https://artifacts.elastic.co/GPG-KEY-elasticsearch | apt-key add -
Добавляем рпозиторий Kibana:
# echo "deb https://artifacts.elastic.co/packages/7.x/apt stable main" | tee -a /etc/apt/sources.list.d/elastic-7.x.list
Запускаем установку Kibana:
# apt update && apt install kibana
Добавляем Кибана в автозагрузку и запускаем:
# systemctl daemon-reload
# systemctl enable kibana.service
# systemctl start kibana.service
Проверяем состояние запущенного сервиса:
# systemctl status kibana.service
По умолчанию, Kibana слушает порт 5601. Только не спешите его проверять после запуска. Кибана стартует долго. Подождите примерно минуту и проверяйте.
# netstat -tulnp | grep 5601
tcp 0 0 127.0.0.1:5601 0.0.0.0:* LISTEN 1487/node
Настройка Kibana
Файл с настройками Кибана располагается по пути - /etc/kibana/kibana.yml. На начальном этапе можно вообще ничего не трогать и оставить все как есть. По умолчанию Kibana слушает только localhost и не позволяет подключаться удаленно. Это нормальная ситуация, если у вас будет на этом же сервере установлен nginx в качестве reverse proxy, который будет принимать подключения и проксировать их в Кибана. Так и нужно делать в production, когда системой будут пользоваться разные люди из разных мест. С помощью nginx можно будет разграничивать доступ, использовать сертификат, настраивать нормальное доменное имя и т.д. Если же у вас это тестовая установка, то можно обойтись без nginx. Для этого надо разрешить Кибана слушать внешний интерфейс и принимать подключения. Измените параметр server.host, указав ip адрес сервера, например вот так:
server.host: "10.20.1.23"
Если хотите, чтобы она слушала все интерфейсы, укажите в качестве адреса 0.0.0.0. После этого Kibana надо перезапустить:
# systemctl restart kibana.service
Теперь можно зайти в веб интерфейс по адресу http://10.20.1.23:5601.
Периодически вы можете видеть в веб интерфейсе предупреждение:
server.publicBaseUrl is missing and should be configured when running in a production environment. Some features may not behave correctly.
Чтобы его не было, просто добавьте в конфиг Kibana параметр:
server.publicBaseUrl: "http://10.20.1.23:5601/"
Или доменное имя, если используете его.
Установка и настройка Logstash
Logstash устанавливается так же просто, как Elasticsearch и Kibana, из того же репозитория. Не буду еще раз показывать, как его добавить. Просто установим его и добавим в автозагрузку.
Установка Logstash:
# apt install logstash
Добавляем logstash в автозагрузку:
# systemctl enable logstash.service
Запускать пока не будем, надо его сначала настроить. Основной конфиг logstash лежит по адресу /etc/logstash/logstash.yml. Я его не трогаю, а все настройки буду по смыслу разделять по разным конфигурационным файлам в директории /etc/logstash/conf.d. Создаем первый конфиг input.conf, который будет описывать прием информации с beats агентов.
input {
beats {
port => 5044
}
}
Тут все просто. Указываю, что принимаем информацию на 5044 порт. Этого достаточно. Если вы хотите использовать ssl сертификаты для передачи логов по защищенным соединениям, здесь добавляются параметры ssl. Я буду собирать данные из закрытого периметра локальной сети, у меня нет необходимости использовать ssl. Теперь укажем, куда будем передавать данные. Тут тоже все относительно просто. Рисуем конфиг output.conf, который описывает передачу данных в Elasticsearch.
output {
elasticsearch {
hosts => "localhost:9200"
index => "nginx-%{+YYYY.MM.dd}"
}
#stdout { codec => rubydebug }
}
Что мы настроили? Передавать все данные в elasticsearch под указанным индексом с маской в виде даты. Разбивка индексов по дням и по типам данных удобна с точки зрения управления данными. Потом легко будет выполнять очистку данных по этим индексам. Я закомментировал последнюю строку. Она отвечает за логирование. Если ее включить, то все поступающие данные logstash будет отправлять дополнительно в системный лог. В centos это /var/log/messages. Используйте только во время отладки, иначе лог быстро разрастется дублями поступающих данных. Остается последний конфиг с описанием обработки данных. Тут начинается небольшая уличная магия, в которой я разбирался некоторое время. Расскажу ниже. Рисуем конфиг filter.conf.
filter {
if [type] == "nginx_access" {
grok {
match => { "message" => "%{IPORHOST:remote_ip} - %{DATA:user} \[%{HTTPDATE:access_time}\] \"%{WORD:http_method} %{DATA:url} HTTP/%{NUMBER:http_version}\" %{NUMBER:response_code} %{NUMBER:body_sent_bytes} \"%{DATA:referrer}\" \"%{DATA:agent}\"" }
}
}
date {
match => [ "timestamp" , "dd/MMM/YYYY:HH:mm:ss Z" ]
}
geoip {
source => "remote_ip"
target => "geoip"
add_tag => [ "nginx-geoip" ]
}
}
Первое, что делает этот фильтр, парсит логи nginx с помощью grok, если указан соответствующий тип логов, и выделяет из лога ключевые данные, которые записывает в определенные поля, чтобы потом с ними было удобно работать. С обработкой логов у новичков возникает недопонимание. В документации к filebeat хорошо описаны модули, идущие в комплекте, которые все это и так уже умеют делать из коробки, нужно только подключить соответствующий модуль.
Модули filebeat работают только в том случае, если вы отправляете данные напрямую в Elasticsearch. На него вы тоже ставите соответствующий плагин и получаете отформатированные данные с помощью elastic ingest. Но у нас работает промежуточное звено Logstash, который принимает данные. С ним плагины filebeat не работают, поэтому приходится отдельно в logstash парсить данные. Это не очень сложно, но тем не менее. Как я понимаю, это плата за удобства, которые дает logstash. Если у вас много разрозненных данных, то отправлять их напрямую в Elasticsearch не так удобно, как с использованием предобработки в Logstash.
Для фильтра grok, который использует Logstash, есть удобный дебаггер, где можно посмотреть, как будут парситься ваши данные. Покажу на примере одной строки из конфига nginx. Например, возьмем такую строку из лога:
180.163.220.100 - travvels.ru [05/Sep/2021:14:45:52 +0300] "GET /assets/galleries/26/1.png HTTP/1.1" 304 0 "https://travvels.ru/ru/glavnaya/" "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/50.0.2661.102 Safari/537.36"
И посмотрим, как ее распарсит правило grok, которое я использовал в конфиге выше.
%{IPORHOST:remote_ip} - %{DATA:user} \[%{HTTPDATE:access_time}\] \"%{WORD:http_method} %{DATA:url} HTTP/%{NUMBER:http_version}\" %{NUMBER:response_code} %{NUMBER:body_sent_bytes} \"%{DATA:referrer}\" \"%{DATA:agent}\"
Собственно, результат вы можете сами увидеть в дебаггере. Фильтр распарсит лог и на выходе сформирует json, где каждому значению будет присвоено свое поле, по которому потом удобно будет в Kibana строить отчеты и делать выборки. Только не забывайте про формат логов. Приведенное мной правило соответствует дефолтному формату main логов в nginx. Если вы каким-то образом модифицировали формат логов, внесите изменения в grok фильтр. Надеюсь понятно объяснил работу этого фильтра. Вы можете таким образом парсить любые логи и передавать их в еластикс. Потом на основе этих данных строить отчеты, графики, дашборды.
Дальше используется модуль date для того, чтобы выделять дату из поступающих логов и использовать ее в качестве даты документа в elasticsearch. Делается это для того, чтобы не возникало путаницы, если будут задержки с доставкой логов. В системе сообщения будут с одной датой, а внутри лога будет другая дата. Неудобно разбирать инциденты.
В конце я использую geoip фильтр, который на основе ip адреса, который мы получили ранее с помощью фильтра grok и записали в поле remote_ip, определяет географическое расположение. Он добавляет новые метки и записывает туда географические данные. Для его работы используется база данных из файла /usr/share/logstash/vendor/bundle/jruby/2.5.0/gems/logstash-filter-geoip-6.0.3-java/vendor/GeoLite2-City.mmdb. Она будет установлена вместе с logstash. Впоследствии вы скорее всего захотите ее обновлять. Раньше она была доступна по прямой ссылке, но с 30-го декабря 2019 года правила изменились. База по-прежнему доступна бесплатно, но для загрузки нужна регистрация на сайте сервиса. Регистрируемся и качаем отсюда - https://dev.maxmind.com/geoip/geoip2/geolite2/#Download_Access. Передаем на сервер, распаковываем и копируем в /etc/logstash файл GeoLite2-City.mmdb. Теперь нам нужно в настройках модуля geoip указать путь к файлу с базой. Добавляем в /etc/logstash/conf.d/filter.conf:
geoip {
database => "/etc/logstash/GeoLite2-City.mmdb"
source => "remote_ip"
target => "geoip"
add_tag => [ "nginx-geoip" ]
}
Закончили настройку logstash. Запускаем его:
# systemctl start logstash.service
Можете проверить на всякий случай лог /var/log/logstash/logstash-plain.log, чтобы убедиться в том, что все в порядке. Признаком того, что скачанная geoip база успешно добавлена будет вот эта строчка в логе:
[2021-10-18T12:52:07,118][INFO ][logstash.filters.geoip ][main] Using geoip database {:path=>"/etc/logstash/GeoLite2-City.mmdb"}
Теперь настроим агенты для отправки данных.
Установка Filebeat для отправки логов в Logstash
Установим первого агента Filebeat на сервер с nginx для отправки логов веб сервера на сервер с ELK. Ставить можно как из общего репозитория, который мы подключали ранее, так и по отдельности пакеты. Как ставить - решать вам. В первом случае придется на все хосты добавлять репозиторий, но зато потом удобно обновлять пакеты. Если подключать репозиторий не хочется, можно просто скачать пакет и установить его.
Можно ставить так:
# curl -L -O https://artifacts.elastic.co/downloads/beats/filebeat/filebeat-7.15.1-amd64.deb
# dpkg -i filebeat-7.15.1-amd64.deb
Или просто:
# yum install filebeat
# apt install filebeat
После установки рисуем примерно такой конфиг /etc/filebeat/filebeat.yml для отправки логов в logstash.
filebeat.inputs:
- type: log
enabled: true
paths:
- /var/log/nginx/*-access.log
fields:
type: nginx_access
fields_under_root: true
scan_frequency: 5s
type: log
enabled: true
paths:- /var/log/nginx/*-error.log
fields:
type: nginx_error
fields_under_root: true
scan_frequency: 5s
output.logstash:
hosts: ["10.1.4.114:5044"]
xpack.monitoring:
enabled: true
elasticsearch:
hosts: ["http://10.1.4.114:9200"]
Некоторые пояснения к конфигу, так как он не совсем дефолтный и минималистичный. Я его немного модифицировал для удобства. Во-первых, я разделил логи access и error с помощью отдельного поля type, куда записываю соответствующий тип лога: nginx_access или nginx_error. В зависимости от типа меняются правила обработки в logstash. Плюс, я включил мониторинг и для этого указал адрес elastichsearch, куда filebeat передает данные мониторинга напрямую. Показываю это для вас просто с целью продемонстрировать возможность. У меня везде отдельно работает мониторинг на zabbix, так что большого смысла в отдельном мониторинге нет. Но вы посмотрите на него, возможно вам он пригодится. Чтобы мониторинг работал, его надо активировать в соответствующем разделе в Management - Stack Monitoring.
И не забудьте запустить elasticsearch на внешнем интерфейсе. В первоначальной настройке я указал слушать только локальный интерфейс. Запускаем filebeat и добавляем в автозагрузку.
# systemctl start filebeat
systemctl enable filebeat
Проверяйте логи filebeat в дефолтном системном логе. По умолчанию, он все пишет туда. Лог весьма информативен. Если все в порядке, увидите список всех логов в директории /var/log/nginx, которые нашел filebeat и начал готовить к отправке. Если все сделали правильно, то данные уже потекли в elasticsearch. Мы их можем посмотреть в Kibana. Для этого открываем web интерфейс и переходим в раздел Discover. Так как там еще нет индекса, нас перенаправит в раздел Managemet, где мы сможем его добавить.
Вы должны увидеть индекс, который начал заливать logstash в elasticsearch. В поле Index pattern введите nginx-* . Выберите имя поля для временного фильтра. У вас будет только один вариант - @timestamp, выбирайте его и жмите Create Index Pattern.
Новый индекс добавлен. Теперь при переходе в раздел Discover, он будет открываться по умолчанию со всеми данными, которые в него поступают.
Получение логов с веб сервера nginx на linux настроили. Подобным образом настраивается сбор и анализ любых логов. Можно либо самим писать фильтры для парсинга с помощью grok, либо брать готовые.
Настройка безопасности и авторизация в Kibana
Во время предыдущих настроек вы могли заметить информационные сообщения в веб интерфейсе, которые постоянно выскакивали. Текст их следующий:
Warning: 299 Elasticsearch-7.15.1-83c34f456ae29d60e94d886e455e6a3409bba9ed "Elasticsearch built-in security features are not enabled. Without authentication, your cluster could be accessible to anyone. See https://www.elastic.co/guide/en/elasticsearch/reference/7.15/security-minimal-setup.html to enable security."
Нас предупреждают, что в настоящий момент аутентификация не настроена, поэтому доступ к информации может получить любой желающий. Если для вас это не проблема и вы изолировали доступ к данным, к примеру, на уровне firewall, можете игнорировать эти сообщения. Далее я расскажу, как избавиться от этого предупреждения и настроить доступ к поиску elastic, а также информации в Kibana с помощью авторизации по пользователю и паролю.
Открываем конфиг кластера в файле elasticsearch.yml и добавляем туда параметр:
xpack.security.enabled: true
Перезапускаем службу elasticsearch:
# systemctl restart elasticsearch
Если вы ранее не настраивали работу кластера по ssl, то скорее всего получите ошибку:
[ERROR][o.e.b.Bootstrap ] [centos8] node validation exception
[1] bootstrap checks failed. You must address the points described in the following [1] lines before starting Elasticsearch.
bootstrap check failure [1] of [1]: Transport SSL must be enabled if security is enabled on a [basic] license. Please set [xpack.security.transport.ssl.enabled] to [true] or disable security by setting [xpack.security.enabled] to [false]
Для того, чтобы настройки xpack.security заработали, добавляем еще один параметр:
xpack.security.transport.ssl.enabled: true
Запускаем elasticsearch еще раз. Теперь он должен запуститься, но доступа к нему ни у кого не будет. Kibana будет показывать ошибку:
{"statusCode":503,"error":"Service Unavailable","message":"License is not available."}
Нам нужно подготовить учётные данные, которые будем использовать для доступа к elasticsearch. Для этого запускаем утилиту elasticsearch-setup-passwords. По умолчанию она находится в папке /usr/share/elasticsearch/bin.
# /usr/share/elasticsearch/bin/elasticsearch-setup-passwords auto
Данные пароли вам стоит сохранить, так как в открытом виде вы их больше нигде не увидите. Они вам понадобятся далее, для организации доступа к кластеру остальным компонентам стека.
Настраиваем парольный доступ к кластеру для Kibana, используя сгенерированный только что пароль. Для этого добавляем в конфиг kibana.yml параметры:
xpack.security.enabled: true
elasticsearch.username: "kibana_system"
elasticsearch.password: "JArsJZj10YC8LlPlQr4u"
После этого перезапустите службу и пробуйте зайти в веб интерфес. Вы должны увидеть окно авторизации. Чтобы попасть в веб интерфейс, необходимо использовать сгенерированную ранее учетную запись elastic !!! А не какую-либо другую.
Мы настроили доступ в Kibana по паролю. Управялть пользователями и ролями можно через веб интерфейс. Делается это в разделе Stack Management -> Users. Там можно как отредактировать встроенные учетные записи, так и добавить новые.
Сделаем это на примере пользователя для Logstash. Создадим отдельного юзера с правами на запись в нужные нам индексы. Для этого сначала создадим роль с соответствующими правами. Переходим в Roles и добавляем новую.
Набор индексов и права доступа к ним вы можете выбрать те, что вам нужны. Можно разделить доступ: одни могут только писать данные, другие удалять и т.д. После создания роли, добавьте пользователя и добавьте ему созданную ранее роль.
Сразу можем создать пользователей и для Yii и Mysql чтоб по 5 раз не ходить. Только я не знаю почему, но им нужно задать роль суперюзера , по другому у меня не завелось, но вы можете попробовать создать свои роли и попробовать использовать их.
Теперь нам нужно настроить Logstash для авторизации в кластере. В настоящий момент он не может отправлять данные. В логе у него ошибки, которые явно не указывают, в чем проблема, но мы это знаем и так.
[ERROR][logstash.outputs.elasticsearch][main][f8cdb7a9c640d0ed412a776071b8530fd5c0011075712a1979bee6c58b4c1d9f] Encountered a retryable error (will retry with exponential backoff) {:code=>401, :url=>"http://localhost:9200/_bulk", :content_length=>2862}
Открываем конфиг logstash, отвечающий за output и добавляем туда учетные данные для доступа в кластер. Выглядеть это будет примерно так:
output {
elasticsearch {
user => "logstash_nginx_rw"
password => "gdhsgfadsfsdfgsfdget45t"
hosts => "localhost:9200"
index => "nginx-%{+YYYY.MM.dd}"
}
}
После этого данные как и прежде начнут поступать из logstash в elasticsearch, только теперь уже с авторизацией по пользователю и паролю.
Автоматическая очистка индексов в ES
Kibana есть встроенный инструмент для очистки данных - Index Lifecycle Policies. Его не трудно настроить самостоятельно, хотя и не могу сказать, что там всё очевидно. Есть некоторые нюансы, так что я по шагам расскажу, как это сделать. Для примера возьму всё тот же индекс nginx-, который использовал ранее в статье.
Настроим срок жизни индексов следующим образом:
Первые 30 дней - Hot phase. В этом режиме индексы активны, в них пишутся новые данные.
После 30-ти дней - Cold phase. В этой фазе в индексы невозможна запись новых данных. Запросы к этим данным имеют низкий приоритет.
Все, что старше 90 дней удаляется.
Чтобы реализовать эту схему хранения данных, идем в раздел Stack Management -> Index Lifecycle Management и добавляем новую политику. Я её назвал Nginx_logs. Выставляем параметры фаз в соответствии с заданными требованиями.
Не уместилось полное изображение настроек, но, думаю, вы там сами разберётесь, что выбрать. Ничего сложного тут нет. Далее нам нужно назначить новую политику хранения данных к индексам. Для этого переходим в Index Management -> Index Templates и добавляем новый индекс. В качестве шаблона укажите nginx-, все остальные параметры можно оставить дефолтными.
Теперь возвращаемся в Index Lifecycle Policies, напротив нашей политики нажимаем на + и выбираем только что созданный шаблон.
Проверяем свойства шаблона и убеждаемся в том, что Lifecycle Policies применилась.
Теперь ко всем новым индексам, созданным по этому шаблону, будет применяться политика хранения данных. Для уже существующих это нужно проделать вручную. Достаточно выбрать нужный индекс и в выпадающем списке с опциями выбрать нужное действие.
Такими несложными действиями можно настроить автоматическую очистку индексов встроенными инструментами Elasticsearch и Kibana. В некоторых случаях быстрее и удобнее воспользоваться Curator, особенно если нужно быстро реализовать много разных схем. Единый конфиг куратора выглядит более наглядным, но это уже вкусовщина и от ситуации зависит.
Подключение к Mysql и настройка автоматического индексирования.
Зачем это нужно, в моем случае весь бэк заточен на MySql и контачит с ним , и мне надо постоянно актуализировать данные в ES согласно SQL. Если у вас статичные данные или вы решите хранить все данные в ES без SQL, то можно пропустить этот шаг.
Покажу как все сделать на примере Mysql для других видов БД все также только в конфиге меняем Mysql на название вашего движка.
Итак идем в папку "/usr/share/logstash" и создаем новый файл logstash-db.conf с содержанием :
input {
jdbc {
# путь к драйверу БД "гуглится за 2 минуты запросом "
jdbc_driver_library => "/etc/logstash/conf.d/mysql-connector-java-8.0.27.jar"
jdbc_driver_class => "com.mysql.jdbc.Driver"
jdbc_connection_string => "jdbc:mysql://localhost/my_test_db"
# лог и пароль к БД
jdbc_user => "root"
jdbc_password => "admin"
# Можно настроить расписание выполнения индексирования, но у меня опять же хз почему оно не завелось .
# Я сделал обычную CRON функцию.
# schedule => "* * * * *"
# Наш SQL запрос к БД, как видим может быть абсалютно любым.
statement => "SELECT s.* , ST_AsText(s.geo) as geo ,st.`name` AS type_name ,
CONCAT(' ', group_concat(DISTINCT benefits.name SEPARATOR ' ') ,' ') as concat_bn,
st.alias as spot_type_alias, benefits.alias as benefit_alias,
group_concat(DISTINCT benefits.id SEPARATOR ' ') as concat_bn,
countries.name as country
FROM `spots` s
JOIN `spot_type` st ON s.id_spot_type = st.id
left join spots_benefits
on spots_benefits.spot = s.id
left join benefits
on benefits.id = spots_benefits.benefit
left join countries
on s.id_country = countries.id
where category = 1
group by s.id
"
}
}
output {
elasticsearch {
# название индекса в ES
index => "spots"
document_type => "spots"
document_id => "%{id}"
# путь к хосту ES и лог и пароль те что мы ранее создали в kibana
hosts => "localhost:9200"
user => "db"
password => "MascasdaehaefwP"
}
}
Все вроде настроили, можно запустить индексацию командой:
sudo /usr/share/logstash/bin/logstash -f /usr/share/logstash/logstash-db.conf
Можете сразу создать крон функцию и настроить необходимый интервал обновления, у меня индексация 100000 полей sql занимала порядком 5-10сек.
Настройка и работа с ES в Yii2
И так перейдем к настройке в Yii
Для начала установим плагин "Elasticsearch Query and ActiveRecord for Yii 2"
composer require --prefer-dist yiisoft/yii2-elasticsearch:"~2.1.0"
Далее в backend/config/main.php в поле "components" бэка добавим следующее
'elasticsearch' => [
'class' => 'yii\elasticsearch\Connection',
# адрес нашей ноды
'nodes' => [
['http_address' => '127.0.0.1:9200', ],
// configure more hosts if you have a cluster
],
# данные для входа что мы создали ранее
'auth' => ['username' => 'yiiuser', 'password' => 'MascasdasdwP8NGa3hfnn'],
'dslVersion' => 7, // default is 5
],
Теперь можно создать модель для нашего ES
<?php
namespace common\models;
use yii\elasticsearch\ActiveRecord;
class ElasticObj extends ActiveRecord
{
public static function index()
{
# имя нашего индеса в ES
return 'spots';
}
# мапинг индекса лучше заполнить чтобы потом проблем было меньше
# подробнее об этом будет в ссылках ниже
public static function mapping()
{
return [
'properties' => [
'name' => ['type' => 'text'],
//.....
]
];
}
#Атрибуты. Важно указать. Иначе, данные не сохранятся, и если в БД и индексе
# есть поле условно type и вы его тут не укажите , то на выдачу оно вам не вернется
# те проще говоря это те поля что вы сохраните и что будут видны после запроса
public function attributes()
{
return [
"id",
"name",
//....
];
}
#Правила. Важно указать. Иначе, данные не сохранятся.
#Я, поставил всем атрибутам правила как безопасные.
#В можете указать любые другие, которые вам необходимы.
public function rules()
{
return [
[$this->attributes(), 'safe']
];
}
}
Теперь попробуем провзаимодействовать с данными.
Создание:
$model = new ElasticObj();
$model->attributes = [
"name" => "test",
];
$model->save();
Получаем данные по определенному Id:
$model = ElasticObj::get(1)
Ищем все данные:
$model = ElasticObj::find()->all();
Поиск по параметрам:
$params = [
'match' => [
'last_name' => 'Smith'
]
];
$model = ElasticObj::find()->query($params)->all();
Фильтры:
$params = [
'filtered' => [
'filter' => [
'range' => [ // - диапазонный фильтр
'age' => [
'gt'=> 20 // - ищем старше 20 лет (параметр - gt)
]
]
],
'query' => [
'match' => [
'last_name' => 'smith'
]
],
],
];
$model = ElasticObj::find()->query($params)->all();
Полнотекстовый поиск по всем полям:
$params = [
"query_string" => [
"query"=> 'test'
],
];
$model = ElasticObj::find()->query($params)->all();
Добавление обязательных вхождений в выдачу:
$params['bool']['must'][]['match']['name'] = 'test';
$model = ElasticObj::find()->query($params)->all();
параметры bool можно задать необязательными или исключить какие-то данные из выдачи:
$params = [
'bool' => [
'must' => [
'match' => ['name' => 'test']
],
'should' => [
'match' => ['name' => 'not test'],
],
'must_not' => [
'match' => [ 'name' => 'one']
]
]
]
Можно обеденить несколько фильтров :
$params['bool']['must'][] = [
"query_string" => [
"query"=> 'test'
],
];
$params['bool']['must'][] =
[
'range' => [
'age' => [
'gt'=> 20
]
]
];
$model = ElasticObj::find()->query($params)->all();
и все в этом духе.
Для более углубленного понимания происходящего и построения более сложных запросов советую почитать
Заключение
Вроде все должно быть понятно, что и как. пытался расписать максимально доступно.
А так в общем хочу сказать что технология очень интересная и быстрая, но капец какая прожорливая по ресурсам. И использовать ее нужно только с очень жирными проектами, если у вас нет огромной БД и/или в будущем ее не предвидится, то смысла от ES нету , лучше посмотрите в сторону Sphinx или вообще сделайте свою реализацию на базе Sql благо в последних версиях завезли поддержку полнотекстового поиска.
Что почитать дополнительно
Инфа по ES на русском , очень хорошо написано, для общего понимания происходящего отлично подойдет.
Дока по плагину ES для Yii (есть на русском)
Источники
Комментарии (4)
alpik
21.11.2021 17:01Вместо логсташ и битсов можно использовать vector.dev - более гибкий и лояльный к ресурсам инструмент.
topuserman
23.11.2021 09:07Первое что сделали как временное решение проблемы, перекинули сервер на более мощное железо
Мне кажется, или первое, что надо было сделать - это заняться изучением проблемы.
Начиная с sql запроса и индексов и заканчивая структурой базы?
15 сек для 100к записей - здесь явно что-то не так.. Конечно, если у вас у железа не 256мб ОЗУ.
wolfandman
Да... в сравнении с Sphinx - ES и его настройка кажутся сложноватыми