Elasticsearch'te uzun süreli veri depolama

Elasticsearch'te uzun süreli veri depolama

Benim adım Igor Sidorenko, Domclick'in tüm altyapısının bakımını yapan yöneticiler ekibinde teknik liderim.

Elasticsearch'te dağıtılmış veri depolamayı kurma deneyimimi paylaşmak istiyorum. Parçaların dağıtımından düğümlerdeki hangi ayarların sorumlu olduğuna, ILM'nin nasıl çalıştığına ve çalıştığına bakacağız.

Günlüklerle çalışanlar, öyle ya da böyle, daha sonraki analizler için uzun süreli depolama sorunuyla karşı karşıya kalır. Elasticsearch'te bu özellikle doğrudur, çünkü küratör işlevselliğiyle ilgili her şey talihsizdi. Sürüm 6.6, ILM işlevselliğini tanıttı. 4 aşamadan oluşur:

  • Sıcak - Dizin aktif olarak güncelleniyor ve sorgulanıyor.
  • Sıcak - Dizin artık güncellenmiyor, ancak hâlâ sorgulanıyor.
  • Soğuk - Dizin artık güncellenmez ve nadiren sorgulanır. Bilgiler yine de aranabilir olmalıdır, ancak sorgular daha yavaş olabilir.
  • Sil - Dizin artık gerekli değildir ve güvenle silinebilir.

Dano

  • Elasticsearch Data Hot: 24 işlemci, 128 GB bellek, 1,8 TB SSD RAID 10 (8 düğüm).
  • Elasticsearch Data Warm: 24 işlemci, 64 GB bellek, 8 TB NetApp SSD İlkesi (4 düğüm).
  • Elasticsearch Data Cold: 8 işlemci, 32 GB bellek, 128 TB HDD RAID 10 (4 düğüm).

Gol

Bu ayarlar bireyseldir, hepsi düğümlerdeki yere, dizin sayısına, günlüklere vb. bağlıdır. Günde 2-3 TB veriye sahibiz.

  • 5 gün - Sıcak aşama (8 ana / 1 kopya).
  • 20 gün - Sıcak faz (büzülme indeksi 4 ana / 1 kopya).
  • 90 gün - Soğuk faz (donma indeksi 4 ana / 1 kopya).
  • 120 gün - Silme aşaması.

Elasticsearch'ü kurma

Parçaları düğümler arasında dağıtmak için yalnızca bir parametreye ihtiyacınız vardır:

  • Popüler-düğümler:
    ~]# cat /etc/elasticsearch/elasticsearch.yml | grep attr
    # Add custom attributes to the node:
    node.attr.box_type: hot
  • Ilık, hafif sıcak-düğümler:
    ~]# cat /etc/elasticsearch/elasticsearch.yml | grep attr
    # Add custom attributes to the node:
    node.attr.box_type: warm
  • Soğuk-düğümler:
    ~]# cat /etc/elasticsearch/elasticsearch.yml | grep attr
    # Add custom attributes to the node:
    node.attr.box_type: cold

Logstash'ı kurma

Her şey nasıl çalışıyor ve bu özelliği nasıl uyguladık? Günlükleri Elasticsearch'e alarak başlayalım. İki yol vardır:

  1. Logstash, günlükleri Kafka'dan alır. Temiz alabilir veya yanınızda dönüştürebilir.
  2. Bir APM sunucusu gibi bir şeyin kendisi Elasticsearch'e yazar.

Logstash aracılığıyla dizinleri yönetmeye bir örnek düşünün. Bir dizin oluşturur ve ona uygulanır indeks deseni ve karşılık gelen ILM.

k8s-giriş.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 kurulumu

Tüm yeni dizinler için geçerli olan bir temel model vardır. Sıcak dizinlerin dağılımını, parça sayısını, kopyaları vb. ayarlar. Şablonun ağırlığı seçenek tarafından belirlenir order. Daha yüksek ağırlığa sahip şablonlar, mevcut şablon parametrelerini geçersiz kılar veya yenilerini ekler.

Elasticsearch'te uzun süreli veri depolama
Elasticsearch'te uzun süreli veri depolama

GET _template/varsayılan

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

Ardından, eşlemeyi dizinlere uygulayın k8s-ingress-* daha yüksek ağırlığa sahip bir şablon kullanarak.

Elasticsearch'te uzun süreli veri depolama
Elasticsearch'te uzun süreli veri depolama

GET _template/k8s girişi

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

Tüm şablonları uyguladıktan sonra ILM politikasını uyguluyoruz ve indekslerin ömrünü izlemeye başlıyoruz.

Elasticsearch'te uzun süreli veri depolama

Elasticsearch'te uzun süreli veri depolama

Elasticsearch'te uzun süreli veri depolama

_film/policy/k8s-girişini ALIN

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

Sorunları

Kurulum ve hata ayıklama aşamasında sorunlar vardı.

sıcak faz

İndekslerin doğru dönüşü için sonda bulunması kritiktir. index_name-date-000026 format numaraları 000001. Kodda, sonunda sayıların varlığı için düzenli bir ifade kullanarak dizinleri kontrol eden satırlar vardır. Aksi takdirde hata oluşacak, dizine herhangi bir politika uygulanmayacak ve sürekli sıcak fazda kalacaktır.

sıcak faz

Küçültmek (kesme) — sıcak ve soğuk aşamalarda 4 düğümümüz olduğu için parça sayısının azaltılması. Belgeler aşağıdaki satırları içerir:

  • Dizin salt okunur olmalıdır.
  • Dizindeki her parçanın bir kopyası aynı düğümde bulunmalıdır.
  • Küme sağlığı durumu yeşil olmalıdır.

Bir dizini budamak için, Elasticsearch tüm birincil parçaları bir düğüme taşır, kesilen dizini gerekli parametrelerle çoğaltır ve sonra eskisini siler. Parametre total_shards_per_node bir düğüme sığması için ana parça sayısına eşit veya daha fazla olmalıdır. Aksi takdirde bildirimler gelir ve parçalar doğru düğümlere taşınmaz.

Elasticsearch'te uzun süreli veri depolama
Elasticsearch'te uzun süreli veri depolama

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

soğuk faz

Dondurmak (dondur) - Geçmiş verilerle ilgili sorguları optimize etmek için dizini dondururuz.

Dondurulmuş dizinlerde gerçekleştirilen aramalar, her düğümde donmuş parçalara ulaşan eşzamanlı aramaların sayısını kontrol etmek için küçük, özel, search_throttled iş parçacığı havuzunu kullanır. Bu, donmuş parçalara karşılık gelen geçici veri yapıları için gereken ekstra bellek miktarını sınırlar ve sonuç olarak düğümleri aşırı bellek tüketimine karşı korur.
Dondurulmuş indeksler salt okunurdur: onları indeksleyemezsiniz.
Dondurulmuş endekslerdeki aramaların yavaş yürütülmesi bekleniyor. Dondurulmuş endeksler, yüksek arama yükü için tasarlanmamıştır. Endeksler dondurulmadığında aynı aramalar milisaniye cinsinden tamamlanmış olsa bile, dondurulmuş bir dizin aramasının tamamlanması saniyeler veya dakikalar alabilir.

sonuçlar

ILM ile çalışmak için düğümleri nasıl hazırlayacağımızı, parçaları sıcak düğümler arasında dağıtmak için bir şablon oluşturmayı ve yaşamın tüm evrelerini içeren bir dizin için ILM'yi kurmayı öğrendik.

Faydalı linkler

Kaynak: habr.com