Развертывание отказоустойчивого кластера Elasticsearch в Linux

Elasticsearch — это масштабируемый полнотекстовый поисковый и аналитический поисковый движок с открытым исходным кодом, позволяющий хранить большие объемы данных, проводить среди них быстрый поиск и аналитику. Он является чрезвычайно мощным инструментом для поиска и анализа данных, благодаря его способности к масштабированию. Описание всех преимуществ этого движка доступно на официальном сайте.

Статья является переводом и адаптацией англоязычной статьи.

Что такое кластер Elasticsearch

Как следует из названия, кластер Elasticsearch — это группа из нескольких связанных друг с другом экземпляров Elasticsearch. Основное преимущество кластера Elasticsearch заключается в распределении задач, поиске и индексации по всем узлам кластера, а так же обеспечении высокой доступности за счет отсутствия единой точки отказа в специальных отказоустойчивых конфигурациях.

Узлы в кластере Elasticsearch имеют различные роли:

  • Узел данных (Data node) хранит данные и выполняет связанные с ними операции, такие как поиск и агрегация;
  • Главный узел (Master node) отвечает за действия по управлению и настройке всего кластера, такие как добавление и удаление узлов;
  • Клиентский узел (Client node) перенаправляет запросы кластера на главный узел, а запросы, связанные с данными, на узлы данных;
  • Выделенный узел вставки данных (Ingest node) служит для предварительной обработки документов перед индексацией.

По умолчанию каждому узлу автоматически присваивается уникальный идентификатор или имя, которое используется в целях управления кластером и играет важную роль в многоузловой или кластерной среде.

После первоначальной установки Elasticsearch, формируется новый одноузловой кластер под названием «elasticsearch». В этой статье мы познакомимся с тем, как его настроить для присоединения к кластеру.

Установка кластера Elasticsearch

Как правило, существует несколько способов настройки кластера Elasticsearch. Для автоматизации процесса можно использовать инструмент управления конфигурациями, например, Puppet или Ansible. В данной статье мы рассмотрим, как вручную настроить кластер, состоящий из одного главного узла и двух узлов данных в Ubuntu 16.04, 18.04 или Debian 9.

Установка Java

Elasticsearch реализован с использованием Java и требует для запуска Java 8 или Java 9. Первым делом необходимо установить Java на всех узлах кластера. На всех узлах Elasticsearch в кластере должна быть установлена одна и та же версия Java.

Повторите следующие шаги на всех серверах кластера.

Обновите систему:

sudo apt-get update

Установите Java:

sudo apt-get install default-jre

Проверьте, что установлена корректная версия Java. Вы должны увидеть подобный ответ:

java -version

java version "1.8.0_181"
Java(TM) SE Runtime Environment (build 1.8.0_181-b13)
Java HotSpot(TM) 64-Bit Server VM (build 25.181-b13, mixed mode)

Установка Elasticsearch

Следующий шаг — установка Elasticsearch. Повторите указанные ниже шаги на всех серверах.

Добавьте ключ цифровой подписи Elastic, чтобы загружаемый пакет был принят системой без предупреждения:

wget -qO - https://artifacts.elastic.co/GPG-KEY-elasticsearch | sudo apt-key add -

Для Debian установите пакет apt-transport-https:

sudo apt-get install apt-transport-https

Следующим шагом добавьте определение репозитория в систему:

echo "deb https://artifacts.elastic.co/packages/6.x/apt stable main" | sudo tee -a /etc/apt/sources.list.d/elastic-6.x.list

Осталось только обновить репозитории и установить Elasticsearch:

sudo apt-get update 
sudo apt-get install elasticsearch

Настройка кластера Elasticsearch с одним главным узлом и двумя узлами данных

Следующий шаг — настройка кластера таким образом, чтобы узлы могли подключаться и устанавливать соединение друг с другом.

Для каждого узла откройте файл конфигурации Elasticsearch:

sudo nano /etc/elasticsearch/elasticsearch.yml

Этот файл довольно длинный и содержит ряд настроек для разных разделов. Просмотрите файл и измените его содержимое для каждого узла, как указано далее.

es-node-1:

# назовите кластер
cluster.name: my-cluster

node.name: es-node-1

node.master: true

node.data: false

# введите частный IP и порт узла:
network.host: 172.11.61.27
http.port: 9200

# укажите IP узлов для сборки кластера:
discovery.zen.ping.unicast.hosts: ["172.11.61.27", "172.31.22.131","172.31.32.221"]

es-node-2:

# назовите кластер
cluster.name: my-cluster

node.name: es-node-2

node.master: false

node.data: true

# введите частный IP и порт узла:
network.host: 172.31.22.131
http.port: 9200

# укажите IP узлов для сборки кластера:
discovery.zen.ping.unicast.hosts: ["172.11.61.27", "172.31.22.131","172.31.32.221"]

es-node-3:

# назовите кластер
cluster.name: my-cluster

node.name: es-node-3

node.master: false

node.data: true

network.host: 172.31.32.221
http.port: 9200

# укажите IP узлов для сборки кластера:
discovery.zen.ping.unicast.hosts: ["172.11.61.27", "172.31.22.131","172.31.32.221"]

Запуск кластера Elasticsearch

Теперь все готово для того, чтобы запустить узлы Elasticsearch и убедиться, что они взаимодействуют друг с другом как кластер.

На каждом сервере выполните следующую команду:

sudo service elasticsearch start

Если все настроено правильно, кластер Elasticsearch должен запуститься. Чтобы убедиться, что все работает корректно, запросите Elasticsearch с любого из узлов:

curl -XGET 'http://localhost:9200/_cluster/state?pretty'

Ответ должен вернуть информацию о кластере и его узлах:

{
  "cluster_name" : "my-cluster",
  "compressed_size_in_bytes" : 351,
  "version" : 4,
  "state_uuid" : "3LSnpinFQbCDHnsFv-Z8nw",
  "master_node" : "IwEK2o1-Ss6mtx50MripkA",
  "blocks" : { },
  "nodes" : {
    "IwEK2o1-Ss6mtx50MripkA" : {
      "name" : "es-node-2",
      "ephemeral_id" : "x9kUrr0yRh--3G0ckESsEA",
      "transport_address" : "172.31.50.123:9300",
      "attributes" : { }
    },
    "txM57a42Q0Ggayo4g7-pSg" : {
      "name" : "es-node-1",
      "ephemeral_id" : "Q370o4FLQ4yKPX4_rOIlYQ",
      "transport_address" : "172.31.62.172:9300",
      "attributes" : { }
    },
    "6YNZvQW6QYO-DX31uIvaBg" : {
      "name" : "es-node-3",
      "ephemeral_id" : "mH034-P0Sku6Vr1DXBOQ5A",
      "transport_address" : "172.31.52.220:9300",
      "attributes" : { }
    }
  },
 …

Дополнительные настройки для обеспечения высокой доступности и производительности кластера Elasticsearch

Предотвращение ситуации split brain

Split brain — это ситуация, при которой соединение между узлами в кластере прерывается из-за сбоя в сети или внутреннего сбоя одного из узлов. В таком случае сразу несколько узлов могут назначить себя главным, что приведет к состоянию несогласованности данных.

Чтобы избежать данной ситуации, необходимо изменить директиву discovery.zen.minimum_master_nodes в файле конфигурации Elasticsearch, которая определяет, сколько узлов должно объединиться друг с другом (создать кворум) для выбора главного узла.

Чтобы определить это число, рекомендуется использовать следующую формулу: ОКРУГЛЕНИЕ_ВНИЗ [N / 2] + 1, где N — это общее число главных узлов в кластере.

Например, для кластера с тремя узлами:

discovery.zen.minimum_master_nodes: 2

Настройка размера JVM heap

Чтобы снизить риск появления ошибки в работе кластера Elasticsearch по причине недостаточности ресурсов, необходимо изменить размер кучи (JVM heap), заданный по умолчанию (минимум/максимум 1 Гбайт).

Как правило, максимальный размер кучи должен составлять не более 50% оперативной памяти, и не более 32 Гбайт (из-за неэффективности указателя Java в больших кучах). Elastic также рекомендует, чтобы значение для максимального и минимального размера было одинаковым.

Эти значения можно настроить с помощью параметров Xmx и Xms в файле jvm.options:

sudo nano /etc/elasticsearch/jvm.options

-Xms2g
-Xmx2g

Отключение подкачки

Подкачка в контексте Elasticsearch может привести к разрыву соединения, плохой производительности и в целом нестабильности кластера.

Чтобы не использовать подкачку, можно либо полностью отключить ее (рекомендуется, если Elasticsearch — единственная служба, работающая на сервере), либо использовать mlockall для блокировки оперативной памяти процесса Elasticsearch.

Для этого откройте файл конфигурации Elasticsearch на всех узлах кластера:

sudo vim /etc/elasticsearch/elasticsearch.yml

Раскомментируйте строку:

bootstrap.mlockall: true

Затем откройте файл /etc/default/elasticsearch:

sudo vim /etc/default/elasticsearch

Задайте настройку:

MAX_LOCKED_MEMORY=unlimited

Перезапустите Elasticsearch.

Настройка виртуальной памяти

Чтобы избежать ситуации, при которой виртуальной памяти становится недостаточно, увеличьте количество ограничений на счетчики mmap:

sudo vim /etc/sysctl.conf

Обновите соответствующие настройки:

vm.max_map_count=262144

Увеличение предела количества дескрипторов открытых файлов

Другая важная конфигурация — ограничение на количество дескрипторов открытых файлов. Поскольку Elasticsearch использует большое количество файловых дескрипторов, необходимо убедиться, что установленного предела достаточно, иначе можно потерять данные.

Рекомендуемое значение для этого параметра — 65536 и выше. Настройки по умолчанию уже заданы в соответствии с этим требованием, но, конечно, их можно уточнить.

sudo nano /etc/security/limits.conf

Установите лимит:

  - nofile 65536

API кластера Elasticsearch

Elasticsearch поддерживает большое количество специфичных для кластера операций API, которые позволяют управлять кластером Elasticsearch и отслеживать его работу.

Далее приведен список полезных методов API, которые часто полезны при администрировании кластера Elasticsearch.

Проверка доступности кластера

Данный API можно использовать для просмотра общей информации о кластере и оценки его работоспособности:

curl -XGET 'localhost:9200/_cluster/health?pretty'

Ответ:

{
  "cluster_name" : "my-cluster",
  "status" : "green",
  "timed_out" : false,
  "number_of_nodes" : 3,
  "number_of_data_nodes" : 3,
  "active_primary_shards" : 0,
  "active_shards" : 0,
  "relocating_shards" : 0,
  "initializing_shards" : 0,
  "unassigned_shards" : 0,
  "delayed_unassigned_shards" : 0,
  "number_of_pending_tasks" : 0,
  "number_of_in_flight_fetch" : 0,
  "task_max_waiting_in_queue_millis" : 0,
  "active_shards_percent_as_number" : 100.0
}

Проверка состояния кластера

Данный API можно использовать для просмотра подробного отчета о состоянии всего кластера. Результаты можно отфильтровать, указав нужные параметры в URL вызова.

curl -XGET 'localhost:9200/_cluster/state?pretty'

Ответ:

{
  "cluster_name" : "my-cluster",
  "compressed_size_in_bytes" : 347,
  "version" : 4,
  "state_uuid" : "uMi5OBtAS8SSRJ9hw1-gUg",
  "master_node" : "sqT_y5ENQ9SdjHiE0oco_g",
  "blocks" : { },
  "nodes" : {
    "sqT_y5ENQ9SdjHiE0oco_g" : {
      "name" : "node-1",
      "ephemeral_id" : "-HDzovR0S0e-Nn8XJ-GWPA",
      "transport_address" : "172.31.56.131:9300",
      "attributes" : { }
    },
    "mO0d0hYiS1uB--NoWuWyHg" : {
      "name" : "node-3",
      "ephemeral_id" : "LXjx86Q5TrmefDoq06MY1A",
      "transport_address" : "172.31.58.61:9300",
      "attributes" : { }
    },
    "it1V-5bGT9yQh19d8aAO0g" : {
      "name" : "node-2",
      "ephemeral_id" : "lCJja_QtTYauP3xEWg5NBQ",
      "transport_address" : "172.31.62.65:9300",
      "attributes" : { }
    }
  },
  "metadata" : {
    "cluster_uuid" : "8AqSmmKdQgmRVPsVxyxKrw",
    "templates" : { },
    "indices" : { },
    "index-graveyard" : {
      "tombstones" : [ ]
    }
  },
  "routing_table" : {
    "indices" : { }
  },
  "routing_nodes" : {
    "unassigned" : [ ],
    "nodes" : {
      "it1V-5bGT9yQh19d8aAO0g" : [ ],
      "sqT_y5ENQ9SdjHiE0oco_g" : [ ],
      "mO0d0hYiS1uB--NoWuWyHg" : [ ]
    }
  },
  "snapshots" : {
    "snapshots" : [ ]
  },
  "restore" : {
    "snapshots" : [ ]
  },
  "snapshot_deletions" : {
    "snapshot_deletions" : [ ]
  }
}

Получение статистики кластера

Чрезвычайно полезный метод API для мониторинга показателей производительности всего кластера:

curl -XGET 'localhost:9200/_cluster/stats?human&pretty'

Ответ:

{
  "_nodes" : {
    "total" : 3,
    "successful" : 3,
    "failed" : 0
  },
  "cluster_name" : "my-cluster",
  "timestamp" : 1517224098451,
  "status" : "green",
  "indices" : {
    "count" : 0,
    "shards" : { },
    "docs" : {
      "count" : 0,
      "deleted" : 0
    },
    "store" : {
      "size" : "0b",
      "size_in_bytes" : 0
    },
    "fielddata" : {
      "memory_size" : "0b",
      "memory_size_in_bytes" : 0,
      "evictions" : 0
    },
    "query_cache" : {
      "memory_size" : "0b",
      "memory_size_in_bytes" : 0,
      "total_count" : 0,
      "hit_count" : 0,
      "miss_count" : 0,
      "cache_size" : 0,
      "cache_count" : 0,
      "evictions" : 0
    },
    "completion" : {
      "size" : "0b",
      "size_in_bytes" : 0
    },
    "segments" : {
      "count" : 0,
      "memory" : "0b",
      "memory_in_bytes" : 0,
      "terms_memory" : "0b",
      "terms_memory_in_bytes" : 0,
      "stored_fields_memory" : "0b",
      "stored_fields_memory_in_bytes" : 0,
      "term_vectors_memory" : "0b",
      "term_vectors_memory_in_bytes" : 0,
      "norms_memory" : "0b",
      "norms_memory_in_bytes" : 0,
      "points_memory" : "0b",
      "points_memory_in_bytes" : 0,
      "doc_values_memory" : "0b",
      "doc_values_memory_in_bytes" : 0,
      "index_writer_memory" : "0b",
      "index_writer_memory_in_bytes" : 0,
      "version_map_memory" : "0b",
      "version_map_memory_in_bytes" : 0,
      "fixed_bit_set" : "0b",
      "fixed_bit_set_memory_in_bytes" : 0,
      "max_unsafe_auto_id_timestamp" : -9223372036854775808,
      "file_sizes" : { }
    }
  },
  "nodes" : {
    "count" : {
      "total" : 3,
      "data" : 3,
      "coordinating_only" : 0,
      "master" : 3,
      "ingest" : 3
    },
    "versions" : [
      "6.1.2"
    ],
    "os" : {
      "available_processors" : 3,
      "allocated_processors" : 3,
      "names" : [
        {
          "name" : "Linux",
          "count" : 3
        }
      ],
      "mem" : {
        "total" : "10.4gb",
        "total_in_bytes" : 11247157248,
        "free" : "4.5gb",
        "free_in_bytes" : 4915200000,
        "used" : "5.8gb",
        "used_in_bytes" : 6331957248,
        "free_percent" : 44,
        "used_percent" : 56
      }
    },
    "process" : {
      "cpu" : {
        "percent" : 10
      },
      "open_file_descriptors" : {
        "min" : 177,
        "max" : 178,
        "avg" : 177
      }
    },
    "jvm" : {
      "max_uptime" : "6m",
      "max_uptime_in_millis" : 361766,
      "versions" : [
        {
          "version" : "1.8.0_151",
          "vm_name" : "OpenJDK 64-Bit Server VM",
          "vm_version" : "25.151-b12",
          "vm_vendor" : "Oracle Corporation",
          "count" : 3
        }
      ],
      "mem" : {
        "heap_used" : "252.1mb",
        "heap_used_in_bytes" : 264450008,
        "heap_max" : "2.9gb",
        "heap_max_in_bytes" : 3195076608
      },
      "threads" : 63
    },
    "fs" : {
      "total" : "23.2gb",
      "total_in_bytes" : 24962703360,
      "free" : "19.4gb",
      "free_in_bytes" : 20908818432,
      "available" : "18.2gb",
      "available_in_bytes" : 19570003968
    },
    "plugins" : [ ],
    "network_types" : {
      "transport_types" : {
        "netty4" : 3
      },
      "http_types" : {
        "netty4" : 3
      }
    }
  }
}

Статистика узлов

Используйте данный метод API, если нужно проверить показатели для определенных узлов в кластере. Вы можете просмотреть информацию обо всех узлах, конкретном узле или запросить просмотр статистики по индексу или ОС/процессу.

Для всех узлов:

curl -XGET 'localhost:9200/_nodes/stats?pretty'

Для конктретного узла:

curl -XGET 'localhost:9200/_nodes/node-1/stats?pretty'

Статистика только по индексу:

curl -XGET 'localhost:9200/_nodes/stats/indices?pretty'

Больше методов API можно найти в официальной документации API кластера.

Заключение

Из статьи вы узнали как настроить кластер Elasticsearch и получили ряд рекомендаций по настройке его для продуктового использования. Тем не менее, очевидно, что практически невозможно описать универсальное решение, подходящее для каждой отдельной среды и варианта использования.

Более подробные инструкции можно найти в официальной документации и на основании опыта использования данного продукта.