Dugotrajno skladištenje podataka u Elasticsearch-u

Dugotrajno skladištenje podataka u Elasticsearch-u

Moje ime je Igor Sidorenko, ja sam tehnički vođa u timu admina koji održava kompletnu infrastrukturu Domclicka.

Želim podijeliti svoje iskustvo u postavljanju distribuirane pohrane podataka u Elasticsearch. Pogledaćemo koja su podešavanja na čvorovima odgovorna za distribuciju šarda, kako ILM radi i radi.

Oni koji rade sa trupcima, na ovaj ili onaj način, suočavaju se s problemom dugotrajnog skladištenja za kasniju analizu. U Elasticsearch-u je to posebno tačno, jer je sve bilo nesretno sa funkcionalnošću kustosa. Verzija 6.6 uvela je ILM funkcionalnost. Sastoji se od 4 faze:

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

Dato

  • 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 politika (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 glavne / 1 replika).
  • 90 dana - hladna faza (indeks zamrzavanja 4 glavne / 1 replika).
  • 120 dana - Brisanje faze.

Postavljanje Elasticsearch-a

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

  • Vruć-č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 Logstash-a

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

  1. Logstash preuzima dnevnike od Kafke. Može se pokupiti čisto ili pretvoriti na vašoj strani.
  2. Nešto samo piše u Elasticsearch, na primjer, APM server.

Razmotrite primjer upravljanja indeksima putem Logstash-a. Kreira indeks i primjenjuje se na njega indeksni obrazac i odgovarajući VRIJEME.

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 setup

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

Dugotrajno skladištenje podataka u Elasticsearch-u
Dugotrajno skladištenje podataka u Elasticsearch-u

GET_template/default

{
  "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-* korištenjem šablona veće težine.

Dugotrajno skladištenje podataka u Elasticsearch-u
Dugotrajno skladištenje podataka u Elasticsearch-u

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 šablona, ​​primjenjujemo ILM politiku i počinjemo pratiti životni vijek indeksa.

Dugotrajno skladištenje podataka u Elasticsearch-u

Dugotrajno skladištenje podataka u Elasticsearch-u

Dugotrajno skladištenje podataka u Elasticsearch-u

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

Problemi

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

Vruća faza

Za ispravnu rotaciju indeksa, prisustvo na kraju je kritično index_name-date-000026 formatirajte brojeve 000001. Postoje redovi u kodu koji provjeravaju indekse koristeći regularni izraz za prisustvo brojeva na kraju. U suprotnom će doći do greške, nikakve politike neće biti primijenjene na indeks i on će uvijek biti u vrućoj fazi.

Topla faza

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

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

Da bi smanjio indeks, Elasticsearch premješta sve primarne dijelove na 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 dijelova da stane na jedan čvor. U suprotnom će biti obavještenja i dijelovi se neće premjestiti na ispravne čvorove.

Dugotrajno skladištenje podataka u Elasticsearch-u
Dugotrajno skladištenje podataka u Elasticsearch-u

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

zamrzavanje (zamrzavanje) - Zamrzavamo indeks da bismo optimizirali upite o istorijskim podacima.

Pretrage koje se izvode na zamrznutim indeksima koriste mali, namjenski, search_throttled threadpool za kontrolu broja istovremenih pretraga koje pogađaju zamrznute dijelove na svakom čvoru. Ovo ograničava količinu dodatne memorije potrebne za prolazne strukture podataka koje odgovaraju zamrznutim dijelovima, što posljedično štiti čvorove od prekomjerne potrošnje memorije.
Zamrznuti indeksi su samo za čitanje: ne možete indeksirati u njih.
Očekuje se da će pretrage na zamrznutim indeksima biti sporo. Zamrznuti indeksi nisu namijenjeni za veliko opterećenje pretraživanja. Moguće je da pretraga zamrznutog indeksa može potrajati nekoliko sekundi ili minuta da se završi, čak i ako su ista pretraživanja završena u milisekundama kada indeksi nisu bili zamrznuti.

Ishodi

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

korisni linkovi

izvor: www.habr.com