OpenSearch — поисковый движок и хранилище логов с открытой лицензией Apache 2.0, выросший из ветки Elasticsearch 7.10. Он умеет главное: индексировать документы, искать по полнотекстовым полям, строить агрегации и хранить журналы. Разберём установку, маппинги, bulk-загрузку, поисковые запросы, управление жизненным циклом индексов и снапшоты.
Чем OpenSearch отличается от Elasticsearch
После смены лицензии Elasticsearch в 2021 году проект OpenSearch продолжил развитие открытой ветки. Для администратора это означает: базовая безопасность, алертинг и SQL-плагин доступны без платной подписки, а код можно свободно использовать и модифицировать. REST API во многом совместим с Elasticsearch 7.x, но не с 8.x, поэтому старые клиенты нужно проверять.
Вместо Kibana используется OpenSearch Dashboards, вместо ILM — ISM (Index State Management), а для клиентов есть официальные библиотеки opensearch-py и opensearch-js. При миграции прогоните запросы и шаблоны на тестовом кластере: различия в мелочах встречаются часто.
- Лицензия Apache 2.0 без ограничений на функции.
- Плагин безопасности включён по умолчанию.
- SQL и PPL для запросов вместо JSON.
- ISM для ротации и удаления старых индексов.
- Векторный поиск через k-NN.
Быстрый запуск в контейнере
Для знакомства хватит одного узла. Пароль администратора задаётся переменной и должен соответствовать требованиям сложности, иначе контейнер не стартует. Лимиты memlock и nofile лучше поднять сразу: без них движок предупреждает о нестабильности.
docker run -d --name opensearch \
-p 9200:9200 -p 9600:9600 \
-e discovery.type=single-node \
-e OPENSEARCH_INITIAL_ADMIN_PASSWORD='Str0ng-Passw0rd!' \
-e OPENSEARCH_JAVA_OPTS='-Xms2g -Xmx2g' \
--ulimit memlock=-1:-1 --ulimit nofile=65536:65536 \
-v osdata:/usr/share/opensearch/data \
opensearchproject/opensearch:latest
Проверка готовности: curl -k -u admin:'Str0ng-Passw0rd!' https://localhost:9200/_cluster/health?pretty. Флаг -k отключает проверку самоподписанного сертификата и годится только для теста. В production сертификат должен быть доверенным, иначе клиенты будут отключать проверку и терять защиту от подмены.
Индексы, маппинги и шаблоны
Индекс — это набор документов с общим маппингом. Поля типа text анализируются и разбиваются на слова, keyword хранится целиком и подходит для агрегаций, date требует явного формата.
curl -k -u admin:$PW -X PUT "https://localhost:9200/logs-app-000001" \
-H 'Content-Type: application/json' -d '{
"settings": {"number_of_shards": 1, "number_of_replicas": 0},
"mappings": {"properties": {
"@timestamp": {"type": "date"},
"level": {"type": "keyword"},
"message": {"type": "text"},
"status": {"type": "integer"}
}}
}'
Для потока логов удобнее шаблон и alias с is_write_index: приложение пишет в alias, а ISM создаёт новые индексы по мере роста. Число шардов на индекс должно быть небольшим — один шард на 10–50 ГБ данных, иначе кластер тратит память на служебные структуры.
Массовая загрузка документов
Bulk-API принимает NDJSON: строка действия, затем документ. Формат строгий — последняя строка должна заканчиваться переводом строки, иначе часть пакета будет отвергнута.
curl -k -u admin:$PW -X POST "https://localhost:9200/_bulk" \
-H 'Content-Type: application/x-ndjson' \
--data-binary @bulk.ndjson
curl -k -u admin:$PW "https://localhost:9200/_cat/indices?v&s=store.size:desc"
curl -k -u admin:$PW "https://localhost:9200/logs-app-*/_count?pretty"
Размер пакета подбирают экспериментально: обычно это 5–15 МБ или несколько тысяч документов. Слишком крупный пакет создаёт нагрузку на heap и очередь, слишком мелкий — лишние накладные расходы.
Поиск и агрегации
Полнотекстовый запрос по умолчанию ищет с анализом, а фильтр по keyword работает точно и кешируется. Комбинация query и aggs позволяет за один запрос получить и примеры, и статистику.
curl -k -u admin:$PW -X POST "https://localhost:9200/logs-app-*/_search" \
-H 'Content-Type: application/json' -d '{
"size": 0,
"query": {"bool": {
"filter": [{"range": {"@timestamp": {"gte": "now-1h"}}}],
"must": [{"match": {"message": "timeout"}}]
}},
"aggs": {"by_level": {"terms": {"field": "level", "size": 10}}}
}'
Правило простое: всё, что не влияет на релевантность, переносите в filter. Такие условия не считают score и работают заметно быстрее на больших выборках. Для тяжёлых агрегаций используйте size: 0, чтобы не тянуть сами документы.
Ротация и удаление старых логов
ISM-политика описывает состояния и переходы. В примере индекс переходит в состояние удаления через 30 дней, а rollover срабатывает по возрасту или размеру шарда.
curl -k -u admin:$PW -X PUT "https://localhost:9200/_plugins/_ism/policies/logs-rollover" \
-H 'Content-Type: application/json' -d '{
"policy": {
"description": "rollover and delete",
"default_state": "hot",
"states": [
{"name": "hot", "actions": [{"rollover": {"min_index_age": "1d"}}],
"transitions": [{"state_name": "delete", "conditions": {"min_index_age": "30d"}}]},
{"name": "delete", "actions": [{"delete": {}}], "transitions": []}
],
"ism_template": [{"index_patterns": ["logs-app-*"], "priority": 100}]
}
}'
Проверить привязку и текущее состояние: GET _plugins/_ism/explain/logs-app-*. Если политика не применяется, чаще всего не совпал шаблон индекса или документ создан до её регистрации.
Снапшоты и восстановление
Снапшот — единственный штатный способ сохранить данные. Сначала в opensearch.yml задаётся path.repo, затем регистрируется репозиторий. Для кластера из нескольких узлов путь должен быть общим сетевым хранилищем.
curl -k -u admin:$PW -X PUT "https://localhost:9200/_snapshot/backup" \
-H 'Content-Type: application/json' -d '{
"type": "fs",
"settings": {"location": "/mnt/backups/opensearch", "compress": true}
}'
curl -k -u admin:$PW -X PUT "https://localhost:9200/_snapshot/backup/snap-2026-11-06?wait_for_completion=true"
Снапшоты инкрементальны: повторный запуск копирует только новые сегменты. Восстановление делают в индекс с другим именем, чтобы не затереть рабочие данные.
Приём логов и Dashboards
Fluent Bit, Logstash и Data Prepper умеют отправлять данные в OpenSearch. Для Fluent Bit достаточно output-плагина opensearch с адресом, портом, TLS и именем индекса. Dashboards разворачивается отдельным контейнером и подключается к кластеру через OPENSEARCH_HOSTS.
Не индексируйте всё подряд: поля с высокой кардинальностью съедают память. Используйте keyword с ignore_above или не сохраняйте такие значения.
Диагностика и производительность
Здоровье кластера смотрят через _cluster/health, распределение шардов — через _cat/shards, узкие места — через _nodes/stats. Если шард не размещается, команда _cluster/allocation/explain объяснит причину: диск, фильтры размещения или нехватка места.
- Heap не больше половины RAM и не выше 32 ГБ.
- Свободное место на диске — не менее 15% на каждом узле.
- Один шард на индекс для небольших объёмов, реплики — минимум одна в кластере.
- Отключите wildcard-запросы по всем индексам в приложении.
При высокой нагрузке смотрите _nodes/hot_threads: он показывает занятые потоки. Частая причина тормозов — агрегации по полю text вместо keyword и слишком большое число шардов на узел.
Итог
OpenSearch закрывает типовую задачу: хранить и искать большие объёмы документов и логов без привязки к платной лицензии. Минимальный рабочий набор — индекс с продуманным маппингом, bulk-загрузка, фильтры вместо полнотекста там, где нужна точность, ISM-политика и регулярные снапшоты. Начните с одного узла, измерьте heap и размер шардов, и только потом добавляйте реплики.