Казвам се Игор Сидоренко, технически лидер съм в екипа от администратори, които поддържат цялата инфраструктура на Domclick.
Искам да споделя моя опит в настройването на разпределено съхранение на данни в Elasticsearch. Ще разгледаме какви настройки на възлите са отговорни за разпределението на фрагментите, как работи и работи ILM.
Тези, които работят с регистрационни файлове, по един или друг начин са изправени пред проблема с дългосрочното съхранение за по-късен анализ. В Elasticsearch това е особено вярно, защото всичко беше жалко с функционалността на куратора. Версия 6.6 въведе ILM функционалност. Състои се от 4 фази:
Горещо - Индексът се актуализира активно и се извършват заявки.
Топло - Индексът вече не се актуализира, но все още се запитва.
Студено - Индексът вече не се актуализира и рядко се задават заявки. Информацията все още трябва да може да се търси, но заявките може да са по-бавни.
Изтриване - Индексът вече не е необходим и може безопасно да бъде изтрит.
За да разпределите фрагменти между възли, имате нужда само от един параметър:
Hot-възли:
~]# cat /etc/elasticsearch/elasticsearch.yml | grep attr
# Add custom attributes to the node:
node.attr.box_type: hot
Топло-възли:
~]# cat /etc/elasticsearch/elasticsearch.yml | grep attr
# Add custom attributes to the node:
node.attr.box_type: warm
Студ-възли:
~]# cat /etc/elasticsearch/elasticsearch.yml | grep attr
# Add custom attributes to the node:
node.attr.box_type: cold
Настройка на Logstash
Как работи всичко това и как внедрихме тази функция? Нека започнем с получаване на регистрационни файлове в Elasticsearch. Има два начина:
Logstash извлича регистрационни файлове от Kafka. Може да вземе чисто или да преобразува от ваша страна.
Нещо само по себе си пише в Elasticsearch, например APM сървър.
Помислете за пример за управление на индекси чрез Logstash. Той създава индекс и се прилага към него шаблон за индекс и съответстващи ILM.
k8s-ingress.conf
input {
kafka {
bootstrap_servers => "node01, node02, node03"
topics => ["ingress-k8s"]
decorate_events => false
codec => "json"
}
}
filter {
ruby {
path => "/etc/logstash/conf.d/k8s-normalize.rb"
}
if [log] =~ "[warn]" or [log] =~ "[error]" or [log] =~ "[notice]" or [log] =~ "[alert]" {
grok {
match => { "log" => "%{DATA:[nginx][error][time]} [%{DATA:[nginx][error][level]}] %{NUMBER:[nginx][error][pid]}#%{NUMBER:[nginx][error][tid]}: *%{NUMBER:[nginx][error][connection_id]} %{DATA:[nginx][error][message]}, client: %{IPORHOST:[nginx][error][remote_ip]}, server: %{DATA:[nginx][error][server]}, request: "%{WORD:[nginx][error][method]} %{DATA:[nginx][error][url]} HTTP/%{NUMBER:[nginx][error][http_version]}", (?:upstream: "%{DATA:[nginx][error][upstream][proto]}://%{DATA:[nginx][error][upstream][host]}:%{DATA:[nginx][error][upstream][port]}/%{DATA:[nginx][error][upstream][url]}", )?host: "%{DATA:[nginx][error][host]}"(?:, referrer: "%{DATA:[nginx][error][referrer]}")?" }
remove_field => "log"
}
}
else {
grok {
match => { "log" => "%{IPORHOST:[nginx][access][host]} - [%{IPORHOST:[nginx][access][remote_ip]}] - %{DATA:[nginx][access][remote_user]} [%{HTTPDATE:[nginx][access][time]}] "%{WORD:[nginx][access][method]} %{DATA:[nginx][access][url]} HTTP/%{NUMBER:[nginx][access][http_version]}" %{NUMBER:[nginx][access][response_code]} %{NUMBER:[nginx][access][bytes_sent]} "%{DATA:[nginx][access][referrer]}" "%{DATA:[nginx][access][agent]}" %{NUMBER:[nginx][access][request_lenght]} %{NUMBER:[nginx][access][request_time]} [%{DATA:[nginx][access][upstream][name]}] (?:-|%{IPORHOST:[nginx][access][upstream][addr]}:%{NUMBER:[nginx][access][upstream][port]}) (?:-|%{NUMBER:[nginx][access][upstream][response_lenght]}) %{DATA:[nginx][access][upstream][response_time]} %{DATA:[nginx][access][upstream][status]} %{DATA:[nginx][access][request_id]}" }
remove_field => "log"
}
}
}
output {
elasticsearch {
id => "k8s-ingress"
hosts => ["node01", "node02", "node03", "node04", "node05", "node06", "node07", "node08"]
manage_template => true # включаем управление шаблонами
template_name => "k8s-ingress" # имя применяемого шаблона
ilm_enabled => true # включаем управление ILM
ilm_rollover_alias => "k8s-ingress" # alias для записи в индексы, должен быть уникальным
ilm_pattern => "{now/d}-000001" # шаблон для создания индексов, может быть как "{now/d}-000001" так и "000001"
ilm_policy => "k8s-ingress" # политика прикрепляемая к индексу
index => "k8s-ingress-%{+YYYY.MM.dd}" # название создаваемого индекса, может содержать %{+YYYY.MM.dd}, зависит от ilm_pattern
}
}
Настройка на Kibana
Има основен модел, който се прилага за всички нови индекси. Той задава разпределението на горещите индекси, броя на шардовете, репликите и т.н. Теглото на шаблона се определя от опцията order. Шаблоните с по-голямо тегло заместват съществуващите параметри на шаблона или добавят нови.
GET _template/по подразбиране
{
"default" : {
"order" : -1, # вес шаблона
"version" : 1,
"index_patterns" : [
"*" # применяем ко всем индексам
],
"settings" : {
"index" : {
"codec" : "best_compression", # уровень сжатия
"routing" : {
"allocation" : {
"require" : {
"box_type" : "hot" # распределяем только по горячим нодам
},
"total_shards_per_node" : "8" # максимальное количество шардов на ноду от одного индекса
}
},
"refresh_interval" : "5s", # интервал обновления индекса
"number_of_shards" : "8", # количество шардов
"auto_expand_replicas" : "0-1", # количество реплик на ноду от одного индекса
"number_of_replicas" : "1" # количество реплик
}
},
"mappings" : {
"_meta" : { },
"_source" : { },
"properties" : { }
},
"aliases" : { }
}
}
След това приложете картографирането към индексите k8s-ingress-* използване на шаблон с по-голямо тегло.
След прилагане на всички шаблони прилагаме ILM политиката и започваме да наблюдаваме живота на индексите.
ВЗЕМЕТЕ _ilm/policy/k8s-ingress
{
"k8s-ingress" : {
"version" : 14,
"modified_date" : "2020-06-11T10:27:01.448Z",
"policy" : {
"phases" : {
"warm" : { # теплая фаза
"min_age" : "5d", # срок жизни индекса после ротации до наступления теплой фазы
"actions" : {
"allocate" : {
"include" : { },
"exclude" : { },
"require" : {
"box_type" : "warm" # куда перемещаем индекс
}
},
"shrink" : {
"number_of_shards" : 4 # обрезание индексов, т.к. у нас 4 ноды
}
}
},
"cold" : { # холодная фаза
"min_age" : "25d", # срок жизни индекса после ротации до наступления холодной фазы
"actions" : {
"allocate" : {
"include" : { },
"exclude" : { },
"require" : {
"box_type" : "cold" # куда перемещаем индекс
}
},
"freeze" : { } # замораживаем для оптимизации
}
},
"hot" : { # горячая фаза
"min_age" : "0ms",
"actions" : {
"rollover" : {
"max_size" : "50gb", # максимальный размер индекса до ротации (будет х2, т.к. есть 1 реплика)
"max_age" : "1d" # максимальный срок жизни индекса до ротации
},
"set_priority" : {
"priority" : 100
}
}
},
"delete" : { # фаза удаления
"min_age" : "120d", # максимальный срок жизни после ротации перед удалением
"actions" : {
"delete" : { }
}
}
}
}
}
}
Проблеми
Имаше проблеми на етапа на настройка и отстраняване на грешки.
Гореща фаза
За правилното въртене на индексите присъствието в края е критично index_name-date-000026 форматиране на числа 000001. В кода има редове, които проверяват индексите с помощта на регулярен израз за наличието на числа в края. В противен случай ще има грешка, няма да се прилагат политики към индекса и той винаги ще бъде в гореща фаза.
Топла фаза
Свиване (cutoff) — намаляване на броя на фрагментите, тъй като имаме 4 възела в топлата и студената фаза Документацията съдържа следните редове:
Индексът трябва да е само за четене.
Копие на всеки шард в индекса трябва да се намира на същия възел.
Здравният статус на клъстера трябва да е зелен.
За да съкрати индекс, Elasticsearch премества всички първични фрагменти в един възел, дублира съкратения индекс с необходимите параметри и след това изтрива стария. Параметър total_shards_per_node трябва да бъде равен или по-голям от броя на главните фрагменти, за да се поберат на един възел. В противен случай ще има известия и фрагментите няма да се преместят към правилните възли.
GET /shrink-k8s-ingress-2020.06.06-000025/_settings
Замразете (замразяване) - Ние замразяваме индекса, за да оптимизираме заявките за исторически данни.
Търсенията, извършени върху замразени индекси, използват малкия, специален, search_throttled пул от нишки, за да контролират броя на едновременните търсения, които удрят замразени фрагменти на всеки възел. Това ограничава количеството допълнителна памет, необходима за преходните структури от данни, съответстващи на замразени фрагменти, което следователно защитава възлите от прекомерна консумация на памет.
Замразените индекси са само за четене: не можете да индексирате в тях.
Очаква се търсенията на замразени индекси да се изпълняват бавно. Замразените индекси не са предназначени за голямо натоварване при търсене. Възможно е търсенето на замразен индекс да отнеме секунди или минути, за да завърши, дори ако същите търсения са завършили за милисекунди, когато индексите не са били замразени.
Резултати от
Научихме как да подготвим възли за работа с ILM, да настроим шаблон за разпределяне на сегменти между горещи възли и да настроим ILM за индекс с всички фази на живот.