Elasticsearch дээр урт хугацааны өгөгдөл хадгалах

Elasticsearch дээр урт хугацааны өгөгдөл хадгалах

Намайг Игорь Сидоренко гэдэг, би Domclick-ийн бүх дэд бүтцийг хариуцдаг админуудын багийн техникийн удирдагч юм.

Би Elasticsearch дээр тархсан өгөгдлийн санг тохируулах туршлагаасаа хуваалцахыг хүсч байна. Бид зангилаанууд дээр ямар тохиргоонууд нь хэлтэрхий түгээх үүрэгтэй, ILM хэрхэн ажилладаг, ажилладаг зэргийг авч үзэх болно.

Логтой ажилладаг хүмүүс дараа нь дүн шинжилгээ хийх зорилгоор удаан хугацаагаар хадгалах асуудалтай тулгардаг. Elasticsearch-д энэ нь ялангуяа үнэн юм, учир нь кураторын үйл ажиллагаанд бүх зүйл харамсалтай байсан. 6.6 хувилбар нь ILM функцийг нэвтрүүлсэн. Энэ нь 4 үе шатаас бүрдэнэ:

  • Халуун - Индексийг идэвхтэй шинэчилж, асууж байна.
  • Халуун - Индекс шинэчлэгдэхээ больсон ч асууж байна.
  • Хүйтэн - Индекс шинэчлэгдэхээ больсон бөгөөд ховор асуудаг. Мэдээллийг хайх боломжтой хэвээр байх ёстой, гэхдээ асуулга удааширч магадгүй.
  • Устгах - Индекс шаардлагагүй болсон бөгөөд үүнийг аюулгүйгээр устгаж болно.

Өгсөн

  • Elasticsearch Data Hot: 24 процессор, 128 ГБ санах ой, 1,8 TB SSD RAID 10 (8 зангилаа).
  • Elasticsearch Data Warm: 24 процессор, 64 ГБ санах ой, 8 TB NetApp SSD бодлого (4 зангилаа).
  • Elasticsearch Data Cold: 8 процессор, 32 ГБ санах ой, 128 TB 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-ээр дамжуулан индексийг удирдах жишээг авч үзье. Энэ нь индекс үүсгэж, түүнд хамаарна индекс загвар болон харгалзах ШИНЖЛЭХ УХААН.

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 дээр урт хугацааны өгөгдөл хадгалах

GET_загвар/өгөгдмөл

{
  "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 дээр урт хугацааны өгөгдөл хадгалах

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

Асуудал

Тохируулах болон дибаг хийх шатанд асуудал гарсан.

Халуун үе шат

Индексүүдийг зөв эргүүлэхийн тулд төгсгөлд байх нь чухал юм index_name-date-000026 формат тоо 000001. Кодын төгсгөлд тоо байгаа эсэхийг тогтмол илэрхийлэл ашиглан индексийг шалгадаг мөрүүд байдаг. Үгүй бол алдаа гарч, индекст ямар ч бодлого хэрэгжихгүй бөгөөд энэ нь үргэлж халуун үе шатанд байх болно.

Дулаан үе шат

Дахин багасгах (таслах) — хэлтэрхийний тоог багасгах, учир нь бид дулаан, хүйтэн үе шатанд 4 зангилаатай байдаг.Баримт бичигт дараах мөрүүдийг агуулна.

  • Индекс зөвхөн унших боломжтой байх ёстой.
  • Индекс дэх хэлтэрхий бүрийн хуулбар нэг зангилаа дээр байх ёстой.
  • Кластерийн эрүүл мэндийн байдал нь ногоон өнгөтэй байх ёстой.

Индексийг тайрахын тулд Elasticsearch нь бүх үндсэн хэсгүүдийг нэг зангилаа руу зөөж, шаардлагатай параметрүүдээр таслагдсан индексийг хуулбарлаж, дараа нь хуучин хэсгийг устгадаг. Параметр total_shards_per_node нэг зангилаанд багтах үндсэн хэсгүүдийн тоотой тэнцүү буюу түүнээс их байх ёстой. Үгүй бол мэдэгдэл гарч ирэх бөгөөд хэлтэрхийнүүд зөв зангилаа руу шилжихгүй.

Elasticsearch дээр урт хугацааны өгөгдөл хадгалах
Elasticsearch дээр урт хугацааны өгөгдөл хадгалах

GET /shrink-k8s-ingress-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"
          }
        }
      }
    }
  }
}

Хүйтэн үе шат

Хөлдүү (хөлдөөх) - Бид түүхэн өгөгдөл дээрх асуулгыг оновчтой болгохын тулд индексийг царцаадаг.

Хөлдөөсөн индексүүд дээр хийгдсэн хайлтууд нь зангилаа бүр дээр царцсан хэсгүүдийг цохих зэрэгцсэн хайлтын тоог хянахын тулд жижиг, тусгай зориулалтын хайлтын_throttled threadpool-г ашигладаг. Энэ нь хөлдөөсөн хэсгүүдэд тохирох түр зуурын өгөгдлийн бүтцэд шаардагдах нэмэлт санах ойн хэмжээг хязгаарлаж, улмаар зангилааг санах ойн хэт их зарцуулалтаас хамгаалдаг.
Хөлдөөсөн индексүүд нь зөвхөн унших боломжтой: та тэдгээрийг индексжүүлэх боломжгүй.
Царцаасан индексүүдийн хайлт удаан явагдах төлөвтэй байна. Хөлдөөсөн индексүүд нь хайлтын өндөр ачаалалд зориулагдаагүй болно. Индексийг царцаагаагүй үед ижил хайлт миллисекундэд хийгдсэн байсан ч царцаасан индексийн хайлтыг дуусгахад секунд эсвэл минут шаардлагатай байж магадгүй юм.

Үр дүн

Бид ILM-тэй ажиллахад зангилааг хэрхэн бэлтгэх, халуун зангилааны хооронд хэлтэрхий тараах загвар зохион байгуулах, амьдралын бүх үе шаттай индексийн ILM-ийг тохируулах талаар сурсан.

Ашигтай холбоосууд

Эх сурвалж: www.habr.com