Dolgoročno shranjevanje podatkov v Elasticsearch

Dolgoročno shranjevanje podatkov v Elasticsearch

Moje ime je Igor Sidorenko, sem tehnični vodja v ekipi administratorjev, ki vzdržujejo celotno infrastrukturo Domklika v brezhibnem stanju.

Rad bi delil svojo izkušnjo pri nastavitvi porazdeljenega shranjevanja podatkov v Elasticsearch. Ogledali si bomo, katere nastavitve na vozliščih so odgovorne za porazdelitev drobcev, kako je ILM strukturiran in deluje.

Tisti, ki tako ali drugače delajo s hlodi, se soočajo s problemom dolgotrajnega shranjevanja za naknadno analizo. To še posebej velja za Elasticsearch, ker je bila funkcija kuratorja slaba. V različici 6.6 se je pojavila funkcionalnost ILM. Sestavljen je iz 4 faz:

  • Vroče—indeks se aktivno posodablja in poizveduje.
  • Toplo - indeks se ne posodablja več, vendar je še vedno poizveden.
  • Hladno – Indeks se ne posodablja več in se po njem redko povprašujejo. Po informacijah bi moralo biti še vedno mogoče iskati, vendar bodo poizvedbe morda počasnejše.
  • Izbriši – kazalo ni več potrebno in ga lahko varno izbrišete.

dano

  • Elasticsearch Data Hot: 24 procesorjev, 128 GB pomnilnika, 1,8 TB SSD RAID 10 (8 vozlišč).
  • Elasticsearch Data Warm: 24 procesorjev, 64 GB pomnilnika, 8 TB NetApp SSD Policy (4 vozlišča).
  • Elasticsearch Data Cold: 8 procesorjev, 32 GB pomnilnika, 128 TB HDD RAID 10 (4 vozlišča).

Cilj

Te nastavitve so individualne, vse je odvisno od prostora na vozliščih, števila indeksov, dnevnikov itd. Za nas je to 2-3 TB podatkov na dan.

  • 5 dni - vroča faza (8 glavnih / 1 replika).
  • 20 dni - Topla faza (indeks krčenja 4 glavne / 1 replika).
  • 90 dni - hladna faza (zamrznitveni indeks 4 glavne / 1 replika).
  • 120 dni - faza brisanja.

Nastavitev Elasticsearch

Za razdelitev drobcev med vozlišča potrebujete samo en parameter:

  • Hot-vozlišča:
    ~]# cat /etc/elasticsearch/elasticsearch.yml | grep attr
    # Add custom attributes to the node:
    node.attr.box_type: hot
  • Toplo-vozlišča:
    ~]# cat /etc/elasticsearch/elasticsearch.yml | grep attr
    # Add custom attributes to the node:
    node.attr.box_type: warm
  • Hladno-vozlišča:
    ~]# cat /etc/elasticsearch/elasticsearch.yml | grep attr
    # Add custom attributes to the node:
    node.attr.box_type: cold

Nastavitev Logstash

Kako vse to deluje in kako smo implementirali to funkcijo? Začnimo s prenosom dnevnikov v Elasticsearch. Obstajata dva načina:

  1. Logstash pridobi dnevnike iz Kafke. Lahko pobere čisto ali pretvori na stran.
  2. Nekaj ​​se zapiše v Elasticsearch, na primer strežnik APM.

Oglejmo si primer upravljanja indeksov prek Logstasha. Ustvari indeks in se uporabi predloga kazala in ustrezno 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
    }
}

Postavitev Kibane

Obstaja osnovna predloga, ki velja za vse nove indekse. Nastavi porazdelitev vročih indeksov, število drobcev, replik itd. Teža predloge je določena z možnostjo order. Predloge z višjimi utežmi preglasijo obstoječe parametre predloge ali dodajo nove.

Dolgoročno shranjevanje podatkov v Elasticsearch
Dolgoročno shranjevanje podatkov v Elasticsearch

GET_template/privzeto

{
  "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" : { }
  }
}

Nato uporabite preslikavo za indekse k8s-ingress-* z uporabo predloge z večjo težo.

Dolgoročno shranjevanje podatkov v Elasticsearch
Dolgoročno shranjevanje podatkov v Elasticsearch

PRIDOBITE _template/k8s-ingress

{
  "k8s-ingress" : {
    "order" : 100,
    "index_patterns" : [
      "k8s-ingress-*"
    ],
    "settings" : {
      "index" : {
        "lifecycle" : {
          "name" : "k8s-ingress",
          "rollover_alias" : "k8s-ingress"
        },
        "codec" : "best_compression",
        "routing" : {
          "allocation" : {
            "require" : {
              "box_type" : "hot"
            }
          }
        },
        "number_of_shards" : "8",
        "number_of_replicas" : "1"
      }
    },
    "mappings" : {
      "numeric_detection" : false,
      "_meta" : { },
      "_source" : { },
      "dynamic_templates" : [
        {
          "all_fields" : {
            "mapping" : {
              "index" : false,
              "type" : "text"
            },
            "match" : "*"
          }
        }
      ],
      "date_detection" : false,
      "properties" : {
        "kubernetes" : {
          "type" : "object",
          "properties" : {
            "container_name" : {
              "type" : "keyword"
            },
            "container_hash" : {
              "index" : false,
              "type" : "keyword"
            },
            "host" : {
              "type" : "keyword"
            },
            "annotations" : {
              "type" : "object",
              "properties" : {
                "value" : {
                  "index" : false,
                  "type" : "text"
                },
                "key" : {
                  "index" : false,
                  "type" : "keyword"
                }
              }
            },
            "docker_id" : {
              "index" : false,
              "type" : "keyword"
            },
            "pod_id" : {
              "type" : "keyword"
            },
            "labels" : {
              "type" : "object",
              "properties" : {
                "value" : {
                  "type" : "keyword"
                },
                "key" : {
                  "type" : "keyword"
                }
              }
            },
            "namespace_name" : {
              "type" : "keyword"
            },
            "pod_name" : {
              "type" : "keyword"
            }
          }
        },
        "@timestamp" : {
          "type" : "date"
        },
        "nginx" : {
          "type" : "object",
          "properties" : {
            "access" : {
              "type" : "object",
              "properties" : {
                "agent" : {
                  "type" : "text"
                },
                "response_code" : {
                  "type" : "integer"
                },
                "upstream" : {
                  "type" : "object",
                  "properties" : {
                    "port" : {
                      "type" : "keyword"
                    },
                    "name" : {
                      "type" : "keyword"
                    },
                    "response_lenght" : {
                      "type" : "integer"
                    },
                    "response_time" : {
                      "index" : false,
                      "type" : "text"
                    },
                    "addr" : {
                      "type" : "keyword"
                    },
                    "status" : {
                      "index" : false,
                      "type" : "text"
                    }
                  }
                },
                "method" : {
                  "type" : "keyword"
                },
                "http_version" : {
                  "type" : "keyword"
                },
                "bytes_sent" : {
                  "type" : "integer"
                },
                "request_lenght" : {
                  "type" : "integer"
                },
                "url" : {
                  "type" : "text",
                  "fields" : {
                    "keyword" : {
                      "type" : "keyword"
                    }
                  }
                },
                "remote_user" : {
                  "type" : "text"
                },
                "referrer" : {
                  "type" : "text"
                },
                "remote_ip" : {
                  "type" : "ip"
                },
                "request_time" : {
                  "format" : "yyyy/MM/dd HH:mm:ss||yyyy/MM/dd||epoch_millis||dd/MMM/YYYY:H:m:s Z",
                  "type" : "date"
                },
                "host" : {
                  "type" : "keyword"
                },
                "time" : {
                  "format" : "yyyy/MM/dd HH:mm:ss||yyyy/MM/dd||epoch_millis||dd/MMM/YYYY:H:m:s Z",
                  "type" : "date"
                }
              }
            },
            "error" : {
              "type" : "object",
              "properties" : {
                "server" : {
                  "type" : "keyword"
                },
                "upstream" : {
                  "type" : "object",
                  "properties" : {
                    "port" : {
                      "type" : "keyword"
                    },
                    "proto" : {
                      "type" : "keyword"
                    },
                    "host" : {
                      "type" : "keyword"
                    },
                    "url" : {
                      "type" : "text",
                      "fields" : {
                        "keyword" : {
                          "type" : "keyword"
                        }
                      }
                    }
                  }
                },
                "method" : {
                  "type" : "keyword"
                },
                "level" : {
                  "type" : "keyword"
                },
                "http_version" : {
                  "type" : "keyword"
                },
                "pid" : {
                  "index" : false,
                  "type" : "integer"
                },
                "message" : {
                  "type" : "text"
                },
                "tid" : {
                  "index" : false,
                  "type" : "keyword"
                },
                "url" : {
                  "type" : "text",
                  "fields" : {
                    "keyword" : {
                      "type" : "keyword"
                    }
                  }
                },
                "referrer" : {
                  "type" : "text"
                },
                "remote_ip" : {
                  "type" : "ip"
                },
                "connection_id" : {
                  "index" : false,
                  "type" : "keyword"
                },
                "host" : {
                  "type" : "keyword"
                },
                "time" : {
                  "format" : "yyyy/MM/dd HH:mm:ss||yyyy/MM/dd||epoch_millis||dd/MMM/YYYY:H:m:s Z",
                  "type" : "date"
                }
              }
            }
          }
        },
        "log" : {
          "type" : "text"
        },
        "@version" : {
          "type" : "text",
          "fields" : {
            "keyword" : {
              "ignore_above" : 256,
              "type" : "keyword"
            }
          }
        },
        "eventtime" : {
          "type" : "float"
        }
      }
    },
    "aliases" : { }
  }
}

Po uporabi vseh predlog uporabimo pravilnik ILM in začnemo spremljati življenjsko dobo indeksov.

Dolgoročno shranjevanje podatkov v Elasticsearch

Dolgoročno shranjevanje podatkov v Elasticsearch

Dolgoročno shranjevanje podatkov v Elasticsearch

PRIDOBITE _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" : { }
          }
        }
      }
    }
  }
}

Težave

Prišlo je do težav v fazi namestitve in odpravljanja napak.

Vroča faza

Za pravilno rotacijo indeksov, prisotnost na koncu index_name-date-000026 formatne številke 000001. V kodi so vrstice, ki na koncu preverjajo indekse z uporabo regularnega izraza za številke. V nasprotnem primeru bo prišlo do napake, politike ne bodo uporabljene za indeks in bo vedno v vroči fazi.

Topla faza

Shrink (rezanje) - zmanjšanje števila drobcev, ker imamo 4 vozlišča v topli in hladni fazi.V dokumentaciji so naslednje vrstice:

  • Indeks mora biti samo za branje.
  • Kopija vsakega drobca v indeksu mora biti v istem vozlišču.
  • Zdravstveno stanje grozda mora biti zeleno.

Za obrezovanje indeksa Elasticsearch premakne vse primarne drobce v eno vozlišče, podvoji obrezani indeks s potrebnimi parametri in nato izbriše starega. Parameter total_shards_per_node mora biti enako ali večje od števila glavnih drobcev, da se jih prilega enemu vozlišču. V nasprotnem primeru bodo obvestila in drobci se ne bodo premaknili na zahtevana vozlišča.

Dolgoročno shranjevanje podatkov v Elasticsearch
Dolgoročno shranjevanje podatkov v Elasticsearch

GET /shrink-k8s-ingress-2020.06.06-000025/_settings

{
  "shrink-k8s-ingress-2020.06.06-000025" : {
    "settings" : {
      "index" : {
        "refresh_interval" : "5s",
        "auto_expand_replicas" : "0-1",
        "blocks" : {
          "write" : "true"
        },
        "provided_name" : "shrink-k8s-ingress-2020.06.06-000025",
        "creation_date" : "1592225525569",
        "priority" : "100",
        "number_of_replicas" : "1",
        "uuid" : "psF4MiFGQRmi8EstYUQS4w",
        "version" : {
          "created" : "7060299",
          "upgraded" : "7060299"
        },
        "lifecycle" : {
          "name" : "k8s-ingress",
          "rollover_alias" : "k8s-ingress",
          "indexing_complete" : "true"
        },
        "codec" : "best_compression",
        "routing" : {
          "allocation" : {
            "initial_recovery" : {
              "_id" : "_Le0Ww96RZ-o76bEPAWWag"
            },
            "require" : {
              "_id" : null,
              "box_type" : "cold"
            },
            "total_shards_per_node" : "8"
          }
        },
        "number_of_shards" : "4",
        "routing_partition_size" : "1",
        "resize" : {
          "source" : {
            "name" : "k8s-ingress-2020.06.06-000025",
            "uuid" : "gNhYixO6Skqi54lBjg5bpQ"
          }
        }
      }
    }
  }
}

Hladna faza

Zamrzne (zamrznitev) - zamrznemo indeks za optimizacijo poizvedb na podlagi zgodovinskih podatkov.

Iskanja, izvedena na zamrznjenih indeksih, uporabljajo majhen, namenski niz niti z omejenim iskanjem za nadzor števila sočasnih iskanj, ki zadenejo zamrznjene drobce na vsakem vozlišču. To omejuje količino dodatnega pomnilnika, potrebnega za prehodne podatkovne strukture, ki ustrezajo zamrznjenim delcem, kar posledično ščiti vozlišča pred prekomerno porabo pomnilnika.
Zamrznjeni indeksi so samo za branje: vanje ne morete indeksirati.
Pričakuje se, da bodo iskanja po zamrznjenih indeksih potekala počasi. Zamrznjeni indeksi niso namenjeni veliki iskalni obremenitvi. Možno je, da iskanje po zamrznjenem indeksu traja nekaj sekund ali minut, tudi če so se ista iskanja končala v milisekundah, ko indeksi niso bili zamrznjeni.

Rezultati

Naučili smo se pripraviti vozlišča za delo z ILM, nastaviti predlogo za distribucijo shardov med vročimi vozlišči in konfigurirati ILM za indeks z vsemi življenjskimi fazami.

Uporabne povezave

Vir: www.habr.com