Longperspektiva datumstokado en Elasticsearch

Longperspektiva datumstokado en Elasticsearch

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.

Donita

  • Elasticsearch Data Hot: 24 procesoroj, 128 GB-memoro, 1,8 TB SSD RAID 10 (8 nodoj).
  • Elasticsearch Data Warm: 24 procesoroj, 64 GB-memoro, 8 TB NetApp SSD-Politiko (4 nodoj).
  • Elasticsearch Data Cold: 8 procesoroj, 32 GB-memoro, 128 TB HDD RAID 10 (4 nodoj).

Golo

Ĉi tiuj agordoj estas individuaj, ĉio dependas de la loko sur la nodoj, la nombro da indeksoj, protokoloj, ktp. Ni havas 2-3 TB da datumoj tage.

  • 5 tagoj - Varma fazo (8 ĉefaj / 1 kopio).
  • 20 tagoj - Varma fazo (ŝrumpi-indekso 4 ĉefaj / 1 kopio).
  • 90 tagoj - Malvarma fazo (frosti-indekso 4 ĉefaj / 1 kopio).
  • 120 tagoj - Forigi fazon.

Agordi Elasticsearch

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:

  1. Logstash prenas protokolojn de Kafka. Povas preni purigi aŭ konverti sur via flanko.
  2. 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.

Longperspektiva datumstokado en Elasticsearch
Longperspektiva datumstokado en Elasticsearch

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.

Longperspektiva datumstokado en Elasticsearch
Longperspektiva datumstokado en Elasticsearch

GET _ŝablono/k8s-eniro

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

Post aplikado de ĉiuj ŝablonoj, ni aplikas la ILM-politikon kaj komencas monitori la vivon de la indeksoj.

Longperspektiva datumstokado en Elasticsearch

Longperspektiva datumstokado en Elasticsearch

Longperspektiva datumstokado en Elasticsearch

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.

Longperspektiva datumstokado en Elasticsearch
Longperspektiva datumstokado en 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"
          }
        }
      }
    }
  }
}

Malvarma fazo

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.

utilaj ligoj

fonto: www.habr.com