Ilgtermiņa datu glabāšana Elasticsearch

Ilgtermiņa datu glabāšana Elasticsearch

Mani sauc Igors Sidorenko, esmu tehniskais vadītājs adminu komandā, kas uztur visu Domclick infrastruktūru.

Es vēlos dalīties savā pieredzē par izkliedētās datu krātuves iestatīšanu Elasticsearch. Apskatīsim, kādi mezglu iestatījumi ir atbildīgi par shardu izplatīšanu, kā darbojas un darbojas ILM.

Tie, kas strādā ar apaļkokiem, vienā vai otrā veidā saskaras ar ilgtermiņa uzglabāšanas problēmu vēlākai analīzei. Elasticsearchā tas ir īpaši aktuāli, jo ar kuratora funkcionalitāti viss bija neveiksmīgi. Versijā 6.6 tika ieviesta ILM funkcionalitāte. Tas sastāv no 4 fāzēm:

  • Karsts — indekss tiek aktīvi atjaunināts un vaicāts.
  • Silts — indekss vairs netiek atjaunināts, taču joprojām tiek vaicāts.
  • Auksts — indekss vairs netiek atjaunināts un tiek reti pieprasīts. Informācijai joprojām ir jābūt meklējamai, taču vaicājumi var būt lēnāki.
  • Dzēst — indekss vairs nav vajadzīgs, un to var droši dzēst.

Ņemot vērā

  • Elasticsearch Data Hot: 24 procesori, 128 GB atmiņa, 1,8 TB SSD RAID 10 (8 mezgli).
  • Elasticsearch Data Warm: 24 procesori, 64 GB atmiņa, 8 TB NetApp SSD politika (4 mezgli).
  • Elasticsearch Data Cold: 8 procesori, 32 GB atmiņa, 128 TB HDD RAID 10 (4 mezgli).

mērķis

Šie iestatījumi ir individuāli, tas viss ir atkarīgs no vietas uz mezgliem, indeksu skaita, žurnālu utt. Mums ir 2–3 TB datu dienā.

  • 5 dienas - karstā fāze (8 galvenās / 1 kopija).
  • 20 dienas - siltā fāze (saraušanās indekss 4 galvenās / 1 kopija).
  • 90 dienas – aukstā fāze (sasalšanas indekss 4 galvenās / 1 kopija).
  • 120 dienas — Dzēst fāzi.

Elasticsearch iestatīšana

Lai sadalītu fragmentu pa mezgliem, ir nepieciešams tikai viens parametrs:

  • karsti-mezgli:
    ~]# cat /etc/elasticsearch/elasticsearch.yml | grep attr
    # Add custom attributes to the node:
    node.attr.box_type: hot
  • Siltie-mezgli:
    ~]# cat /etc/elasticsearch/elasticsearch.yml | grep attr
    # Add custom attributes to the node:
    node.attr.box_type: warm
  • vēsa-mezgli:
    ~]# cat /etc/elasticsearch/elasticsearch.yml | grep attr
    # Add custom attributes to the node:
    node.attr.box_type: cold

Logstash iestatīšana

Kā tas viss darbojas un kā mēs ieviesām šo funkciju? Sāksim ar žurnālu iegūšanu Elasticsearch. Ir divi veidi:

  1. Logstash atnes baļķus no Kafkas. Var paņemt tīru vai pārveidot jūsu pusē.
  2. Kaut kas pats raksta uz Elasticsearch, piemēram, APM serveris.

Apsveriet piemēru indeksu pārvaldībai, izmantojot Logstash. Tas izveido indeksu un attiecas uz to indeksa modelis un atbilstošs 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 uzstādīšana

Ir bāzes modelis, kas attiecas uz visiem jaunajiem indeksiem. Tas nosaka karsto indeksu sadalījumu, lauskas, kopiju skaitu utt. Veidnes svaru nosaka opcija order. Veidnes ar lielāku svaru ignorē esošos veidņu parametrus vai pievieno jaunus.

Ilgtermiņa datu glabāšana Elasticsearch
Ilgtermiņa datu glabāšana Elasticsearch

GET_veidne/noklusējums

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

Pēc tam piemērojiet kartēšanu indeksiem k8s-ingress-* izmantojot veidni ar lielāku svaru.

Ilgtermiņa datu glabāšana Elasticsearch
Ilgtermiņa datu glabāšana Elasticsearch

IEGŪT _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" : { }
  }
}

Pēc visu veidņu lietošanas mēs piemērojam ILM politiku un sākam uzraudzīt indeksu darbības laiku.

Ilgtermiņa datu glabāšana Elasticsearch

Ilgtermiņa datu glabāšana Elasticsearch

Ilgtermiņa datu glabāšana Elasticsearch

IEGŪT _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" : { }
          }
        }
      }
    }
  }
}

Problēmas

Iestatīšanas un atkļūdošanas posmā radās problēmas.

Karstā fāze

Pareizai indeksu rotācijai klātbūtne beigās ir kritiska index_name-date-000026 formāta skaitļi 000001. Kodā ir rindas, kas pārbauda indeksus, izmantojot regulāru izteiksmi, lai noskaidrotu, vai beigās ir skaitļi. Pretējā gadījumā radīsies kļūda, indeksam netiks piemērotas politikas, un tas vienmēr būs karstajā fāzē.

Siltā fāze

Sarauties (cutoff) - samaziniet šķembu skaitu, jo mums ir 4 mezgli siltajā un aukstajā fāzē. Dokumentācijā ir šādas rindas:

  • Indeksam ir jābūt tikai lasāmam.
  • Katras indeksa fragmenta kopijai ir jāatrodas tajā pašā mezglā.
  • Klastera veselības statusam jābūt zaļam.

Lai apgrieztu indeksu, Elasticsearch pārvieto visus primāros fragmentus uz vienu mezglu, dublē saīsināto indeksu ar nepieciešamajiem parametriem un pēc tam dzēš veco. Parametrs total_shards_per_node ir jābūt vienādam ar vai lielākam par galveno lauskas skaitu, lai tās ietilptu vienā mezglā. Pretējā gadījumā tiks parādīti paziņojumi, un lauskas netiks pārvietotas uz pareizajiem mezgliem.

Ilgtermiņa datu glabāšana Elasticsearch
Ilgtermiņa datu glabāšana 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"
          }
        }
      }
    }
  }
}

Aukstā fāze

Sasalt (iesaldēt) — mēs iesaldējam indeksu, lai optimizētu vēsturisko datu vaicājumus.

Meklēšanai, kas tiek veikta ar iesaldētajiem indeksiem, tiek izmantots mazs, speciāls, search_throttled pavedienu kopums, lai kontrolētu vienlaicīgu meklējumu skaitu, kas katrā mezglā trāpa iesaldētās lauskas. Tas ierobežo papildu atmiņas apjomu, kas nepieciešams īslaicīgām datu struktūrām, kas atbilst iesaldētajām lauskas, tādējādi aizsargājot mezglus pret pārmērīgu atmiņas patēriņu.
Fiksētie indeksi ir tikai lasāmi: tajos nevar indeksēt.
Paredzams, ka meklēšana iesaldētos indeksos tiks veikta lēni. Iesaldētie indeksi nav paredzēti lielai meklēšanas slodzei. Iespējams, ka iesaldēta indeksa meklēšana var ilgt sekundes vai minūtes, pat ja tā pati meklēšana tika pabeigta milisekundēs, kad indeksi nebija iesaldēti.

Rezultāti

Mēs uzzinājām, kā sagatavot mezglus darbam ar ILM, iestatīt veidni lauskas sadalīšanai starp karstajiem mezgliem un iestatīt ILM indeksam ar visām dzīves fāzēm.

Noderīgas saites

Avots: www.habr.com