# Кластер индексатора Wazuh - OpenSearch и хранение данных

> Настройка кластера Wazuh Indexer на базе OpenSearch - роли узлов, шардирование, ISM-политики, индексные шаблоны, JVM-тюнинг и снапшоты

Source: https://opennix.org/docs/wazuh/infrastructure/wazuh-indexer-cluster/


Кластер индексатора Wazuh построен на OpenSearch и обеспечивает хранение, индексацию и полнотекстовый поиск событий безопасности. Индексатор принимает данные от серверов Wazuh через Filebeat, организует их в индексы с настраиваемым количеством шардов и реплик, предоставляет API для поиска и управления жизненным циклом данных.

## Архитектура кластера

### Роли узлов

Каждый узел кластера OpenSearch выполняет одну или несколько ролей. Правильное распределение ролей критически важно для производительности и отказоустойчивости.

**Cluster-manager (менеджер кластера)**

Узел с ролью `cluster_manager` управляет состоянием кластера:

- Создание и удаление индексов
- Распределение шардов по узлам
- Отслеживание состояния узлов
- Управление шаблонами индексов и ISM-политиками

В продуктивном кластере рекомендуется выделять 3 узла с ролью `cluster_manager` для кворума. Эти узлы не должны хранить данные (не назначайте им роль `data`).

```yaml
# opensearch.yml - dedicated cluster-manager node
node.name: indexer-manager-01
node.roles:
  - cluster_manager
```

**Data (узел данных)**

Узлы с ролью `data` хранят шарды индексов и выполняют операции поиска и агрегации:

- Хранение первичных шардов и реплик
- Выполнение поисковых запросов
- Агрегация данных
- Индексация новых документов

Data-узлы потребляют наибольшее количество ресурсов (CPU, RAM, диск).

```yaml
# opensearch.yml - dedicated data node
node.name: indexer-data-01
node.roles:
  - data
```

**Ingest (узел приема данных)**

Узлы с ролью `ingest` выполняют предварительную обработку документов перед индексацией:

- Трансформация полей
- Обогащение данных
- Преобразование форматов

В типичной установке Wazuh роль ingest совмещается с ролью data, поскольку Filebeat передает данные в готовом формате.

**Coordinating (координирующий узел)**

Узел без явно назначенных ролей выступает координатором:

- Маршрутизация поисковых запросов к нужным data-узлам
- Объединение результатов из нескольких шардов
- Снижение нагрузки на data-узлы при большом количестве параллельных запросов

```yaml
# opensearch.yml - coordinating-only node
node.name: indexer-coord-01
node.roles: []
```

### Типовые конфигурации кластера

**Минимальный кластер (3 узла)**

```
Узел 1: cluster_manager + data
Узел 2: cluster_manager + data
Узел 3: cluster_manager + data
```

**Продуктивный кластер (5+ узлов)**

```
Узлы 1-3: cluster_manager (dedicated, без данных)
Узлы 4-6: data
Узел 7:   coordinating (опционально)
```

## Обнаружение и формирование кластера

### Конфигурация discovery

Узлы кластера находят друг друга через механизм обнаружения (discovery). Конфигурация задается в `opensearch.yml`:

```yaml
# Список seed-узлов для начального обнаружения
discovery.seed_hosts:
  - 192.168.1.20
  - 192.168.1.21
  - 192.168.1.22

# Узлы, участвующие в начальном формировании кластера (bootstrap)
cluster.initial_cluster_manager_nodes:
  - indexer-01
  - indexer-02
  - indexer-03
```

**Параметр `discovery.seed_hosts`** содержит список IP-адресов или FQDN узлов, к которым новый узел обращается для присоединения к кластеру. Рекомендуется указывать все узлы с ролью `cluster_manager`.

**Параметр `cluster.initial_cluster_manager_nodes`** используется только при первом запуске кластера (bootstrap). После успешного формирования кластера этот параметр можно удалить. Он содержит имена узлов (`node.name`), а не IP-адреса.

### Полный пример opensearch.yml

```yaml
cluster.name: wazuh-cluster
node.name: indexer-01
node.roles:
  - cluster_manager
  - data

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

discovery.seed_hosts:
  - 192.168.1.20
  - 192.168.1.21
  - 192.168.1.22

cluster.initial_cluster_manager_nodes:
  - indexer-01
  - indexer-02
  - indexer-03

plugins.security.ssl.transport.enabled: true
plugins.security.ssl.http.enabled: true
plugins.security.ssl.transport.pemcert_filepath: /etc/wazuh-indexer/certs/indexer.pem
plugins.security.ssl.transport.pemkey_filepath: /etc/wazuh-indexer/certs/indexer-key.pem
plugins.security.ssl.transport.pemtrustedcas_filepath: /etc/wazuh-indexer/certs/root-ca.pem
plugins.security.ssl.http.pemcert_filepath: /etc/wazuh-indexer/certs/indexer.pem
plugins.security.ssl.http.pemkey_filepath: /etc/wazuh-indexer/certs/indexer-key.pem
plugins.security.ssl.http.pemtrustedcas_filepath: /etc/wazuh-indexer/certs/root-ca.pem

compatibility.override_main_response_version: true
```

## Стратегия шардирования

### Принципы шардирования

Шарды (shards) - это единицы хранения и распределения данных в OpenSearch. Каждый индекс состоит из одного или нескольких первичных шардов, каждый из которых может иметь реплики.

**Рекомендации по количеству шардов:**

- Количество первичных шардов должно равняться количеству data-узлов в кластере
- Оптимальный размер одного шарда: 10-50 ГБ
- Слишком большое количество мелких шардов создает избыточную нагрузку на cluster-manager
- Слишком малое количество крупных шардов ограничивает параллелизм поиска

**Количество шардов нельзя изменить после создания индекса** - для изменения требуется переиндексация (reindex).

### Конфигурация реплик

Реплики обеспечивают отказоустойчивость и увеличивают пропускную способность чтения:

- Для однонодового кластера: `number_of_replicas: 0`
- Для кластера из 3+ узлов: `number_of_replicas: 1`
- Для критически важных данных: `number_of_replicas: 2`

Количество реплик можно изменить в любой момент без переиндексации:

```bash
curl -sk -u admin:password \
  -X PUT "https://localhost:9200/wazuh-alerts-*/_settings" \
  -H "Content-Type: application/json" \
  -d '{"index": {"number_of_replicas": 1}}'
```

### Настройка шаблона индексов

Wazuh использует шаблоны индексов для автоматического применения настроек к новым индексам. Настройка выполняется через API индексатора:

```bash
curl -sk -u admin:password \
  -X PUT "https://localhost:9200/_template/wazuh-custom" \
  -H "Content-Type: application/json" \
  -d '{
    "order": 1,
    "index_patterns": ["wazuh-alerts-*"],
    "settings": {
      "index.number_of_shards": 3,
      "index.number_of_replicas": 1,
      "index.refresh_interval": "5s"
    }
  }'
```

Параметр `order: 1` гарантирует, что пользовательский шаблон применяется поверх стандартного шаблона Filebeat.

## Индексные шаблоны Wazuh

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

| Шаблон индекса | Описание | Объем данных |
|---|---|---|
| `wazuh-alerts-*` | Алерты безопасности (результаты срабатывания правил) | Основной поток |
| `wazuh-archives-*` | Все события (включая несработавшие правила) | Очень большой (если включен) |
| `wazuh-statistics-*` | Статистика производительности менеджера | Небольшой |
| `wazuh-monitoring-*` | Данные мониторинга состояния агентов | Средний |

Индексы `wazuh-archives-*` по умолчанию отключены. Их включение значительно увеличивает потребление дискового пространства и требует соответствующего масштабирования хранилища.

## Управление жизненным циклом индексов (ISM)

### Обзор ISM-политик

Index State Management (ISM) позволяет автоматизировать управление индексами на основе возраста, размера или количества документов. Типичный жизненный цикл включает состояния:

1. **Hot** - активная запись и чтение, максимальная производительность
2. **Warm** - только чтение, сниженные ресурсы
3. **Cold** - архивное хранение, минимальные ресурсы
4. **Delete** - автоматическое удаление

### Создание ISM-политики

```bash
curl -sk -u admin:password \
  -X PUT "https://localhost:9200/_plugins/_ism/policies/wazuh-alerts-policy" \
  -H "Content-Type: application/json" \
  -d '{
    "policy": {
      "description": "Wazuh alerts lifecycle policy",
      "default_state": "hot",
      "states": [
        {
          "name": "hot",
          "actions": [
            {
              "rollover": {
                "min_size": "25gb",
                "min_index_age": "1d"
              }
            }
          ],
          "transitions": [
            {
              "state_name": "warm",
              "conditions": {
                "min_index_age": "7d"
              }
            }
          ]
        },
        {
          "name": "warm",
          "actions": [
            {
              "replica_count": {
                "number_of_replicas": 0
              }
            },
            {
              "force_merge": {
                "max_num_segments": 1
              }
            }
          ],
          "transitions": [
            {
              "state_name": "cold",
              "conditions": {
                "min_index_age": "30d"
              }
            }
          ]
        },
        {
          "name": "cold",
          "actions": [
            {
              "read_only": {}
            }
          ],
          "transitions": [
            {
              "state_name": "delete",
              "conditions": {
                "min_index_age": "90d"
              }
            }
          ]
        },
        {
          "name": "delete",
          "actions": [
            {
              "delete": {}
            }
          ],
          "transitions": []
        }
      ],
      "ism_template": [
        {
          "index_patterns": ["wazuh-alerts-*"],
          "priority": 100
        }
      ]
    }
  }'
```

### Rollover - ротация индексов

Rollover создает новый индекс при достижении заданных условий:

- `min_size` - максимальный размер индекса (например, `25gb`)
- `min_index_age` - максимальный возраст индекса (например, `1d`)
- `min_doc_count` - максимальное количество документов

Условия комбинируются по принципу OR - ротация происходит при достижении любого из условий.

## Настройка производительности

### JVM Heap Size

Размер кучи JVM - критический параметр производительности индексатора.

**Правила:**

- Установите `-Xms` и `-Xmx` на одинаковое значение для предотвращения изменения размера кучи в рантайме
- Выделяйте не более 50% оперативной памяти системы (оставшаяся память используется для файловых кешей ОС)
- Максимальное значение: 32 ГБ (при превышении отключается Compressed OOPs, что снижает эффективность)

Конфигурация в файле `/etc/wazuh-indexer/jvm.options`:

```
-Xms4g
-Xmx4g
```

**Рекомендации по размеру:**

| Оперативная память | JVM Heap | Примечание |
|---|---|---|
| 8 ГБ | 4 ГБ | Минимум для продуктивной среды |
| 16 ГБ | 8 ГБ | Средняя нагрузка |
| 32 ГБ | 16 ГБ | Высокая нагрузка |
| 64 ГБ | 32 ГБ | Максимальное значение |

### Блокировка памяти

Для предотвращения свопинга JVM включите блокировку памяти:

```yaml
# opensearch.yml
bootstrap.memory_lock: true
```

В конфигурации systemd:

```ini
# /etc/systemd/system/wazuh-indexer.service.d/override.conf
[Service]
LimitMEMLOCK=infinity
```

### Refresh Interval

Параметр `refresh_interval` определяет частоту обновления данных для поиска. Значение по умолчанию - 1 секунда. Увеличение интервала повышает производительность записи:

```bash
curl -sk -u admin:password \
  -X PUT "https://localhost:9200/wazuh-alerts-*/_settings" \
  -H "Content-Type: application/json" \
  -d '{"index": {"refresh_interval": "5s"}}'
```

Для массовой загрузки данных можно временно отключить refresh:

```bash
curl -sk -u admin:password \
  -X PUT "https://localhost:9200/wazuh-alerts-*/_settings" \
  -H "Content-Type: application/json" \
  -d '{"index": {"refresh_interval": "-1"}}'
```

### Настройка merge

Уменьшение количества сегментов через force merge повышает производительность поиска на неизменяемых индексах:

```bash
curl -sk -u admin:password \
  -X POST "https://localhost:9200/wazuh-alerts-2024.01.01/_forcemerge?max_num_segments=1"
```

Применяйте force merge только к индексам, в которые больше не ведется запись (warm/cold состояние).

### Количество шардов на узел

Ограничьте максимальное количество шардов на узел для предотвращения деградации:

```bash
curl -sk -u admin:password \
  -X PUT "https://localhost:9200/_cluster/settings" \
  -H "Content-Type: application/json" \
  -d '{"persistent": {"cluster.max_shards_per_node": 1000}}'
```

## Мониторинг здоровья кластера

### Основные запросы мониторинга

```bash
# Состояние кластера (green/yellow/red)
curl -sk -u admin:password \
  "https://localhost:9200/_cluster/health?pretty"

# Список узлов с ресурсами
curl -sk -u admin:password \
  "https://localhost:9200/_cat/nodes?v&h=name,role,heap.percent,disk.used_percent,cpu"

# Список индексов с размерами
curl -sk -u admin:password \
  "https://localhost:9200/_cat/indices/wazuh-*?v&h=index,health,status,pri,rep,docs.count,store.size&s=index:desc"

# Распределение шардов
curl -sk -u admin:password \
  "https://localhost:9200/_cat/shards/wazuh-*?v&h=index,shard,prirep,state,docs,store,node"

# Нераспределенные шарды
curl -sk -u admin:password \
  "https://localhost:9200/_cat/shards?v&h=index,shard,prirep,state,unassigned.reason&s=state:desc"

# Использование диска
curl -sk -u admin:password \
  "https://localhost:9200/_cat/allocation?v"
```

### Интерпретация статуса кластера

| Статус | Описание | Действие |
|---|---|---|
| **Green** | Все первичные и реплика-шарды распределены | Нет необходимости в действиях |
| **Yellow** | Все первичные шарды распределены, некоторые реплики нет | Проверьте количество узлов и настройки реплик |
| **Red** | Некоторые первичные шарды не распределены | Немедленное расследование и восстановление |

## Резервное копирование (Snapshot/Restore)

### Регистрация репозитория

**Файловая система (NFS):**

```bash
curl -sk -u admin:password \
  -X PUT "https://localhost:9200/_snapshot/wazuh-backups" \
  -H "Content-Type: application/json" \
  -d '{
    "type": "fs",
    "settings": {
      "location": "/mnt/snapshots/wazuh",
      "compress": true
    }
  }'
```

**Amazon S3:**

```bash
curl -sk -u admin:password \
  -X PUT "https://localhost:9200/_snapshot/wazuh-s3-backups" \
  -H "Content-Type: application/json" \
  -d '{
    "type": "s3",
    "settings": {
      "bucket": "wazuh-snapshots",
      "region": "us-east-1",
      "base_path": "indexer-snapshots",
      "compress": true
    }
  }'
```

Путь к файловому репозиторию должен быть указан в `opensearch.yml`:

```yaml
path.repo: ["/mnt/snapshots/wazuh"]
```

### Создание снапшота

```bash
# Полный снапшот
curl -sk -u admin:password \
  -X PUT "https://localhost:9200/_snapshot/wazuh-backups/snapshot-$(date +%Y%m%d)?wait_for_completion=false" \
  -H "Content-Type: application/json" \
  -d '{
    "indices": "wazuh-alerts-*,wazuh-archives-*",
    "ignore_unavailable": true,
    "include_global_state": false
  }'

# Проверка статуса снапшота
curl -sk -u admin:password \
  "https://localhost:9200/_snapshot/wazuh-backups/_status?pretty"

# Список снапшотов
curl -sk -u admin:password \
  "https://localhost:9200/_snapshot/wazuh-backups/_all?pretty"
```

### Восстановление из снапшота

```bash
curl -sk -u admin:password \
  -X POST "https://localhost:9200/_snapshot/wazuh-backups/snapshot-20240101/_restore" \
  -H "Content-Type: application/json" \
  -d '{
    "indices": "wazuh-alerts-2024.01.01",
    "ignore_unavailable": true,
    "include_global_state": false,
    "rename_pattern": "(.+)",
    "rename_replacement": "restored-$1"
  }'
```

### Автоматизация снапшотов

Для автоматического создания снапшотов добавьте задание в cron:

```bash
# /etc/cron.d/wazuh-snapshot
0 2 * * * root curl -sk -u admin:password \
  -X PUT "https://localhost:9200/_snapshot/wazuh-backups/snapshot-$(date +\%Y\%m\%d)" \
  -H "Content-Type: application/json" \
  -d '{"indices":"wazuh-alerts-*","ignore_unavailable":true,"include_global_state":false}'
```

## Сравнение с другими платформами

### Elasticsearch Cluster

| Характеристика | Wazuh Indexer (OpenSearch) | Elasticsearch |
|---|---|---|
| Основа | OpenSearch 2.x (fork Elasticsearch 7.10) | Elasticsearch 8.x |
| Лицензия | Apache 2.0 | Elastic License / SSPL |
| Безопасность | OpenSearch Security (встроена) | X-Pack Security (платная в прошлом) |
| ISM | Index State Management | Index Lifecycle Management (ILM) |
| ML | ML Commons plugin | ML features (X-Pack) |
| Стоимость | Бесплатно | Бесплатно (Basic) / Коммерческая |

### Splunk Indexer Cluster

| Характеристика | Wazuh Indexer | Splunk Indexer |
|---|---|---|
| Формат хранения | JSON-документы в Lucene-индексах | Проприетарный (tsidx + journal) |
| Шардирование | Настраиваемые шарды и реплики | Репликация через search factor / replication factor |
| Жизненный цикл | ISM-политики (hot/warm/cold/delete) | SmartStore, frozen tier |
| Поиск | DSL-запросы, PPL | SPL (Search Processing Language) |
| Масштабирование | Горизонтальное (добавление data-узлов) | Горизонтальное (indexer peers) |
| Стоимость | Бесплатно | По объему данных |

## Устранение неполадок

### Кластер в статусе Yellow

**Причина:** реплика-шарды не распределены. Типично для однонодового кластера или когда количество реплик превышает количество data-узлов минус один.

**Решение:**

```bash
# Проверка нераспределенных шардов
curl -sk -u admin:password \
  "https://localhost:9200/_cluster/allocation/explain?pretty"

# Установка реплик в 0 для однонодового кластера
curl -sk -u admin:password \
  -X PUT "https://localhost:9200/wazuh-alerts-*/_settings" \
  -H "Content-Type: application/json" \
  -d '{"index": {"number_of_replicas": 0}}'
```

### Кластер в статусе Red

**Причина:** первичные шарды не распределены. Возможные причины: нехватка дискового пространства, сбой узла, повреждение данных.

**Решение:**

1. Проверьте причину нераспределения:

```bash
curl -sk -u admin:password \
  "https://localhost:9200/_cluster/allocation/explain?pretty"
```

2. Проверьте дисковое пространство:

```bash
curl -sk -u admin:password \
  "https://localhost:9200/_cat/allocation?v"
```

3. При необходимости принудительно перераспределите шарды:

```bash
curl -sk -u admin:password \
  -X POST "https://localhost:9200/_cluster/reroute?retry_failed=true"
```

### Disk Watermarks (пороги диска)

OpenSearch прекращает запись при достижении пороговых значений использования диска:

| Порог | Значение по умолчанию | Поведение |
|---|---|---|
| Low watermark | 85% | Новые шарды не размещаются на узле |
| High watermark | 90% | Шарды переносятся с узла |
| Flood stage | 95% | Индексы переводятся в режим read-only |

Изменение порогов:

```bash
curl -sk -u admin:password \
  -X PUT "https://localhost:9200/_cluster/settings" \
  -H "Content-Type: application/json" \
  -d '{
    "persistent": {
      "cluster.routing.allocation.disk.watermark.low": "85%",
      "cluster.routing.allocation.disk.watermark.high": "90%",
      "cluster.routing.allocation.disk.watermark.flood_stage": "95%"
    }
  }'
```

Для снятия блокировки read-only после освобождения пространства:

```bash
curl -sk -u admin:password \
  -X PUT "https://localhost:9200/wazuh-alerts-*/_settings" \
  -H "Content-Type: application/json" \
  -d '{"index.blocks.read_only_allow_delete": null}'
```

### JVM Out of Memory (OOM)

**Симптомы:** узел падает или перестает отвечать, в логах `java.lang.OutOfMemoryError`.

**Решение:**

1. Увеличьте JVM heap в `/etc/wazuh-indexer/jvm.options` (не более 50% RAM и не более 32 ГБ).

2. Проверьте потребление heap:

```bash
curl -sk -u admin:password \
  "https://localhost:9200/_cat/nodes?v&h=name,heap.percent,heap.max"
```

3. Уменьшите количество шардов или индексов (рассмотрите ISM-политику удаления старых данных).

4. Добавьте data-узлы для распределения нагрузки.

### Нераспределенные шарды (Unassigned Shards)

**Диагностика причины:**

```bash
curl -sk -u admin:password \
  "https://localhost:9200/_cluster/allocation/explain?pretty" \
  -H "Content-Type: application/json" \
  -d '{"index":"wazuh-alerts-2024.01.01","shard":0,"primary":true}'
```

**Частые причины:**

- `NODE_LEFT` - узел покинул кластер (восстановите узел или дождитесь возвращения)
- `ALLOCATION_FAILED` - ошибка при размещении (проверьте логи узла)
- `INDEX_CREATED` - индекс только что создан, шарды в процессе распределения
- `CLUSTER_RECOVERED` - кластер восстанавливается после перезапуска

Для общего обзора инфраструктуры см. [раздел инфраструктуры Wazuh](/docs/wazuh/infrastructure/). Данные в индексатор поступают из [серверного кластера](/docs/wazuh/infrastructure/wazuh-server-cluster/), а управление осуществляется через [REST API](/docs/wazuh/infrastructure/wazuh-server-api/).

