Ruajtja afatgjatë e të dhënave në Elasticsearch

Ruajtja afatgjatë e të dhënave në Elasticsearch

Unë quhem Igor Sidorenko, unë jam një drejtues teknik në një ekip administratorësh që mirëmbajnë të gjithë infrastrukturën Domklik në gjendje pune.

Dëshiroj të ndaj përvojën time në vendosjen e ruajtjes së të dhënave të shpërndara në Elasticsearch. Ne do të shikojmë se cilat cilësime në nyjet janë përgjegjëse për shpërndarjen e copëzave, si strukturohet dhe funksionon ILM.

Ata që punojnë me shkrime janë përballur në një mënyrë ose në një tjetër me problemin e ruajtjes afatgjatë për analiza të mëvonshme. Kjo është veçanërisht e vërtetë në Elasticsearch sepse funksionaliteti i kuratorit ka qenë i mjerueshëm. Në versionin 6.6, u shfaq funksionaliteti ILM. Ai përbëhet nga 4 faza:

  • Hot—Indeksi po përditësohet dhe pyetet në mënyrë aktive.
  • Ngrohtë - indeksi nuk përditësohet më, por ende kërkohet.
  • Ftohtë - Indeksi nuk përditësohet më dhe pyetet rrallë. Informacioni duhet të jetë ende i kërkueshëm, por pyetjet mund të jenë më të ngadalta.
  • Fshi - Indeksi nuk është më i nevojshëm dhe mund të fshihet në mënyrë të sigurt.

E dhënë

  • Elasticsearch Data Hot: 24 procesorë, 128 GB memorie, 1,8 TB SSD RAID 10 (8 nyje).
  • Elasticsearch Data Warm: 24 procesorë, 64 GB memorie, 8 TB NetApp SSD Policy (4 nyje).
  • Elasticsearch Data Cold: 8 procesorë, 32 GB memorie, 128 TB HDD RAID 10 (4 nyje).

Qëllim

Këto cilësime janë individuale, gjithçka varet nga hapësira në nyje, numri i indekseve, regjistrat, etj. Për ne kjo është 2-3 TB të dhëna në ditë.

  • 5 ditë - Faza e nxehtë (8 kryesore / 1 kopje).
  • 20 ditë - Faza e ngrohtë (tkurrje-indeks 4 kryesore / 1 kopje).
  • 90 ditë - Faza e ftohtë (ngrirje-indeks 4 kryesore / 1 kopje).
  • 120 ditë - Faza e fshirjes.

Vendosja e Elasticsearch

Për të shpërndarë copëza midis nyjeve, ju duhet vetëm një parametër:

  • nxehtë-nyjet:
    ~]# cat /etc/elasticsearch/elasticsearch.yml | grep attr
    # Add custom attributes to the node:
    node.attr.box_type: hot
  • I ngrohtë-nyjet:
    ~]# cat /etc/elasticsearch/elasticsearch.yml | grep attr
    # Add custom attributes to the node:
    node.attr.box_type: warm
  • Të ftohtë-nyjet:
    ~]# cat /etc/elasticsearch/elasticsearch.yml | grep attr
    # Add custom attributes to the node:
    node.attr.box_type: cold

Konfigurimi i Logstash

Si funksionon e gjithë kjo dhe si e zbatuam këtë funksion? Le të fillojmë me futjen e regjistrave në Elasticsearch. Ka dy mënyra:

  1. Logstash merr shkrimet nga Kafka. Mund të marrë të pastër ose të konvertohet në anën e saj.
  2. Diçka i shkruan vetë Elasticsearch, për shembull, një server APM.

Le të shohim një shembull të menaxhimit të indekseve përmes Logstash. Krijon një indeks dhe zbatohet shabllonin e indeksit dhe përkatëse 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
    }
}

Vendosja e Kibana

Ekziston një shabllon bazë që vlen për të gjithë indekset e reja. Përcakton shpërndarjen e indekseve të nxehta, numrin e copëzave, kopjeve, etj. Pesha e shabllonit përcaktohet nga opsioni order. Modelet me pesha më të larta anashkalojnë parametrat ekzistues të shabllonit ose shtojnë të rinj.

Ruajtja afatgjatë e të dhënave në Elasticsearch
Ruajtja afatgjatë e të dhënave në Elasticsearch

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

Më pas aplikoni hartën e indekseve k8s-ingress-* duke përdorur një shabllon me peshë më të madhe.

Ruajtja afatgjatë e të dhënave në Elasticsearch
Ruajtja afatgjatë e të dhënave në 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" : { }
  }
}

Pas aplikimit të të gjithë shablloneve, ne aplikojmë politikën ILM dhe fillojmë të monitorojmë jetëgjatësinë e indekseve.

Ruajtja afatgjatë e të dhënave në Elasticsearch

Ruajtja afatgjatë e të dhënave në Elasticsearch

Ruajtja afatgjatë e të dhënave në 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" : { }
          }
        }
      }
    }
  }
}

Problemet

Kishte probleme në fazën e konfigurimit dhe korrigjimit.

Faza e nxehtë

Për rrotullimin e saktë të indekseve, prania në fund të index_name-date-000026 numrat e formatit 000001. Ka rreshta në kod që kontrollojnë indekset duke përdorur një shprehje të rregullt për numrat në fund. Përndryshe, do të ketë një gabim, nuk do të aplikohen politika për indeksin dhe ai do të jetë gjithmonë në fazën e nxehtë.

Faza e ngrohtë

Tkurr (prerja) - zvogëlimi i numrit të copave, sepse kemi 4 nyje në fazën e ngrohtë dhe të ftohtë.Dokumentacioni përmban këto rreshta:

  • Indeksi duhet të jetë vetëm për lexim.
  • Një kopje e çdo copëze në indeks duhet të qëndrojë në të njëjtën nyje.
  • Gjendja shëndetësore e grupit duhet të jetë e gjelbër.

Për të shkurtuar një indeks, Elasticsearch zhvendos të gjitha copëzat kryesore në një nyje, kopjon indeksin e shkurtuar me parametrat e nevojshëm dhe më pas fshin atë të vjetër. Parametri total_shards_per_node duhet të jetë i barabartë ose më i madh se numri i copëzave kryesore në mënyrë që të vendosen në një nyje. Përndryshe, do të ketë njoftime dhe copëzat nuk do të lëvizin në nyjet e kërkuara.

Ruajtja afatgjatë e të dhënave në Elasticsearch
Ruajtja afatgjatë e të dhënave në 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"
          }
        }
      }
    }
  }
}

Faza e ftohtë

Freeze (ngrirje) - ne ngrijmë indeksin për të optimizuar pyetjet bazuar në të dhënat historike.

Kërkimet e kryera në indekset e ngrira përdorin grupin e vogël, të dedikuar, search_throttled për të kontrolluar numrin e kërkimeve të njëkohshme që godasin copëza të ngrira në secilën nyje. Kjo kufizon sasinë e memories shtesë që kërkohet për strukturat kalimtare të të dhënave që korrespondojnë me copëzat e ngrira, gjë që rrjedhimisht mbron nyjet nga konsumimi i tepërt i memories.
Indekset e ngrira janë vetëm për lexim: nuk mund të indeksoheni në to.
Kërkimet në indekset e ngrira pritet të ekzekutohen ngadalë. Indekset e ngrira nuk janë të destinuara për ngarkesë të lartë kërkimi. Është e mundur që një kërkim i një indeksi të ngrirë mund të marrë sekonda ose minuta për të përfunduar, edhe nëse të njëjtat kërkime përfundojnë në milisekonda kur indekset nuk ishin të ngrira.

Rezultatet e

Mësuam se si të përgatisim nyjet për të punuar me ILM, vendosëm një shabllon për shpërndarjen e copëzave midis nyjeve të nxehta dhe konfiguruam ILM për një indeks me të gjitha fazat e jetës.

Lidhje të dobishme

Burimi: www.habr.com