Elasticsearch жүйесінде деректерді ұзақ мерзімді сақтау

Elasticsearch жүйесінде деректерді ұзақ мерзімді сақтау

Менің атым Игорь Сидоренко, мен Domclick-тің барлық инфрақұрылымын жүргізетін әкімшілер тобының техникалық жетекшісімін.

Мен Elasticsearch жүйесінде таратылған деректер қоймасын орнату тәжірибесімен бөліскім келеді. Түйіндердегі қандай параметрлер сынықтарды таратуға жауапты екенін, ILM қалай жұмыс істейтінін және жұмыс істейтінін қарастырамыз.

Бөренелермен жұмыс істейтіндер, қандай да бір жолмен, кейінірек талдау үшін ұзақ мерзімді сақтау мәселесіне тап болады. Elasticsearch-те бұл әсіресе дұрыс, өйткені куратордың жұмысында бәрі сәтсіз болды. 6.6 нұсқасы ILM функциясын енгізді. Ол 4 кезеңнен тұрады:

  • Ыстық - индекс белсенді түрде жаңартылуда және сұралуда.
  • Жылы - индекс енді жаңартылмайды, бірақ әлі де сұралуда.
  • Суық - индекс енді жаңартылмайды және сирек сұралады. Ақпарат әлі де іздеуге болатын болуы керек, бірақ сұраулар баяу болуы мүмкін.
  • Жою - индекс енді қажет емес және оны қауіпсіз жоюға болады.

Берілген

  • Elasticsearch Data Hot: 24 процессор, 128 ГБ жад, 1,8 ТБ SSD RAID 10 (8 түйін).
  • Elasticsearch Data Warm: 24 процессор, 64 ГБ жад, 8 ТБ NetApp SSD саясаты (4 түйін).
  • Elasticsearch Data Cold: 8 процессор, 32 ГБ жады, 128 ТБ HDD RAID 10 (4 түйін).

Максат

Бұл параметрлер жеке болып табылады, бәрі түйіндердегі орынға, индекстер санына, журналдарға және т.б. Бізде күніне 2-3 ТБ деректер бар.

  • 5 күн - Ыстық фаза (8 негізгі / 1 реплика).
  • 20 күн - жылы фаза (қысқарту индексі 4 негізгі / 1 көшірме).
  • 90 күн - Суық кезең (қату индексі 4 негізгі / 1 көшірме).
  • 120 күн - кезеңді жою.

Elasticsearch орнату

Бөлшектерді түйіндер бойынша тарату үшін сізге тек бір параметр қажет:

  • ыстық-түйіндер:
    ~]# 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

Logstash орнату

Мұның бәрі қалай жұмыс істейді және біз бұл мүмкіндікті қалай жүзеге асырдық? Elasticsearch жүйесіне журналдарды енгізуден бастайық. Екі жол бар:

  1. Logstash журналдарды Кафкадан алады. Таза ала алады немесе жағыңызда түрлендіреді.
  2. Бір нәрсенің өзі Elasticsearch-ке жазады, мысалы, APM сервері.

Logstash арқылы индекстерді басқару мысалын қарастырыңыз. Ол индекс жасайды және оған қолданылады индекс үлгісі және сәйкес 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
    }
}

Кибана орнату

Барлық жаңа индекстерге қолданылатын негізгі үлгі бар. Ол ыстық индекстердің таралуын, үзінділер санын, көшірмелерді және т.б. белгілейді. Үлгінің салмағы опциямен анықталады order. Салмағы жоғары үлгілер бар үлгі параметрлерін қайта анықтайды немесе жаңаларын қосады.

Elasticsearch жүйесінде деректерді ұзақ мерзімді сақтау
Elasticsearch жүйесінде деректерді ұзақ мерзімді сақтау

_үлгі/әдепкі алу

{
  "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-* салмағы жоғары үлгіні пайдалану.

Elasticsearch жүйесінде деректерді ұзақ мерзімді сақтау
Elasticsearch жүйесінде деректерді ұзақ мерзімді сақтау

_template/k8s-кіру алу

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

Барлық үлгілерді қолданғаннан кейін біз ILM саясатын қолданамыз және индекстердің қызмет ету мерзімін бақылауды бастаймыз.

Elasticsearch жүйесінде деректерді ұзақ мерзімді сақтау

Elasticsearch жүйесінде деректерді ұзақ мерзімді сақтау

Elasticsearch жүйесінде деректерді ұзақ мерзімді сақтау

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

проблемалар

Орнату және жөндеу кезеңінде проблемалар болды.

Ыстық фаза

Индекстердің дұрыс айналуы үшін соңында болуы өте маңызды index_name-date-000026 сандарды пішімдеу 000001. Кодта соңында сандар бар-жоғын тұрақты өрнекті пайдаланып индекстерді тексеретін жолдар бар. Әйтпесе, қате орын алады, индекске ешқандай саясат қолданылмайды және ол әрқашан ыстық фазада болады.

Жылы фаза

Шөгу (кесу) — сынықтар санын азайту, өйткені бізде жылы және суық фазаларда 4 түйін бар.Құжаттамада келесі жолдар бар:

  • Индекс тек оқуға арналған болуы керек.
  • Индекстегі әрбір үзіндінің көшірмесі бір түйінде орналасуы керек.
  • Кластердің денсаулық күйі жасыл болуы керек.

Индексті кесу үшін, Elasticsearch барлық негізгі бөліктерді бір түйінге жылжытады, кесілген индексті қажетті параметрлермен көшіреді, содан кейін ескісін жояды. Параметр total_shards_per_node бір түйінге сыятын негізгі үзінділер санына тең немесе одан көп болуы керек. Әйтпесе, хабарландырулар болады және үзінділер дұрыс түйіндерге ауыспайды.

Elasticsearch жүйесінде деректерді ұзақ мерзімді сақтау
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"
          }
        }
      }
    }
  }
}

Суық фаза

тоңу (мұздату) - Тарихи деректер бойынша сұрауларды оңтайландыру үшін индексті тоқтатамыз.

Мұздатылған индекстерде орындалған іздеулер әрбір түйінде бекітілген үзінділерге соқтығысатын бір мезгілде іздеулердің санын басқару үшін шағын, бөлінген, search_throttled ағынын пайдаланады. Бұл мұздатылған үзінділерге сәйкес келетін өтпелі деректер құрылымдары үшін қажет қосымша жад көлемін шектейді, нәтижесінде түйіндерді жадты шамадан тыс тұтынудан қорғайды.
Мұздатылған индекстер тек оқуға арналған: оларға индекстеу мүмкін емес.
Мұздатылған индекстер бойынша іздеулер баяу орындалады деп күтілуде. Мұздатылған индекстер жоғары іздеу жүктемесіне арналмаған. Мұздатылған индексті іздеуді аяқтау үшін бірнеше секунд немесе минут кетуі мүмкін, тіпті индекстер бекітілмеген кезде бірдей іздеулер миллисекундтарда аяқталса да.

Нәтижелері

Біз түйіндерді ILM-мен жұмыс істеуге дайындауды, ыстық түйіндер арасында үзінділерді таратуға арналған үлгіні орнатуды және өмірдің барлық кезеңдері бар индекс үшін ILM орнатуды үйрендік.

Пайдалы сілтемелер

Ақпарат көзі: www.habr.com