Elasticsearch кластер: настройка для продакшена

Архитектура production-кластера Elasticsearch

Elasticsearch — распределённый поисковый и аналитический движок, являющийся основой ELK/Elastic Stack. Для production-среды критически важна правильная архитектура кластера: разделение ролей нод, настройка шардирования, отказоустойчивость и безопасность.

Минимальный production-кластер включает три типа нод:

  • Master-eligible nodes (3 шт.) — управляют состоянием кластера, распределением шардов, создание/удаление индексов. Не хранят данные, минимальные ресурсы
  • Data nodes (2+ шт.) — хранят шарды индексов, выполняют поиск и агрегации. Требуют максимум RAM, CPU и быстрых SSD
  • Coordinating/Ingest nodes (1-2 шт.) — принимают клиентские запросы, распределяют по data-нодам, собирают результаты. Ingest — обработка pipeline (grok, date, geoip)

Для отказоустойчивости master-нод используется кворум: при 3 master-eligible нодах кластер переживает падение одной. Минимум 3 master-eligible нод — правило, нарушение которого ведёт к split-brain.

Расчёт ресурсов

Ключевые рекомендации по железу для data-нод:

ПараметрРекомендацияОбоснование
RAM64 ГБ (32 ГБ для JVM heap)Heap не более 50% RAM и не более 32 ГБ (compressed oops)
CPU16-32 ядраПоиск и агрегации CPU-bound
ДискNVMe SSD, 2-4 ТБIO-bound при индексации и merge
Сеть10 Гбит/сРепликация шардов между нодами

Формула для heap: JVM Heap = min(RAM / 2, 31 ГБ). Оставшаяся RAM используется для файлового кэша ОС (Lucene segments), что критически важно для производительности поиска.

Установка и базовая конфигурация

Установка Elasticsearch 8.x на Ubuntu:

# Импорт GPG ключа и репозитория
wget -qO - https://artifacts.elastic.co/GPG-KEY-elasticsearch | gpg --dearmor -o /usr/share/keyrings/elastic.gpg
echo "deb [signed-by=/usr/share/keyrings/elastic.gpg] https://artifacts.elastic.co/packages/8.x/apt stable main" > /etc/apt/sources.list.d/elastic-8.x.list
apt update && apt install -y elasticsearch

# Начиная с 8.x, безопасность включена по умолчанию!
# При установке выводится пароль elastic и enrollment token

Конфигурация master-ноды (/etc/elasticsearch/elasticsearch.yml):

cluster.name: production-cluster
node.name: master-01
node.roles: [ master ]

network.host: 10.0.1.11
http.port: 9200
transport.port: 9300

discovery.seed_hosts:
  - 10.0.1.11
  - 10.0.1.12
  - 10.0.1.13

cluster.initial_master_nodes:
  - master-01
  - master-02
  - master-03

# Безопасность (по умолчанию включена в 8.x)
xpack.security.enabled: true
xpack.security.transport.ssl.enabled: true
xpack.security.http.ssl.enabled: true

Конфигурация Data-ноды

# /etc/elasticsearch/elasticsearch.yml — data node
cluster.name: production-cluster
node.name: data-01
node.roles: [ data, data_content, data_hot, ingest ]

network.host: 10.0.2.11
http.port: 9200
transport.port: 9300

discovery.seed_hosts:
  - 10.0.1.11
  - 10.0.1.12
  - 10.0.1.13

# Paths — несколько дисков для данных
path.data:
  - /data/es/disk1
  - /data/es/disk2
path.logs: /var/log/elasticsearch

JVM настройки (/etc/elasticsearch/jvm.options.d/heap.options):

# Для сервера с 64 ГБ RAM
-Xms31g
-Xmx31g
# Не превышайте 32 ГБ — потеря compressed oops!

Шардирование и индексы

Правильная настройка шардов — ключ к производительности. Каждый индекс делится на шарды (primary shards), каждый шард реплицируется (replica shards). Рекомендации:

# Оптимальный размер шарда: 10-50 ГБ
# Количество шардов на ноду: не более 20-25 на 1 ГБ heap

# Создание индекса с настройками шардирования
curl -X PUT "https://localhost:9200/logs-2026.04" \
  -u elastic:password \
  -H 'Content-Type: application/json' \
  -d '{
  "settings": {
    "number_of_shards": 3,
    "number_of_replicas": 1,
    "refresh_interval": "30s",
    "codec": "best_compression"
  },
  "mappings": {
    "properties": {
      "@timestamp": { "type": "date" },
      "message": { "type": "text", "analyzer": "russian" },
      "level": { "type": "keyword" },
      "host": { "type": "keyword" },
      "response_time": { "type": "float" }
    }
  }
}'

Типичная ошибка — слишком много маленьких шардов. Каждый шард потребляет ~50 МБ heap, поэтому 10000 мелких шардов съедят ~500 ГБ heap в кластере. Используйте Index Lifecycle Management (ILM) и Data Streams для автоматической ротации.

Index Lifecycle Management (ILM)

ILM автоматически управляет жизненным циклом индексов — от hot (активная запись) до delete:

# Создание ILM-политики
curl -X PUT "https://localhost:9200/_ilm/policy/logs-policy" \
  -u elastic:password \
  -H 'Content-Type: application/json' \
  -d '{
  "policy": {
    "phases": {
      "hot": {
        "min_age": "0ms",
        "actions": {
          "rollover": {
            "max_primary_shard_size": "30gb",
            "max_age": "1d"
          }
        }
      },
      "warm": {
        "min_age": "7d",
        "actions": {
          "shrink": { "number_of_shards": 1 },
          "forcemerge": { "max_num_segments": 1 },
          "allocate": {
            "require": { "data": "warm" }
          }
        }
      },
      "cold": {
        "min_age": "30d",
        "actions": {
          "allocate": {
            "require": { "data": "cold" }
          }
        }
      },
      "delete": {
        "min_age": "90d",
        "actions": { "delete": {} }
      }
    }
  }
}'

Безопасность кластера

Elasticsearch 8.x включает безопасность по умолчанию (TLS, аутентификация). Дополнительные настройки для production:

# Создание ролей и пользователей
curl -X POST "https://localhost:9200/_security/role/logs_reader" \
  -u elastic:password \
  -H 'Content-Type: application/json' \
  -d '{
  "indices": [{
    "names": ["logs-*"],
    "privileges": ["read", "view_index_metadata"]
  }]
}'

curl -X POST "https://localhost:9200/_security/user/grafana" \
  -u elastic:password \
  -H 'Content-Type: application/json' \
  -d '{
  "password": "GrafanaReadOnlyPass",
  "roles": ["logs_reader"],
  "full_name": "Grafana Reader"
}'

# API Key для приложений (вместо паролей)
curl -X POST "https://localhost:9200/_security/api_key" \
  -u elastic:password \
  -H 'Content-Type: application/json' \
  -d '{
  "name": "logstash-writer",
  "role_descriptors": {
    "logstash": {
      "indices": [{
        "names": ["logs-*"],
        "privileges": ["create_index", "write", "manage"]
      }]
    }
  },
  "expiration": "365d"
}'

TLS-сертификаты для кластера

Генерация сертификатов для inter-node коммуникации:

# Генерация CA
/usr/share/elasticsearch/bin/elasticsearch-certutil ca \
  --out /etc/elasticsearch/certs/elastic-ca.p12 --pass ""

# Генерация сертификатов для нод
/usr/share/elasticsearch/bin/elasticsearch-certutil cert \
  --ca /etc/elasticsearch/certs/elastic-ca.p12 --ca-pass "" \
  --out /etc/elasticsearch/certs/elastic-certificates.p12 --pass ""

# Или индивидуальные сертификаты с SAN:
/usr/share/elasticsearch/bin/elasticsearch-certutil cert \
  --ca /etc/elasticsearch/certs/elastic-ca.p12 --ca-pass "" \
  --name data-01 --dns data-01.company.local --ip 10.0.2.11 \
  --out /etc/elasticsearch/certs/data-01.p12 --pass ""

chown elasticsearch:elasticsearch /etc/elasticsearch/certs/*
chmod 640 /etc/elasticsearch/certs/*

Оптимизация производительности

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

# Системные настройки (на каждой ноде)
# /etc/sysctl.d/elasticsearch.conf
vm.max_map_count=262144
vm.swappiness=1
net.core.somaxconn=65535

sysctl --system

# /etc/security/limits.d/elasticsearch.conf
elasticsearch soft nofile 65535
elasticsearch hard nofile 65535
elasticsearch soft memlock unlimited
elasticsearch hard memlock unlimited

# elasticsearch.yml — отключение swap
bootstrap.memory_lock: true

# Оптимизация индексации
# В шаблоне индекса:
{
  "settings": {
    "refresh_interval": "30s",
    "translog.durability": "async",
    "translog.sync_interval": "30s",
    "merge.scheduler.max_thread_count": 1,
    "number_of_replicas": 0
  }
}
# ВНИМАНИЕ: number_of_replicas: 0 — только для bulk-загрузки!
# После загрузки установите replicas: 1

Thread pools и очереди

Настройка пулов потоков для баланса между индексацией и поиском:

# elasticsearch.yml
thread_pool:
  write:
    queue_size: 1000  # Для burst-нагрузки при индексации
  search:
    queue_size: 1000

# Мониторинг отклонений
curl -s -u elastic:password \
  "https://localhost:9200/_cat/thread_pool/write,search?v&h=name,active,queue,rejected,completed"
# name   active queue rejected completed
# write       4     0        0    234567
# search      8    12        0    567890

# Если rejected > 0 — очередь переполнена, нужно:
# 1. Увеличить queue_size
# 2. Снизить нагрузку (rate limiting на стороне клиента)
# 3. Добавить data-ноды

Мониторинг кластера

Мониторинг здоровья кластера через API и интеграцию с Prometheus/Grafana:

# Здоровье кластера
curl -s -u elastic:password "https://localhost:9200/_cluster/health?pretty"
# {
#   "cluster_name": "production-cluster",
#   "status": "green",         ← green/yellow/red
#   "number_of_nodes": 8,
#   "active_primary_shards": 150,
#   "active_shards": 300,
#   "unassigned_shards": 0    ← должно быть 0
# }

# Статус нод
curl -s -u elastic:password \
  "https://localhost:9200/_cat/nodes?v&h=name,role,heap.percent,ram.percent,cpu,disk.used_percent"

# Размер индексов
curl -s -u elastic:password \
  "https://localhost:9200/_cat/indices?v&s=store.size:desc&h=index,docs.count,store.size,pri.store.size"

# Горячие потоки (диагностика высокой нагрузки)
curl -s -u elastic:password \
  "https://localhost:9200/_nodes/hot_threads?threads=5"

Prometheus exporter и алерты

Для интеграции с Prometheus используйте elasticsearch-exporter:

# Docker-запуск exporter
docker run -d --name es-exporter \
  -p 9114:9114 \
  -e ES_URI=https://elastic:password@10.0.1.11:9200 \
  -e ES_SSL_SKIP_VERIFY=true \
  quay.io/prometheuscommunity/elasticsearch-exporter:latest \
  --es.all --es.indices

# prometheus.yml
scrape_configs:
  - job_name: 'elasticsearch'
    static_configs:
      - targets: ['es-exporter:9114']

# Alertmanager rules
groups:
  - name: elasticsearch
    rules:
      - alert: ElasticsearchClusterRed
        expr: elasticsearch_cluster_health_status{color="red"} == 1
        for: 5m
        labels: { severity: critical }
        annotations:
          summary: "ES cluster {{ $labels.cluster }} is RED"
      
      - alert: ElasticsearchHeapHigh
        expr: elasticsearch_jvm_memory_used_bytes{area="heap"} / elasticsearch_jvm_memory_max_bytes{area="heap"} > 0.9
        for: 10m
        labels: { severity: warning }

Snapshot и восстановление

Snapshot — единственный надёжный способ бэкапа Elasticsearch. Создайте репозиторий и настройте автоматические снапшоты:

# Регистрация snapshot-репозитория (NFS или S3)
curl -X PUT "https://localhost:9200/_snapshot/backup-repo" \
  -u elastic:password \
  -H 'Content-Type: application/json' \
  -d '{
  "type": "fs",
  "settings": {
    "location": "/mnt/es-snapshots",
    "compress": true,
    "max_snapshot_bytes_per_sec": "100mb"
  }
}'

# path.repo в elasticsearch.yml (на КАЖДОЙ ноде):
# path.repo: ["/mnt/es-snapshots"]

# Создание SLM (Snapshot Lifecycle Management) политики
curl -X PUT "https://localhost:9200/_slm/policy/nightly-backup" \
  -u elastic:password \
  -H 'Content-Type: application/json' \
  -d '{
  "schedule": "0 30 2 * * ?",
  "name": "",
  "repository": "backup-repo",
  "config": {
    "indices": ["*"],
    "ignore_unavailable": true
  },
  "retention": {
    "expire_after": "30d",
    "min_count": 5,
    "max_count": 30
  }
}'

Часто задаваемые вопросы

Green — все primary и replica шарды размещены, кластер полностью работоспособен. Yellow — все primary шарды на месте, но часть replica не размещена (например, одна data-нода в кластере — реплику некуда класть). Данные не потеряны, но отказоустойчивость снижена. Red — часть primary шардов недоступна, данные частично потеряны или недоступны. Требуется немедленное вмешательство.

Elasticsearch масштабируется горизонтально: добавляя data-ноды, можно хранить петабайты данных. Практические ограничения: не более 1000 шардов на ноду (мягкий лимит), каждый шард 10-50 ГБ оптимально. Кластер с 10 data-нодами по 4 ТБ SSD хранит ~25-30 ТБ данных с одной репликой. Для временных данных (логи) используйте ILM с автоудалением старых индексов.

Да, rolling upgrade поддерживается для минорных и мажорных версий (7.x→8.x). Порядок: остановите индексацию шардов (allocation exclude), обновите ноду, дождитесь green-статуса, переходите к следующей. Начинайте с master-нод, затем data-ноды. Обязательно сделайте snapshot перед обновлением. Между мажорными версиями (6→8) прямое обновление невозможно — только через промежуточную (6→7→8).

При heap до ~32 ГБ JVM использует compressed ordinary object pointers (compressed oops) — 32-битные указатели вместо 64-битных. Это экономит ~30% памяти. При heap > 32 ГБ compressed oops отключается, и реальный выигрыш от дополнительной памяти минимален. Лучшая стратегия: 31 ГБ heap + 31 ГБ для файлового кэша ОС (Lucene использует mmap). Если нужно больше — добавляйте data-ноды, а не heap.

Три способа: 1) Snapshot/Restore — создайте snapshot в старом кластере, зарегистрируйте тот же репозиторий в новом, восстановите. Самый надёжный. 2) Reindex from remotePOST _reindex {"source":{"remote":{"host":"https://old-cluster:9200"}},"dest":{"index":"new-index"}}. 3) Cross-cluster replication (CCR) — для постоянной синхронизации между кластерами. Требует лицензию Platinum.

Нужна помощь с настройкой?

Специалисты АйТи Фреш помогут с внедрением и настройкой — 15+ лет опыта, обслуживание от 15 000 ₽/мес

📞 Связаться с нами
#Elasticsearch кластер#Elasticsearch настройка#Elasticsearch продакшен#ELK стек#Elasticsearch шарды#Elasticsearch безопасность#индексы Elasticsearch#Elasticsearch мониторинг
Комментарии 0

Оставить комментарий

загрузка...