Дугорочно складиштење података у Еластицсеарцх-у

Дугорочно складиштење података у Еластицсеарцх-у

Моје име је Игор Сидоренко, ја сам технички вођа у тиму админа који одржавају целокупну инфраструктуру Домцлицк-а.

Желим да поделим своје искуство у постављању дистрибуираног складиштења података у Еластицсеарцх-у. Погледаћемо која подешавања на чворовима су одговорна за дистрибуцију шарда, како ИЛМ ради и ради.

Они који раде са трупцима, на овај или онај начин, суочавају се са проблемом дуготрајног складиштења за каснију анализу. У Еластицсеарцх-у је то посебно тачно, јер је све било несрећно са функционалношћу кустоса. Верзија 6.6 увела је ИЛМ функционалност. Састоји се од 4 фазе:

  • Вруће – Индекс се активно ажурира и поставља упит.
  • Топло – Индекс се више не ажурира, али се још увек поставља упит.
  • Хладно – Индекс се више не ажурира и ретко се поставља упит. Информације и даље морају бити претраживе, али упити могу бити спорији.
  • Избриши – Индекс више није потребан и може се безбедно избрисати.

Дато

  • Еластицсеарцх Дата Хот: 24 процесора, 128 ГБ меморије, 1,8 ТБ ССД РАИД 10 (8 чворова).
  • Еластицсеарцх Дата Варм: 24 процесора, 64 ГБ меморије, 8 ТБ НетАпп ССД политика (4 чвора).
  • Еластицсеарцх Дата Цолд: 8 процесора, 32 ГБ меморије, 128 ТБ ХДД РАИД 10 (4 чвора).

Циљ

Ова подешавања су индивидуална, све зависи од места на чворовима, броја индекса, дневника итд. Имамо 2-3 ТБ података дневно.

  • 5 дана - Врућа фаза (8 главних / 1 реплика).
  • 20 дана - топла фаза (схринк-индек 4 главне / 1 реплика).
  • 90 дана - хладна фаза (индекс замрзавања 4 главне / 1 реплика).
  • 120 дана - Фаза брисања.

Подешавање Еластицсеарцх-а

Да бисте дистрибуирали делове по чворовима, потребан вам је само један параметар:

  • врућ-чворови:
    ~]# cat /etc/elasticsearch/elasticsearch.yml | grep attr
    # Add custom attributes to the node:
    node.attr.box_type: hot
  • Топао-чворови:
    ~]# cat /etc/elasticsearch/elasticsearch.yml | grep attr
    # Add custom attributes to the node:
    node.attr.box_type: warm
  • Хладан-чворови:
    ~]# cat /etc/elasticsearch/elasticsearch.yml | grep attr
    # Add custom attributes to the node:
    node.attr.box_type: cold

Подешавање Логстасх-а

Како све то функционише и како смо имплементирали ову функцију? Почнимо са уносом евиденције у Еластицсеарцх. Постоје два начина:

  1. Логстасх преузима дневнике од Кафке. Може покупити чист или претворити на вашој страни.
  2. Нешто само пише у Еластицсеарцх, на пример, АПМ сервер.

Размотрите пример управљања индексима преко Логстасх-а. Креира индекс и примењује се на њега индексни образац и одговарајући ИЛМ.

к8с-ингресс.цонф

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

Постављање Кибана

Постоји основни образац који се примењује на све нове индексе. Поставља дистрибуцију врућих индекса, број фрагмената, реплика итд. Тежина шаблона је одређена опцијом order. Шаблони са већом тежином замењују постојеће параметре шаблона или додају нове.

Дугорочно складиштење података у Еластицсеарцх-у
Дугорочно складиштење података у Еластицсеарцх-у

ГЕТ _темплате/дефаулт

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

Затим примените мапирање на индексе k8s-ingress-* коришћењем шаблона веће тежине.

Дугорочно складиштење података у Еластицсеарцх-у
Дугорочно складиштење података у Еластицсеарцх-у

ГЕТ _темплате/к8с-ингресс

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

Након примене свих шаблона, примењујемо ИЛМ политику и почињемо да пратимо животни век индекса.

Дугорочно складиштење података у Еластицсеарцх-у

Дугорочно складиштење података у Еластицсеарцх-у

Дугорочно складиштење података у Еластицсеарцх-у

ПРЕУЗМИ _илм/полици/к8с-ингресс

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

Проблеми

Било је проблема у фази подешавања и отклањања грешака.

Врућа фаза

За исправну ротацију индекса, присуство на крају је критично index_name-date-000026 формат бројева 000001. У коду постоје редови који проверавају индексе користећи регуларни израз за присуство бројева на крају. У супротном, доћи ће до грешке, никакве политике неће бити примењене на индекс и он ће увек бити у врућој фази.

Топла фаза

Психијатар (цутофф) — смањење броја фрагмената, јер имамо 4 чвора у топлој и хладној фази. Документација садржи следеће редове:

  • Индекс мора бити само за читање.
  • Копија сваког шарда у индексу мора да се налази на истом чвору.
  • Здравствени статус кластера мора бити зелен.

Да би смањио индекс, Еластицсеарцх премешта све примарне делове на један чвор, дуплира скраћени индекс са потребним параметрима, а затим брише стари. Параметар total_shards_per_node мора бити једнак или већи од броја главних делова да стане на један чвор. У супротном, биће обавештења и делови се неће померити на исправне чворове.

Дугорочно складиштење података у Еластицсеарцх-у
Дугорочно складиштење података у Еластицсеарцх-у

ГЕТ /схринк-к8с-ингресс-2020.06.06-000025/_сеттингс

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

Хладна фаза

Замрзнути (замрзавање) – Замрзавамо индекс да бисмо оптимизовали упите о историјским подацима.

Претраге које се обављају на замрзнутим индексима користе мали, наменски, сеарцх_тхроттлед скуп нити да контролишу број истовремених претрага које погађају замрзнуте делове на сваком чвору. Ово ограничава количину додатне меморије потребне за пролазне структуре података које одговарају замрзнутим деловима, што последично штити чворове од прекомерне потрошње меморије.
Замрзнути индекси су само за читање: не можете индексирати у њих.
Очекује се да ће претраге на замрзнутим индексима бити споро. Замрзнути индекси нису намењени за велико оптерећење претраге. Могуће је да претрага замрзнутог индекса може потрајати неколико секунди или минута да се заврши, чак и ако су исте претраге завршене у милисекундама када индекси нису били замрзнути.

Резултати

Научили смо како да припремимо чворове за рад са ИЛМ-ом, подесимо шаблон за дистрибуцију делова међу врућим чворовима и подесимо ИЛМ за индекс са свим фазама живота.

Корисни линкови

Извор: ввв.хабр.цом