Dugoročna pohrana podataka u Elasticsearchu

Dugoročna pohrana podataka u Elasticsearchu

Zovem se Igor Sidorenko, tehnički sam vođa u timu admina koji održava cjelokupnu infrastrukturu Domclick-a.

Želim podijeliti svoje iskustvo u postavljanju distribuirane pohrane podataka u Elasticsearchu. Pogledat ćemo koje su postavke na čvorovima odgovorne za distribuciju shardova, kako ILM radi i radi.

Oni koji rade s trupcima, na ovaj ili onaj način, suočavaju se s problemom dugotrajnog skladištenja za kasniju analizu. U Elasticsearchu je to osobito istinito, jer je sve bilo nesretno s funkcijom kustosa. Verzija 6.6 uvela je ILM funkcionalnost. Sastoji se od 4 faze:

  • Vruće - Indeks se aktivno ažurira i ispituje.
  • Toplo - Indeks se više ne ažurira, ali se još uvijek ispituje.
  • Hladno - Indeks se više ne ažurira i rijetko se postavljaju upiti. Informacije i dalje moraju biti pretražive, ali upiti mogu biti sporiji.
  • Izbriši - Indeks više nije potreban i može se sigurno izbrisati.

S obzirom na to

  • Elasticsearch Data Hot: 24 procesora, 128 GB memorije, 1,8 TB SSD RAID 10 (8 čvorova).
  • Elasticsearch Data Warm: 24 procesora, 64 GB memorije, 8 TB NetApp SSD Policy (4 čvora).
  • Elasticsearch Data Cold: 8 procesora, 32 GB memorije, 128 TB HDD RAID 10 (4 čvora).

Cilj

Ove postavke su individualne, sve ovisi o mjestu na čvorovima, broju indeksa, dnevnika itd. Imamo 2-3 TB podataka dnevno.

  • 5 dana - Vruća faza (8 glavnih / 1 replika).
  • 20 dana - Topla faza (indeks skupljanja 4 glavna / 1 replika).
  • 90 dana - hladna faza (indeks smrzavanja 4 glavna / 1 replika).
  • 120 dana - faza brisanja.

Postavljanje Elasticsearch-a

Za distribuciju sharda po čvorovima potreban vam je samo jedan parametar:

  • Aktuelne-čvorovi:
    ~]# cat /etc/elasticsearch/elasticsearch.yml | grep attr
    # Add custom attributes to the node:
    node.attr.box_type: hot
  • Topao-čvorovi:
    ~]# cat /etc/elasticsearch/elasticsearch.yml | grep attr
    # Add custom attributes to the node:
    node.attr.box_type: warm
  • Hladan-čvorovi:
    ~]# cat /etc/elasticsearch/elasticsearch.yml | grep attr
    # Add custom attributes to the node:
    node.attr.box_type: cold

Postavljanje Logstasha

Kako sve to funkcionira i kako smo implementirali ovu značajku? Počnimo s unosom logova u Elasticsearch. Postoje dva načina:

  1. Logstash dohvaća trupce od Kafke. Može pokupiti čist ili pretvoriti na svoju stranu.
  2. Nešto samo piše u Elasticsearch, na primjer, APM poslužitelj.

Razmotrite primjer upravljanja indeksima putem Logstasha. Stvara indeks i primjenjuje se na njega uzorak indeksa i odgovarajuće 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
    }
}

Postavljanje Kibane

Postoji osnovni obrazac koji se primjenjuje na sve nove indekse. Postavlja distribuciju vrućih indeksa, broj shardova, replika itd. Težina predloška određena je opcijom order. Predlošci s većom težinom nadjačavaju postojeće parametre predloška ili dodaju nove.

Dugoročna pohrana podataka u Elasticsearchu
Dugoročna pohrana podataka u Elasticsearchu

GET_predložak/zadano

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

Zatim primijenite mapiranje na indekse k8s-ingress-* koristeći šablonu s većom težinom.

Dugoročna pohrana podataka u Elasticsearchu
Dugoročna pohrana podataka u Elasticsearchu

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

Nakon primjene svih predložaka, primjenjujemo ILM politiku i počinjemo pratiti životni vijek indeksa.

Dugoročna pohrana podataka u Elasticsearchu

Dugoročna pohrana podataka u Elasticsearchu

Dugoročna pohrana podataka u Elasticsearchu

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

Problemi

Bilo je problema u fazi postavljanja i otklanjanja pogrešaka.

Vruća faza

Za ispravnu rotaciju indeksa, prisutnost na kraju je kritična index_name-date-000026 format brojeva 000001. Postoje linije u kodu koje provjeravaju indekse pomoću regularnog izraza za prisutnost brojeva na kraju. U protivnom će doći do pogreške, na indeks se neće primjenjivati ​​pravila i on će uvijek biti u vrućoj fazi.

Topla faza

Psihijatar (cutoff) — smanjenje broja fragmenata, jer imamo 4 čvora u toploj i hladnoj fazi. Dokumentacija sadrži sljedeće retke:

  • Indeks mora biti samo za čitanje.
  • Kopija svakog šarda u indeksu mora se nalaziti na istom čvoru.
  • Zdravstveni status klastera mora biti zelen.

Za smanjenje indeksa, Elasticsearch premješta sve primarne šardove u jedan čvor, duplicira skraćeni indeks s potrebnim parametrima, a zatim briše stari. Parametar total_shards_per_node mora biti jednak ili veći od broja glavnih fragmenata da bi stao na jedan čvor. U protivnom će se pojaviti obavijesti i krhotine se neće premjestiti na ispravne čvorove.

Dugoročna pohrana podataka u Elasticsearchu
Dugoročna pohrana podataka u Elasticsearchu

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

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

Zamrznuti (zamrzavanje) - Zamrzavamo indeks kako bismo optimizirali upite na povijesnim podacima.

Pretrage koje se izvode na zamrznutim indeksima koriste mali, namjenski, search_throttled skup niti za kontrolu broja istodobnih pretraživanja koja pogađaju zamrznute krhotine na svakom čvoru. Ovo ograničava količinu dodatne memorije potrebne za prolazne podatkovne strukture koje odgovaraju zamrznutim krhotinama, što posljedično štiti čvorove od prekomjerne potrošnje memorije.
Zamrznuti indeksi su samo za čitanje: ne možete ih indeksirati.
Očekuje se da će se pretraživanja na zamrznutim indeksima izvršavati sporo. Zamrznuti indeksi nisu namijenjeni velikom opterećenju pretraživanja. Moguće je da pretraživanje zamrznutog indeksa može potrajati nekoliko sekundi ili minuta, čak i ako su ista pretraživanja završila u milisekundama kada indeksi nisu bili zamrznuti.

Rezultati

Naučili smo kako pripremiti čvorove za rad s ILM-om, postaviti predložak za distribuciju shardova među vrućim čvorovima te postaviti ILM za indeks sa svim fazama života.

korisni linkovi

Izvor: www.habr.com