Pikaajaline andmete salvestamine Elasticsearchis

Pikaajaline andmete salvestamine Elasticsearchis

Minu nimi on Igor Sidorenko, olen tehniline juht administraatorite meeskonnas, kes hooldab kogu Domclicki infrastruktuuri.

Tahan jagada oma kogemusi Elasticsearchis hajutatud andmesalvestuse seadistamisel. Vaatame, millised sõlmede sätted vastutavad kildude levitamise eest, kuidas ILM töötab ja töötab.

Need, kes töötavad ühel või teisel viisil palgiga, seisavad silmitsi pikaajalise ladustamise probleemiga hilisemaks analüüsiks. Elasticsearchis kehtib see eriti, sest kuraatori funktsionaalsusega oli kõik kahetsusväärne. Versioon 6.6 tutvustas ILM-i funktsioone. See koosneb 4 faasist:

  • Kuum – indeksit värskendatakse aktiivselt ja selle kohta tehakse päringuid.
  • Soe – indeksit enam ei värskendata, kuid päringuid tehakse endiselt.
  • Külm – indeksit enam ei uuendata ja päringuid tehakse harva. Teave peab siiski olema otsitav, kuid päringud võivad olla aeglasemad.
  • Kustuta – indeksit pole enam vaja ja selle saab turvaliselt kustutada.

Antud

  • Elasticsearch Data Hot: 24 protsessorit, 128 GB mälu, 1,8 TB SSD RAID 10 (8 sõlme).
  • Elasticsearch Data Warm: 24 protsessorit, 64 GB mälu, 8 TB NetApp SSD poliitika (4 sõlme).
  • Elasticsearch Data Cold: 8 protsessorit, 32 GB mälu, 128 TB HDD RAID 10 (4 sõlme).

Eesmärk

Need sätted on individuaalsed, kõik sõltub kohast sõlmedel, indeksite arvust, logidest jne. Meil on päevas 2-3 TB andmeid.

  • 5 päeva – kuum faas (8 peamist / 1 koopia).
  • 20 päeva – soe faas (kokkutõmbumisindeks 4 peamist / 1 koopia).
  • 90 päeva – külm faas (külmumisindeks 4 peamist / 1 koopia).
  • 120 päeva – faasi kustutamine.

Elasticsearchi seadistamine

Kildude jaotamiseks sõlmede vahel vajate ainult ühte parameetrit:

  • kuum-sõlmed:
    ~]# cat /etc/elasticsearch/elasticsearch.yml | grep attr
    # Add custom attributes to the node:
    node.attr.box_type: hot
  • Soe-sõlmed:
    ~]# cat /etc/elasticsearch/elasticsearch.yml | grep attr
    # Add custom attributes to the node:
    node.attr.box_type: warm
  • Külm-sõlmed:
    ~]# cat /etc/elasticsearch/elasticsearch.yml | grep attr
    # Add custom attributes to the node:
    node.attr.box_type: cold

Logstashi seadistamine

Kuidas see kõik töötab ja kuidas me seda funktsiooni rakendasime? Alustame logide hankimisest Elasticsearchi. On kaks võimalust.

  1. Logstash toob palke Kafkalt. Saab puhtalt korjata või küljelt ümber ehitada.
  2. Midagi ise kirjutab Elasticsearchile, näiteks APM-server.

Vaatleme näidet indeksite haldamisest Logstashi kaudu. See loob indeksi ja rakendab seda indeksi muster ja vastavad 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 seadistamine

Kõigi uute indeksite puhul kehtib põhimuster. See määrab kuumade indeksite jaotuse, kildude, koopiate jne arvu. Malli kaal määratakse valiku järgi order. Suurema kaaluga mallid alistavad olemasolevad malliparameetrid või lisavad uusi.

Pikaajaline andmete salvestamine Elasticsearchis
Pikaajaline andmete salvestamine Elasticsearchis

GET_mall/vaikeseade

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

Seejärel rakendage vastendus indeksitele k8s-ingress-* kasutades suurema kaaluga malli.

Pikaajaline andmete salvestamine Elasticsearchis
Pikaajaline andmete salvestamine Elasticsearchis

HANGI _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ärast kõigi mallide rakendamist rakendame ILM-i poliitikat ja hakkame jälgima indeksite eluiga.

Pikaajaline andmete salvestamine Elasticsearchis

Pikaajaline andmete salvestamine Elasticsearchis

Pikaajaline andmete salvestamine Elasticsearchis

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

Probleemid

Seadistamise ja silumise etapis esines probleeme.

Kuum faas

Indeksite õigeks pööramiseks on nende olemasolu lõpus kriitiline index_name-date-000026 numbrite vormingus 000001. Koodis on read, mis kontrollivad indekseid, kasutades regulaaravaldist, et kontrollida numbrite olemasolu lõpus. Vastasel juhul tekib tõrge, indeksile ei rakendata ühtegi poliitikat ja see on alati kuumas faasis.

Soe faas

Shrink (cutoff) - vähendage kildude arvu, kuna meil on soojas ja külmas faasis 4 sõlme. Dokumentatsioon sisaldab järgmisi ridu:

  • Indeks peab olema kirjutuskaitstud.
  • Iga indeksis sisalduva killu koopia peab asuma samas sõlmes.
  • Klastri tervislik seisund peab olema roheline.

Indeksi kärpimiseks liigutab Elasticsearch kõik esmased killud ühte sõlme, dubleerib kärbitud indeksi vajalike parameetritega ja seejärel kustutab vana. Parameeter total_shards_per_node peab olema võrdne või suurem kui peamiste kildude arv, et need mahuksid ühte sõlme. Vastasel juhul saadetakse märguandeid ja killud ei liigu õigetesse sõlmedesse.

Pikaajaline andmete salvestamine Elasticsearchis
Pikaajaline andmete salvestamine Elasticsearchis

HANKIGE /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"
          }
        }
      }
    }
  }
}

Külm faas

Külmutama (külmutage) – külmutame indeksi, et optimeerida päringuid ajalooliste andmete põhjal.

Külmutatud indeksitega tehtud otsingud kasutavad väikest, spetsiaalset, search_throttled lõime, et juhtida samaaegsete otsingute arvu, mis tabavad iga sõlme külmutatud kilde. See piirab külmutatud kildudele vastavate ajutiste andmestruktuuride jaoks vajaliku lisamälu mahtu, mis kaitseb solme liigse mälutarbimise eest.
Külmutatud indeksid on kirjutuskaitstud: te ei saa neisse indekseerida.
Eeldatakse, et külmutatud indeksite otsingud teostatakse aeglaselt. Külmutatud indeksid ei ole mõeldud suureks otsingukoormuseks. Võimalik, et külmutatud indeksi otsimine võib võtta sekundeid või minuteid, isegi kui samad otsingud lõppesid millisekunditega, kui indeksid ei olnud külmutatud.

Tulemused

Õppisime, kuidas valmistada ette sõlmed ILM-iga töötamiseks, seadistada mall kildude jaotamiseks kuumade sõlmede vahel ja seadistada ILM kõigi elufaasidega indeksi jaoks.

Kasulikud lingid

Allikas: www.habr.com