Доўгатэрміновае захоўванне дадзеных у Elasticsearch
Мяне клічуць Ігар Сідарэнка, я тэхлід у камандзе адмінаў, якія падтрымліваюць у працоўным стане ўсю інфраструктуру Домклік.
Жадаю падзяліцца сваім досведам у наладзе размеркаванага захоўвання дадзеных у Elasticsearch. Мы разгледзім, якія налады на нодах адказваюць за размеркаванне шардаў, як уладкованы і працуе ILM.
Тыя, хто працуюць з логамі, так ці інакш сутыкаюцца з праблемай доўгатэрміновага захоўвання для наступнага аналізу. У Elasticsearch гэта асабліва актуальна, таму што з функцыянальнасцю куратара ўсё было сумна. У версіі 6.6/4 з'явіўся функцыянал ILM. Ён складаецца з XNUMX фаз:
Hot – індэкс актыўна абнаўляецца і запытваецца.
Warm – індэкс больш не абнаўляецца, але ўсё яшчэ запытваецца.
Cold – індэкс больш не абнаўляецца і рэдка запытваецца. Інфармацыя ўсё яшчэ павінна быць даступна для пошуку, але запыты могуць выконвацца больш павольна.
Delete - індэкс больш не патрэбен і можа быць бяспечна выдалены.
Гэтыя налады індывідуальныя, усё залежыць ад месца на нодах, колькасці індэксаў, логаў і г.д. У нас гэта 2-3 Тб дадзеных за суткі.
5 дзён - фаза Hot (8 асноўных / 1 рэпліка).
20 дзён - фаза Warm (shrink-індэкс 4 асноўных / 1 рэпліка).
90 дзён - фаза Cold (freeze-індэкс 4 асноўных / 1 рэпліка).
120 дзён - фаза Delete.
Настройка Elasticsearch
Для размеркавання шард па нодах патрэбен усяго адзін параметр:
гарачы-ноды:
~]# 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
{
"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-палітыку і пачынаем сачыць за жыццём азначнікаў.
GET _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" : { }
}
}
}
}
}
}
Праблемы
Былі праблемы на этапе наладкі і адладкі.
Hot-фаза
Для карэктнай ратацыі індэксаў крытычна прысутнасць у канцы index_name-date-000026 лікаў фармату 000001. У кодзе ёсць радкі, якія правяраюць азначнікі з дапамогай рэгулярнага выраза на наяўнасць лікаў у канцы. Інакш будзе памылка, да індэкса не прымяняцца палітыкі і ён заўсёды будзе ў hot-фазе.
Warm-фаза
Сціскацца (абразанне) - памяншэнне колькасці шардаў, таму што нод у цёплай і халоднай фазах у нас па 4. У дакументацыі ёсць такія радкі:
The index must be read-only.
Зьвесткі зь кожнай шпалы ў index маю reside на самім нумары.
У cluster health status павінны быць уgreen.
Каб зрэзаць азначнік, Elasticsearch перасоўвае ўсе асноўныя (primary) шарды на адну ноду, дублюе зрэзаны азначнік з неабходнымі параметрамі, а потым выдаляе стары. Параметр total_shards_per_node павінен быць роўны ці больш колькасці асноўных шардаў, каб змясціць іх на адной нодзе. У адваротным выпадку будуць апавяшчэння і шарды не пераедуць на патрэбныя ноды.
GET /shrink-k8s-ingress-2020.06.06-000025/_settings
Замарожваць (замарозка) - мы замарожваем індэкс для аптымізацыі запытаў па гістарычных дадзеных.
Searches performed on frozen indexs use the small, dedicated, search_throttled threadpool to control number of concurrent searches that hit frozen shards on each node. Гэтыя ліміты сумевыя дадатковыя памяшканні запатрабаваныя для існуючых дадзеных структур адпавядаюць пярэчаным зножванням, якія, зрэшты, абараняюць nodes proti excessive memory consumption.
Выкапаныя лічбы read-only: Вы не будзеце index in the them.
Searches on frozen indices expected to execute slowly. Frozen indices nejsou intended for high search load. Гэта магчыма, што даследаванне рэдагавання indexа можа пазбыцца секунд або хвілін да ўсяго, толькі калі тыя searches completed in milliseconds when indices no frozen.
Вынікі
Мы навучыліся падрыхтоўваць ноды для працы з ILM, наладзілі шаблон для размеркавання шардаў па гарачых нодах і наладзілі ILM на азначнік са ўсімі фазамі жыцця.