Opslach op lange termyn yn Elasticsearch

Opslach op lange termyn yn Elasticsearch

Myn namme is Igor Sidorenko, ik bin in technyske lead yn in team fan behearders dy't de hiele Domklik-ynfrastruktuer yn wurking hâlde.

Ik wol myn ûnderfining diele by it ynstellen fan ferdielde gegevensopslach yn Elasticsearch. Wy sille sjen op hokker ynstellings op knopen binne ferantwurdlik foar de ferdieling fan shards, hoe't ILM is strukturearre en wurket.

Dejingen dy't wurkje mei logs, op ien of oare manier, wurde konfrontearre mei it probleem fan lange termyn opslach foar folgjende analyze. Dit is foaral wier yn Elasticsearch, om't de funksjonaliteit fan 'e kurator jammerdearlik west hat. Yn ferzje 6.6 ferskynde ILM-funksjonaliteit. It bestiet út 4 fazen:

  • Hot-De yndeks wurdt aktyf bywurke en frege.
  • Warm - de yndeks wurdt net mear bywurke, mar wurdt noch frege.
  • Kâld - De yndeks wurdt net mear bywurke en wurdt selden frege. De ynformaasje moat noch trochsykber wêze, mar fragen kinne stadiger wêze.
  • Wiskje - De yndeks is net mear nedich en kin feilich wiske wurde.

Given

  • Elasticsearch Data Hot: 24 processors, 128 GB ûnthâld, 1,8 TB SSD RAID 10 (8 knopen).
  • Elasticsearch Data Warm: 24 processors, 64 GB ûnthâld, 8 TB NetApp SSD Policy (4 knopen).
  • Elasticsearch Data Cold: 8 processors, 32 GB ûnthâld, 128 TB HDD RAID 10 (4 knopen).

Goal

Dizze ynstellingen binne yndividueel, it hinget allegear ôf fan 'e romte op' e knopen, it oantal yndeksen, logs, ensfh. Foar ús is dit 2-3 TB oan gegevens per dei.

  • 5 dagen - Hot faze (8 main / 1 replika).
  • 20 dagen - Warm faze (krimp-yndeks 4 haad / 1 replika).
  • 90 dagen - Kâlde faze (freeze-yndeks 4 haad / 1 replika).
  • 120 dagen - Fase wiskje.

Elasticsearch ynstelle

Om shards ûnder knooppunten te fersprieden, hawwe jo mar ien parameter nedich:

  • Hyt-knooppunten:
    ~]# cat /etc/elasticsearch/elasticsearch.yml | grep attr
    # Add custom attributes to the node:
    node.attr.box_type: hot
  • Waarm-knooppunten:
    ~]# cat /etc/elasticsearch/elasticsearch.yml | grep attr
    # Add custom attributes to the node:
    node.attr.box_type: warm
  • Kâld-knooppunten:
    ~]# cat /etc/elasticsearch/elasticsearch.yml | grep attr
    # Add custom attributes to the node:
    node.attr.box_type: cold

It ynstellen fan Logstash

Hoe wurket dit allegear en hoe hawwe wy dizze funksje ymplementearre? Litte wy begjinne mei it krijen fan logs yn Elasticsearch. Der binne twa manieren:

  1. Logstash helje logs út Kafka. Kin ophelje skjin of omsette op syn kant.
  2. Iets skriuwt himsels oan Elasticsearch, bygelyks in APM-tsjinner.

Litte wy nei in foarbyld sjen fan it behearen fan yndeksen fia Logstash. It makket in yndeks en jildt index template en oerienkommende WITTENSKIP.

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 ynstelle

D'r is in basissjabloan dat jildt foar alle nije yndeksen. It stelt de ferdieling fan hjitte yndeksen, it oantal shards, replika's, ensfh. It gewicht fan it sjabloan wurdt bepaald troch de opsje order. Sjabloanen mei hegere gewichten oerskriuwe besteande sjabloanparameters of foegje nije ta.

Opslach op lange termyn yn Elasticsearch
Opslach op lange termyn yn 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" : { }
  }
}

Tapasse dan mapping op yndeksen k8s-ingress-* mei help fan in sjabloan mei in heger gewicht.

Opslach op lange termyn yn Elasticsearch
Opslach op lange termyn yn 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" : { }
  }
}

Nei it tapassen fan alle sjabloanen tapasse wy it ILM-belied en begjinne it libben fan 'e yndeksen te kontrolearjen.

Opslach op lange termyn yn Elasticsearch

Opslach op lange termyn yn Elasticsearch

Opslach op lange termyn yn 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" : { }
          }
        }
      }
    }
  }
}

Problemen

D'r wiene problemen by de opset en debuggen.

Hot faze

Foar korrekte rotaasje fan yndeksen, de oanwêzigens oan 'e ein fan index_name-date-000026 opmaak nûmers 000001. D'r binne rigels yn 'e koade dy't yndeksen kontrolearje mei in reguliere útdrukking foar nûmers oan 'e ein. Oars sil d'r in flater wêze, belied sil net tapast wurde op 'e yndeks en sil altyd yn' e hite faze wêze.

Warm faze

Shrink (snijden) - it ferminderjen fan it oantal shards, om't wy 4 knopen hawwe yn 'e waarme en kâlde faze. De dokumintaasje befettet de folgjende rigels:

  • De yndeks moat allinich lêzen wêze.
  • In kopy fan elke shard yn 'e yndeks moat op itselde knooppunt wenje.
  • De kluster sûnensstatus moat grien wêze.

Om in yndeks te trimmen, ferpleatst Elasticsearch alle primêre shards nei ien knooppunt, duplikearret de ôfsnien yndeks mei de nedige parameters, en wisket dan de âlde. Parameter total_shards_per_node moat gelyk wêze oan of grutter wêze as it oantal haadshards om se op ien knooppunt te passen. Oars sille d'r notifikaasjes wêze en sille de shards net ferpleatse nei de fereaske knopen.

Opslach op lange termyn yn Elasticsearch
Opslach op lange termyn yn 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"
          }
        }
      }
    }
  }
}

Kâlde faze

Frieze (freeze) - wy befrieze de yndeks om queries te optimalisearjen basearre op histoaryske gegevens.

Sykopdrachten útfierd op beferzen yndeksen brûke de lytse, tawijde, search_throttled threadpool om it oantal simultane sykopdrachten te kontrolearjen dy't beferzen shards op elke knooppunt treffe. Dit beheint it bedrach fan ekstra ûnthâld nedich foar de transiente gegevensstruktueren dy't oerienkomme mei beferzen shards, dy't dêrtroch knopen beskermet tsjin oermjittich ûnthâldferbrûk.
Beferzen yndeksen binne allinich lêzen: jo kinne der net yn yndeksearje.
Sykopdrachten op beferzen yndeksen wurde ferwachte om stadich út te fieren. Beferzen yndeksen binne net bedoeld foar hege syklast. It is mooglik dat in sykjen nei in beferzen yndeks sekonden of minuten duorje kin om te foltôgjen, sels as deselde sykopdrachten foltôge binne yn millisekonden doe't de yndeksen net beferzen wiene.

Resultaten

Wy learden hoe't jo knopen tariede op it wurkjen mei ILM, in sjabloan opsette foar it fersprieden fan shards ûnder hot knopen, en ILM konfigureare foar in yndeks mei alle libbensfazen.

Nuttige keppelings

Boarne: www.habr.com