Mi nomiĝas Igor Sidorenko, mi estas teknika gvidanto en la teamo de administrantoj, kiuj konservas la tutan infrastrukturon de Domclick.
Mi volas kunhavigi mian sperton pri agordo de distribuitaj datumstokado en Elasticsearch. Ni rigardos, kiaj agordoj sur la nodoj respondecas pri la distribuado de fragmentoj, kiel funkcias kaj funkcias ILM.
Tiuj, kiuj laboras kun ŝtipoj, de unu maniero aŭ alia, alfrontas la problemon de longdaŭra konservado por posta analizo. En Elasticsearch, tio estas precipe vera, ĉar ĉio estis malfeliĉa kun la kuratorfunkcio. Versio 6.6 lanĉis ILM-funkciecon. Ĝi konsistas el 4 fazoj:
Varma - La indekso estas aktive ĝisdatigita kaj pridemandita.
Varma - La indekso ne plu estas ĝisdatigita, sed daŭre estas pridemandita.
Malvarma - La indekso ne plu estas ĝisdatigita kaj malofte estas pridemandita. La informoj ankoraŭ devas esti serĉeblaj, sed demandoj povas esti pli malrapidaj.
Forigi - La indekso ne plu bezonas kaj povas esti sekure forigita.
Por distribui fragmentojn tra nodoj, vi bezonas nur unu parametron:
varma-nodoj:
~]# cat /etc/elasticsearch/elasticsearch.yml | grep attr
# Add custom attributes to the node:
node.attr.box_type: hot
varmajn-nodoj:
~]# cat /etc/elasticsearch/elasticsearch.yml | grep attr
# Add custom attributes to the node:
node.attr.box_type: warm
malvarmo-nodoj:
~]# cat /etc/elasticsearch/elasticsearch.yml | grep attr
# Add custom attributes to the node:
node.attr.box_type: cold
Agordi Logstash
Kiel ĉio funkcias kaj kiel ni efektivigis ĉi tiun funkcion? Ni komencu ricevante protokolojn en Elasticsearch. Estas du manieroj:
Logstash prenas protokolojn de Kafka. Povas preni purigi aŭ konverti sur via flanko.
Io mem skribas al Elasticsearch, ekzemple, APM-servilo.
Konsideru ekzemplon pri administrado de indeksoj per Logstash. Ĝi kreas indekson kaj aplikas al ĝi indeksa ŝablono kaj responda 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 aranĝo
Estas baza ŝablono, kiu validas por ĉiuj novaj indeksoj. Ĝi fiksas la distribuadon de varmaj indeksoj, la nombron da pecetoj, kopioj, ktp. La pezo de la ŝablono estas determinita de la opcio order. Ŝablonoj kun pli alta pezo superregas ekzistantajn ŝablonajn parametrojn aŭ aldonas novajn.
GET _ŝablonon/defaŭltan
{
"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" : { }
}
}
Poste apliku la mapadon al la indeksoj k8s-ingress-* uzante ŝablonon kun pli alta pezo.
Post aplikado de ĉiuj ŝablonoj, ni aplikas la ILM-politikon kaj komencas monitori la vivon de la indeksoj.
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" : { }
}
}
}
}
}
}
Problemoj
Estis problemoj ĉe la aranĝo kaj sencimiga stadio.
Varma fazo
Por la ĝusta rotacio de indeksoj, la ĉeesto ĉe la fino estas kritika index_name-date-000026 formataj nombroj 000001. Estas linioj en la kodo, kiuj kontrolas indeksojn uzante regulan esprimon por la ĉeesto de nombroj ĉe la fino. Alie, estos eraro, neniuj politikoj estos aplikitaj al la indekso, kaj ĝi ĉiam estos en la varma fazo.
Varma fazo
Shrink (tranĉo) — reduktante la nombron da fragmentoj, ĉar ni havas 4 nodojn en la varma kaj malvarma fazoj. La dokumentaro enhavas la jenajn liniojn:
La indekso devas esti nurlegebla.
Kopio de ĉiu fragmento en la indekso devas loĝi sur la sama nodo.
La sano-stato devas esti verda.
Por pritondi indekson, Elasticsearch movas ĉiujn ĉefajn pecetojn al unu nodo, duobligas la detranĉitan indekson kun la necesaj parametroj, kaj poste forigas la malnovan. Parametro total_shards_per_node devas esti egala aŭ pli granda ol la nombro da ĉefaj pecetoj por konveni sur unu nodo. Alie, estos sciigoj kaj pecetoj ne moviĝos al la ĝustaj nodoj.
GET /shrink-k8s-ingress-2020.06.06-000025/_settings
glaciigi (frostigo) - Ni frostas la indekson por optimumigi demandojn pri historiaj datumoj.
Serĉoj faritaj sur frostaj indeksoj uzas la malgrandan, dediĉitan, search_throttled threadpool por kontroli la nombron da samtempaj serĉoj kiuj trafas frostajn pecetojn sur ĉiu nodo. Tio limigas la kvanton de ekstra memoro necesa por la pasemaj datumstrukturoj egalrilatantaj al frostaj pecetoj, kiu sekve protektas nodojn kontraŭ troa memorkonsumo.
Frostigitaj indeksoj estas nurlegeblaj: oni ne povas indeksigi ilin.
Serĉoj sur frostaj indeksoj estas atenditaj ekzekuti malrapide. Frostigitaj indeksoj ne estas destinitaj por alta serĉŝarĝo. Estas eble ke serĉo de frosta indekso povas daŭri sekundojn aŭ minutojn por kompletigi, eĉ se la samaj serĉoj finiĝis en milisekundoj kiam la indeksoj ne estis frostigitaj.
Rezultoj
Ni lernis kiel prepari nodojn por labori kun ILM, agordi ŝablonon por distribuado de fragmentoj inter varmaj nodoj, kaj agordi ILM por indekso kun ĉiuj fazoj de vivo.