Elasticsearch-də uzunmüddətli məlumatların saxlanması

Elasticsearch-də uzunmüddətli məlumatların saxlanması

Mənim adım İqor Sidorenko, mən Domclick-in bütün infrastrukturunu qoruyan adminlər komandasında texniki liderəm.

Elasticsearch-də paylanmış məlumat yaddaşının qurulması ilə bağlı təcrübəmi bölüşmək istəyirəm. Biz qovşaqlarda hansı parametrlərin qırıqların paylanmasına cavabdeh olduğunu, ILM-nin necə işlədiyini və işlədiyini nəzərdən keçirəcəyik.

Günlüklərlə işləyənlər, bu və ya digər şəkildə, sonrakı təhlil üçün uzunmüddətli saxlama problemi ilə üzləşirlər. Elasticsearch-də bu xüsusilə doğrudur, çünki kuratorun funksionallığı ilə hər şey uğursuz idi. Versiya 6.6 ILM funksionallığını təqdim etdi. 4 mərhələdən ibarətdir:

  • İsti - İndeks aktiv şəkildə yenilənir və sorğulanır.
  • İsti - İndeks artıq yenilənmir, lakin hələ də sorğulanır.
  • Soyuq - İndeks artıq yenilənmir və nadir hallarda sorğulanır. Məlumat hələ də axtarıla bilən olmalıdır, lakin sorğular daha yavaş ola bilər.
  • Sil - indeks artıq lazım deyil və təhlükəsiz şəkildə silinə bilər.

verilmiş

  • Elasticsearch Data Hot: 24 prosessor, 128 GB yaddaş, 1,8 TB SSD RAID 10 (8 qovşaq).
  • Elasticsearch Data Warm: 24 prosessor, 64 GB yaddaş, 8 TB NetApp SSD Siyasəti (4 qovşaq).
  • Elasticsearch Data Cold: 8 prosessor, 32 GB yaddaş, 128 TB HDD RAID 10 (4 qovşaq).

Məqsəd

Bu parametrlər fərdi, hamısı qovşaqlardakı yerdən, indekslərin sayından, qeydlərdən və s. Gündə 2-3 TB məlumatımız var.

  • 5 gün - İsti mərhələ (8 əsas / 1 replika).
  • 20 gün - İsti faza (daralma indeksi 4 əsas / 1 replika).
  • 90 gün - Soyuq faza (donma indeksi 4 əsas / 1 replika).
  • 120 gün - Fazanı silin.

Elasticsearch qurulur

Parçanı qovşaqlar arasında yaymaq üçün sizə yalnız bir parametr lazımdır:

  • isti- qovşaqlar:
    ~]# cat /etc/elasticsearch/elasticsearch.yml | grep attr
    # Add custom attributes to the node:
    node.attr.box_type: hot
  • Isti- qovşaqlar:
    ~]# cat /etc/elasticsearch/elasticsearch.yml | grep attr
    # Add custom attributes to the node:
    node.attr.box_type: warm
  • Soyuq- qovşaqlar:
    ~]# cat /etc/elasticsearch/elasticsearch.yml | grep attr
    # Add custom attributes to the node:
    node.attr.box_type: cold

Logstash qurulur

Bütün bunlar necə işləyir və biz bu funksiyanı necə həyata keçirdik? Elasticsearch-ə logları daxil etməklə başlayaq. İki yol var:

  1. Logstash, Kafkadan logları gətirir. Təmiz ala və ya tərəfinizdə çevirə bilər.
  2. Elasticsearch-ə nəyinsə özü yazır, məsələn, APM serveri.

Logstash vasitəsilə indeksləri idarə etmək nümunəsini nəzərdən keçirin. O, indeks yaradır və ona tətbiq edilir indeks nümunəsi və müvafiq 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 quraşdırma

Bütün yeni indekslərə aid olan əsas nümunə var. İsti indekslərin paylanmasını, qırıntıların sayını, replikaları və s. Şablonun çəkisi seçimlə müəyyən edilir order. Daha yüksək çəkiyə malik şablonlar mövcud şablon parametrlərini ləğv edir və ya yenilərini əlavə edir.

Elasticsearch-də uzunmüddətli məlumatların saxlanması
Elasticsearch-də uzunmüddətli məlumatların saxlanması

_şablon/defolt əldə edin

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

Sonra xəritələşdirməni indekslərə tətbiq edin k8s-ingress-* daha yüksək çəki ilə şablondan istifadə edin.

Elasticsearch-də uzunmüddətli məlumatların saxlanması
Elasticsearch-də uzunmüddətli məlumatların saxlanması

_şablon/k8s-giriş əldə edin

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

Bütün şablonları tətbiq etdikdən sonra biz ILM siyasətini tətbiq edirik və indekslərin ömrünü izləməyə başlayırıq.

Elasticsearch-də uzunmüddətli məlumatların saxlanması

Elasticsearch-də uzunmüddətli məlumatların saxlanması

Elasticsearch-də uzunmüddətli məlumatların saxlanması

_ilm/policy/k8s-ingress əldə edin

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

Problemləri

Quraşdırma və sazlama mərhələsində problemlər var idi.

İsti mərhələ

İndekslərin düzgün fırlanması üçün sonunda olması vacibdir index_name-date-000026 format nömrələri 000001. Kodun sonunda nömrələrin olması üçün müntəzəm ifadədən istifadə edərək indeksləri yoxlayan sətirlər var. Əks halda, xəta olacaq, indeksə heç bir siyasət tətbiq edilməyəcək və o, həmişə qaynar mərhələdə olacaq.

İsti mərhələ

Büzülmək (kəsmə) — qırıqların sayının azaldılması, çünki bizdə isti və soyuq fazalarda 4 qovşaq var.Sənədlərdə aşağıdakı sətirlər var:

  • İndeks yalnız oxumaq üçün olmalıdır.
  • İndeksdəki hər bir parçanın surəti eyni qovşaqda yerləşməlidir.
  • Klasterin sağlamlıq vəziyyəti yaşıl olmalıdır.

İndeksi kəsmək üçün Elasticsearch bütün əsas parçaları bir nodeya köçürür, kəsilmiş indeksi lazımi parametrlərlə təkrarlayır və sonra köhnəsini silir. Parametr total_shards_per_node bir node sığdırmaq üçün əsas parça sayına bərabər və ya ondan çox olmalıdır. Əks halda, bildirişlər olacaq və qırıntılar düzgün qovşaqlara keçməyəcək.

Elasticsearch-də uzunmüddətli məlumatların saxlanması
Elasticsearch-də uzunmüddətli məlumatların saxlanması

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

Soyuq faza

Dondurmaq (dondurun) - Tarixi məlumatlar üzrə sorğuları optimallaşdırmaq üçün indeksi dondururuq.

Dondurulmuş indekslərdə həyata keçirilən axtarışlar hər bir qovşaqda dondurulmuş qırıntıları vuran paralel axtarışların sayını idarə etmək üçün kiçik, xüsusi, search_throttled threadpool-dan istifadə edir. Bu, dondurulmuş qırıqlara uyğun gələn keçici məlumat strukturları üçün tələb olunan əlavə yaddaşın miqdarını məhdudlaşdırır və nəticədə qovşaqları həddindən artıq yaddaş istehlakından qoruyur.
Dondurulmuş indekslər yalnız oxunur: siz onları indeksləyə bilməzsiniz.
Dondurulmuş indekslər üzrə axtarışların yavaş-yavaş icra ediləcəyi gözlənilir. Dondurulmuş indekslər yüksək axtarış yükü üçün nəzərdə tutulmayıb. Ola bilsin ki, dondurulmuş indeksin axtarışı hətta indekslər dondurulmadıqda eyni axtarışlar millisaniyələrlə tamamlansa belə, tamamlanması saniyələr və ya dəqiqələr çəkə bilər.

Nəticələri

Biz ILM ilə işləmək üçün qovşaqları necə hazırlamağı, qaynar qovşaqlar arasında parçaları paylamaq üçün şablon qurmağı və həyatın bütün mərhələləri ilə indeks üçün ILM qurmağı öyrəndik.

Faydalı linklər

Mənbə: www.habr.com