Lưu trữ dữ liệu dài hạn trong Elaticsearch

Lưu trữ dữ liệu dài hạn trong Elaticsearch

Tên tôi là Igor Sidorenko, tôi là trưởng nhóm kỹ thuật trong nhóm quản trị viên duy trì toàn bộ cơ sở hạ tầng của Domclick.

Tôi muốn chia sẻ kinh nghiệm của mình trong việc thiết lập lưu trữ dữ liệu phân tán trong Elaticsearch. Chúng tôi sẽ xem xét cài đặt nào trên các nút chịu trách nhiệm phân phối các phân đoạn, cách ILM hoạt động và hoạt động.

Những người làm việc với nhật ký, bằng cách này hay cách khác, phải đối mặt với vấn đề lưu trữ lâu dài để phân tích sau này. Trong Elaticsearch, điều này đặc biệt đúng, bởi vì mọi thứ đều không may với chức năng của người quản lý. Phiên bản 6.6 đã giới thiệu chức năng ILM. Nó bao gồm 4 giai đoạn:

  • Hot - Chỉ mục đang được cập nhật và truy vấn tích cực.
  • Ấm áp - Chỉ mục không còn được cập nhật nhưng vẫn đang được truy vấn.
  • Lạnh - Chỉ mục không còn được cập nhật và hiếm khi được truy vấn. Thông tin vẫn phải có thể tìm kiếm được nhưng truy vấn có thể chậm hơn.
  • Xóa - Chỉ mục không còn cần thiết nữa và có thể được xóa một cách an toàn.

Được cho

  • Dữ liệu Elaticsearch Hot: 24 bộ xử lý, bộ nhớ 128 GB, SSD 1,8 TB RAID 10 (8 nút).
  • Elaticsearch Data Warm: 24 bộ xử lý, bộ nhớ 64 GB, Chính sách SSD NetApp 8 TB (4 nút).
  • Elaticsearch Data Cold: 8 bộ xử lý, bộ nhớ 32 GB, ổ cứng 128 TB RAID 10 (4 nút).

mục tiêu

Các cài đặt này là riêng lẻ, tất cả phụ thuộc vào vị trí trên các nút, số lượng chỉ mục, nhật ký, v.v. Chúng tôi có 2-3 TB dữ liệu mỗi ngày.

  • 5 ngày - Hot phase (8 main/1 replica).
  • 20 ngày - Giai đoạn ấm áp (chỉ số thu nhỏ 4 chính / 1 bản sao).
  • 90 ngày - Giai đoạn lạnh (chỉ số đóng băng 4 chính / 1 bản sao).
  • 120 ngày - Giai đoạn xóa.

Thiết lập Elaticsearch

Để phân phối các phân đoạn trên các nút, bạn chỉ cần một tham số:

  • Nóng bức-điểm giao:
    ~]# cat /etc/elasticsearch/elasticsearch.yml | grep attr
    # Add custom attributes to the node:
    node.attr.box_type: hot
  • Ấm áp-điểm giao:
    ~]# cat /etc/elasticsearch/elasticsearch.yml | grep attr
    # Add custom attributes to the node:
    node.attr.box_type: warm
  • Lạnh-điểm giao:
    ~]# cat /etc/elasticsearch/elasticsearch.yml | grep attr
    # Add custom attributes to the node:
    node.attr.box_type: cold

Thiết lập Logstash

Tất cả hoạt động như thế nào và chúng tôi đã triển khai tính năng này như thế nào? Hãy bắt đầu bằng cách đăng nhập vào Elaticsearch. Có hai cách:

  1. Logstash tìm nạp nhật ký từ Kafka. Có thể nhặt sạch hoặc convert bên mình.
  2. Một cái gì đó tự ghi vào Elaticsearch, chẳng hạn như máy chủ APM.

Hãy xem xét một ví dụ về việc quản lý các chỉ mục thông qua Logstash. Nó tạo một chỉ mục và áp dụng cho nó mẫu chỉ mục và tương ứng phần mở rộng phim.

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

thiết lập Kibana

Có một mẫu cơ sở áp dụng cho tất cả các chỉ mục mới. Nó đặt phân phối các chỉ mục nóng, số lượng phân đoạn, bản sao, v.v. Trọng lượng của mẫu được xác định bởi tùy chọn order. Các mẫu có trọng số cao hơn sẽ ghi đè các tham số mẫu hiện có hoặc thêm các tham số mẫu mới.

Lưu trữ dữ liệu dài hạn trong Elaticsearch
Lưu trữ dữ liệu dài hạn trong Elaticsearch

NHẬN _template/mặc định

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

Sau đó áp dụng ánh xạ cho các chỉ mục k8s-ingress-* sử dụng một mẫu có trọng số cao hơn.

Lưu trữ dữ liệu dài hạn trong Elaticsearch
Lưu trữ dữ liệu dài hạn trong Elaticsearch

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

Sau khi áp dụng tất cả các mẫu, chúng tôi áp dụng chính sách ILM và bắt đầu theo dõi vòng đời của các chỉ mục.

Lưu trữ dữ liệu dài hạn trong Elaticsearch

Lưu trữ dữ liệu dài hạn trong Elaticsearch

Lưu trữ dữ liệu dài hạn trong Elaticsearch

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

Vấn đề

Đã xảy ra sự cố ở giai đoạn thiết lập và gỡ lỗi.

giai đoạn nóng

Đối với vòng quay chính xác của các chỉ số, sự hiện diện ở cuối là rất quan trọng index_name-date-000026 định dạng số 000001. Có những dòng trong mã kiểm tra các chỉ mục bằng cách sử dụng biểu thức chính quy để biết sự hiện diện của các số ở cuối. Nếu không, sẽ xảy ra lỗi, không có chính sách nào được áp dụng cho chỉ mục và chỉ mục sẽ luôn ở giai đoạn nóng.

giai đoạn ấm áp

Shrink (cutoff) — giảm số lượng phân đoạn, bởi vì chúng ta có 4 nút trong giai đoạn nóng và lạnh. Tài liệu chứa các dòng sau:

  • Chỉ mục phải ở dạng chỉ đọc.
  • Bản sao của mọi phân đoạn trong chỉ mục phải nằm trên cùng một nút.
  • Trạng thái sức khỏe của cụm phải có màu xanh lục.

Để cắt bớt một chỉ mục, Elaticsearch di chuyển tất cả các phân đoạn chính đến một nút, sao chép chỉ mục bị cắt ngắn với các tham số cần thiết, sau đó xóa chỉ mục cũ. Tham số total_shards_per_node phải bằng hoặc lớn hơn số lượng phân đoạn chính để vừa trên một nút. Nếu không, sẽ có thông báo và các phân đoạn sẽ không di chuyển đến đúng nút.

Lưu trữ dữ liệu dài hạn trong Elaticsearch
Lưu trữ dữ liệu dài hạn trong Elaticsearch

NHẬN /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"
          }
        }
      }
    }
  }
}

giai đoạn lạnh

Đóng băng (đóng băng) - Chúng tôi đóng băng chỉ mục để tối ưu hóa các truy vấn trên dữ liệu lịch sử.

Các tìm kiếm được thực hiện trên các chỉ số bị đóng băng sử dụng nhóm luồng nhỏ, chuyên dụng, search_throttled để kiểm soát số lượng tìm kiếm đồng thời chạm vào các phân đoạn bị đóng băng trên mỗi nút. Điều này giới hạn lượng bộ nhớ bổ sung cần thiết cho các cấu trúc dữ liệu tạm thời tương ứng với các phân đoạn bị đóng băng, do đó bảo vệ các nút khỏi việc tiêu thụ bộ nhớ quá mức.
Các chỉ số cố định là chỉ đọc: bạn không thể lập chỉ mục cho chúng.
Các tìm kiếm trên các chỉ số bị đóng băng dự kiến ​​sẽ thực hiện chậm. Các chỉ mục cố định không dành cho tải tìm kiếm cao. Có thể việc tìm kiếm chỉ mục bị đóng băng có thể mất vài giây hoặc vài phút để hoàn thành, ngay cả khi các tìm kiếm tương tự hoàn thành trong mili giây khi các chỉ mục không bị đóng băng.

Kết quả

Chúng tôi đã học cách chuẩn bị các nút để làm việc với ILM, thiết lập một mẫu để phân phối các phân đoạn giữa các nút nóng và thiết lập ILM cho một chỉ mục với tất cả các giai đoạn của cuộc đời.

Liên kết hữu ích

Nguồn: www.habr.com