Langsigtet datalagring i Elasticsearch

Langsigtet datalagring i Elasticsearch

Mit navn er Igor Sidorenko, jeg er en teknisk leder i teamet af administratorer, der vedligeholder hele Domclicks infrastruktur.

Jeg vil gerne dele min erfaring med at opsætte distribueret datalagring i Elasticsearch. Vi vil se på, hvilke indstillinger på noderne, der er ansvarlige for fordelingen af ​​shards, hvordan ILM fungerer og virker.

De, der arbejder med logs, på den ene eller anden måde, står over for problemet med langtidsopbevaring til senere analyse. I Elasticsearch gælder det især, fordi alt var uheldigt med kuratorfunktionaliteten. Version 6.6 introducerede ILM-funktionalitet. Den består af 4 faser:

  • Hot - Indekset bliver aktivt opdateret og forespurgt.
  • Varm - Indekset opdateres ikke længere, men bliver stadig forespurgt.
  • Kold - Indekset opdateres ikke længere og spørges sjældent til. Oplysningerne skal stadig være søgbare, men forespørgsler kan være langsommere.
  • Slet - Indekset er ikke længere nødvendigt og kan sikkert slettes.

Givet

  • Elasticsearch Data Hot: 24 processorer, 128 GB hukommelse, 1,8 TB SSD RAID 10 (8 noder).
  • Elasticsearch Data Warm: 24 processorer, 64 GB hukommelse, 8 TB NetApp SSD Policy (4 noder).
  • Elasticsearch Data Cold: 8 processorer, 32 GB hukommelse, 128 TB HDD RAID 10 (4 noder).

mål

Disse indstillinger er individuelle, det hele afhænger af stedet på noderne, antallet af indekser, logfiler osv. Vi har 2-3 TB data om dagen.

  • 5 dage - Varm fase (8 hoved / 1 replika).
  • 20 dage - Varm fase (krympe-indeks 4 hoved / 1 replika).
  • 90 dage - kold fase (fryse-indeks 4 hoved / 1 replika).
  • 120 dage - Slet fase.

Opsætning af Elasticsearch

For at fordele shards på tværs af noder behøver du kun én parameter:

  • varm-knuder:
    ~]# cat /etc/elasticsearch/elasticsearch.yml | grep attr
    # Add custom attributes to the node:
    node.attr.box_type: hot
  • Varm-knuder:
    ~]# cat /etc/elasticsearch/elasticsearch.yml | grep attr
    # Add custom attributes to the node:
    node.attr.box_type: warm
  • Forkølelse-knuder:
    ~]# cat /etc/elasticsearch/elasticsearch.yml | grep attr
    # Add custom attributes to the node:
    node.attr.box_type: cold

Opsætning af Logstash

Hvordan fungerer det hele, og hvordan implementerede vi denne funktion? Lad os starte med at få logs ind i Elasticsearch. Der er to måder:

  1. Logstash henter logs fra Kafka. Kan afhente rent eller konvertere på din side.
  2. Noget selv skriver til Elasticsearch, for eksempel en APM-server.

Overvej et eksempel på styring af indekser gennem Logstash. Det opretter et indeks og anvender det indeksmønster og tilsvarende 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 opsætning

Der er et basismønster, der gælder for alle nye indekser. Den indstiller fordelingen af ​​varme indekser, antallet af shards, replikaer osv. Skabelonens vægt bestemmes af muligheden order. Skabeloner med en højere vægt tilsidesætter eksisterende skabelonparametre eller tilføjer nye.

Langsigtet datalagring i Elasticsearch
Langsigtet datalagring i Elasticsearch

GET_skabelon/standard

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

Anvend derefter tilknytningen til indekserne k8s-ingress-* ved hjælp af en skabelon med en højere vægt.

Langsigtet datalagring i Elasticsearch
Langsigtet datalagring i Elasticsearch

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

Efter at have anvendt alle skabelonerne anvender vi ILM-politikken og begynder at overvåge indeksernes levetid.

Langsigtet datalagring i Elasticsearch

Langsigtet datalagring i Elasticsearch

Langsigtet datalagring i Elasticsearch

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

Problemer

Der var problemer på opsætnings- og fejlfindingsstadiet.

Varm fase

For den korrekte rotation af indekser er tilstedeværelsen i slutningen afgørende index_name-date-000026 formatere tal 000001. Der er linjer i koden, der kontrollerer indekser ved hjælp af et regulært udtryk for tilstedeværelsen af ​​tal i slutningen. Ellers vil der være en fejl, ingen politikker vil blive anvendt på indekset, og det vil altid være i den varme fase.

Varm fase

Shrink (cutoff) — reduktion af antallet af shards, fordi vi har 4 noder i den varme og kolde fase. Dokumentationen indeholder følgende linjer:

  • Indekset skal være skrivebeskyttet.
  • En kopi af hvert shard i indekset skal ligge på den samme node.
  • Klyngens sundhedsstatus skal være grøn.

For at beskære et indeks flytter Elasticsearch alle primære shards til én node, duplikerer det trunkerede indeks med de nødvendige parametre og sletter derefter det gamle. Parameter total_shards_per_node skal være lig med eller større end antallet af hovedskår for at passe på én knude. Ellers vil der være meddelelser, og shards vil ikke flytte til de korrekte noder.

Langsigtet datalagring i Elasticsearch
Langsigtet datalagring i 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"
          }
        }
      }
    }
  }
}

Kold fase

Frys (frys) - Vi fryser indekset for at optimere forespørgsler på historiske data.

Søgninger udført på frosne indekser bruger den lille, dedikerede, search_throttled threadpool til at kontrollere antallet af samtidige søgninger, der rammer frosne shards på hver node. Dette begrænser mængden af ​​ekstra hukommelse, der kræves til de transiente datastrukturer svarende til frosne shards, som følgelig beskytter noder mod for stort hukommelsesforbrug.
Frosne indekser er skrivebeskyttede: du kan ikke indeksere dem.
Søgninger på frosne indekser forventes at udføres langsomt. Frosne indekser er ikke beregnet til høj søgebelastning. Det er muligt, at en søgning på et fastfrosset indeks kan tage sekunder eller minutter at fuldføre, selvom de samme søgninger blev udført på millisekunder, når indeksene ikke blev frosset.

Resultaterne af

Vi lærte at forberede noder til at arbejde med ILM, opsætte en skabelon til fordeling af shards blandt hot noder og opsætte ILM til et indeks med alle livsfaser.

Nyttige links

Kilde: www.habr.com