OpenSearch в Docker Compose: готовый файл
Логи с десятка контейнеров копятся в docker logs, найти конкретную ошибку за прошлую неделю можно только через grep по горе текста, а поиск по каталогу или базе статей упирается в медленный LIKE '%запрос%'. OpenSearch закрывает оба случая одним движком — полнотекстовый поиск и агрегации по логам — но поднимать его с нуля по документации AWS долго и легко упустить важную деталь вроде пароля admin. Ниже — рабочий compose-файл, который сразу учитывает эти детали, и что делать с ним дальше.
Содержание
Обсудить статью, задать вопрос или начать новую тему
Есть вопрос по этой статье, идея для обсуждения или просто хотите поделиться опытом? Сообщество MAATRIX ждёт. Для общения, пожалуйста, зарегистрируйтесь в нашем личном кабинете.
Перейти в сообщество →Почему OpenSearch, а не Elasticsearch
В начале 2021 года Elastic сменила лицензию Elasticsearch и Kibana с Apache 2.0 на SSPL, закрыв возможность бесплатно использовать движок в managed-сервисах конкурентов. AWS форкнула последнюю Apache-версию и выпустила OpenSearch — с открытой лицензией, обратной совместимостью по API и собственным вектором развития. По возможностям на уровне «завести индекс и искать» разница с Elasticsearch небольшая, а лицензия становится важна, если вы продаёте доступ к поиску как часть своего продукта.
Более глубокая сверка версий и требований к серверу разобрана в статье про установку OpenSearch на VPS. Здесь фокус на другом: сразу рабочий compose-файл для двух типичных сценариев — полнотекстовый поиск и приём логов для аналитики.
Готовый docker-compose.yml
Минимальный прод-конфиг: один узел OpenSearch плюс Dashboards для визуализации.
# docker-compose.yml
services:
opensearch:
image: opensearchproject/opensearch:2.19.1
container_name: opensearch
restart: unless-stopped
environment:
- discovery.type=single-node
- bootstrap.memory_lock=true
- "OPENSEARCH_JAVA_OPTS=-Xms1g -Xmx1g"
- OPENSEARCH_INITIAL_ADMIN_PASSWORD=${OPENSEARCH_ADMIN_PASSWORD}
ulimits:
memlock:
soft: -1
hard: -1
nofile:
soft: 65536
hard: 65536
volumes:
- opensearch-data:/usr/share/opensearch/data
- opensearch-snapshots:/usr/share/opensearch/snapshots
ports:
- "127.0.0.1:9200:9200"
deploy:
resources:
limits:
memory: 2g
healthcheck:
test: ["CMD-SHELL", "curl -sk -u admin:$$OPENSEARCH_ADMIN_PASSWORD https://localhost:9200/_cluster/health | grep -q '\"status\":\"green\"\\|\"status\":\"yellow\"'"]
interval: 30s
timeout: 10s
retries: 5
opensearch-dashboards:
image: opensearchproject/opensearch-dashboards:2.19.1
container_name: opensearch-dashboards
restart: unless-stopped
environment:
- 'OPENSEARCH_HOSTS=["https://opensearch:9200"]'
ports:
- "127.0.0.1:5601:5601"
depends_on:
- opensearch
volumes:
opensearch-data:
opensearch-snapshots:
Тег 2.19.1 актуален на момент подготовки этого файла — OpenSearch выпускает минорные версии часто, перед первым деплоем сверьтесь со списком релизов на GitHub и зафиксируйте конкретный тег, а не latest: между минорными версиями меняется формат индексов на диске, и обновление должно быть осознанным шагом с бэкапом заранее.
Порты 9200 и 5601 привязаны к 127.0.0.1 — снаружи VPS они не видны по умолчанию, доступ только с самого сервера или через reverse-proxy/SSH-туннель. Это сознательный выбор для готового файла: открытый наружу OpenSearch без дополнительной защиты — частая причина утечек логов и данных.
Нужен сервер под эту задачу?
Разверните VPS MAATRIX за пару минут: NVMe, AMD EPYC, root-доступ, локации UK, США, Франция и РФ. Оплата картой РФ и по СБП.
Арендовать серверЧто настроить перед первым запуском
Три вещи, без которых контейнер либо не стартует, либо стартует незащищённым.
1. Лимит vm.max_map_count на хосте. OpenSearch использует Lucene, которому нужно много memory-mapped файлов — без поднятого лимита контейнер падает при старте с ошибкой max virtual memory areas vm.max_map_count is too low:
sudo sysctl -w vm.max_map_count=262144
echo 'vm.max_map_count=262144' | sudo tee -a /etc/sysctl.conf
Это системная настройка ядра хоста, не контейнера — применять внутри docker exec бесполезно.
2. Пароль admin. Начиная с OpenSearch 2.12 контейнер отказывается стартовать без явно заданного OPENSEARCH_INITIAL_ADMIN_PASSWORD — дефолтного admin/admin больше нет. Заведите .env рядом с compose:
# .env
OPENSEARCH_ADMIN_PASSWORD=Str0ng-Uniq-P@ssw0rd-2026!
chmod 600 .env
Пароль должен быть сложным — OpenSearch проверяет его на длину, регистры, цифры и спецсимволы и не запустится с чем-то вроде admin123. Не коммитьте .env в git — тот же принцип разделения секретов разобран в статье про управление паролями через Docker secrets.
3. JVM heap не больше половины RAM. В примере выше -Xms1g -Xmx1g рассчитан на VPS с 4 GB RAM — правило простое: heap занимает не больше 50% доступной памяти, остальное JVM оставляет операционной системе под файловый кеш Lucene-индексов. На сервере с 8 GB можно поднять до -Xms2g -Xmx2g, но не выше — задирать heap выше 30-32 GB не имеет смысла даже при большом объёме RAM, JVM теряет оптимизацию compressed oops после этого порога.
Запуск и первая проверка:
docker compose up -d
docker compose logs -f opensearch
curl -sk -u admin:'Str0ng-Uniq-P@ssw0rd-2026!' https://localhost:9200/_cluster/health?pretty
Ответ со "status": "yellow" для single-node — это нормально: реплики шардов физически негде размещать на одном узле, а не признак проблемы.
Тома, персистентность и снапшоты
Два именованных тома в файле выше разделены по назначению:
opensearch-data— сами индексы, критичный том, без него теряются все данные;opensearch-snapshots— точка монтирования под резервные копии, регистрируется в OpenSearch как snapshot-репозиторий типаfs.
Для продакшена часто удобнее заменить именованные тома на bind mount на конкретный смонтированный диск с более предсказуемым I/O.
Чтобы снапшоты вообще заработали, репозиторий нужно зарегистрировать явно — сам по себе смонтированный том для OpenSearch ничего не значит, пока вы не объявите его как fs-репозиторий:
curl -sk -u admin:'...' -X PUT "https://localhost:9200/_snapshot/backup_repo" \
-H 'Content-Type: application/json' -d '{
"type": "fs",
"settings": { "location": "/usr/share/opensearch/snapshots" }
}'
Снимок конкретного индекса или всех сразу:
curl -sk -u admin:'...' -X PUT "https://localhost:9200/_snapshot/backup_repo/snapshot-$(date +%F)?wait_for_completion=true"
Снапшоты пишутся в том же томе на том же диске — от отказа диска или удаления VPS они не спасают, поэтому их всё равно нужно копировать наружу отдельным заданием (cron с rclone на объектное хранилище, например). Общие практики регулярного копирования и проверки восстановления — в статье про бэкап Docker volume.
TLS и reverse-proxy наружу
Порты в готовом файле смотрят только на 127.0.0.1, поэтому для доступа снаружи (админам к Dashboards, приложению к API) нужен reverse-proxy — заодно он же закрывает и TLS-терминацию, если хотите отдать наружу единый HTTPS-домен вместо самоподписанного сертификата, который OpenSearch генерирует сам по себе.
Пример для Nginx перед Dashboards:
server {
listen 443 ssl;
server_name search.example.com;
location / {
proxy_pass http://127.0.0.1:5601;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}
Если стек уже на Traefik, добавьте лейблы прямо в сервис opensearch-dashboards того же compose-файла — принцип тот же, только без отдельного файла конфигурации:
labels:
- "traefik.enable=true"
- "traefik.http.routers.opensearch.rule=Host(`search.example.com`)"
- "traefik.http.routers.opensearch.tls.certresolver=letsencrypt"
- "traefik.http.services.opensearch.loadbalancer.server.port=5601"
REST API на 9200 наружу выносить стоит только если к нему обращаются внешние клиенты напрямую — если поиск идёт из вашего же бэкенда в той же приватной сети или compose-проекте, оставьте порт непубликуемым и обращайтесь по внутреннему имени сервиса: https://opensearch:9200.
Приём логов для аналитики
Один из главных сценариев для OpenSearch — не поиск по каталогу, а разбор логов: собрать вывод контейнеров, nginx-access-логи, ошибки приложения в один индекс и смотреть их в Dashboards вместо grep по десятку файлов.
Сам OpenSearch не читает логи за вас — нужен отдельный сборщик, который тянет логи с хоста или из контейнеров Docker и пушит их в индекс через bulk API. Практичная схема для VPS: Fluent Bit как лёгкий агент (заметно меньше по памяти, чем Logstash), который читает файлы логов или /var/lib/docker/containers/*/*.log, парсит JSON и шлёт HTTP-запросами на OpenSearch:
fluent-bit:
image: fluent/fluent-bit:3.1
container_name: fluent-bit
restart: unless-stopped
volumes:
- /var/lib/docker/containers:/var/lib/docker/containers:ro
- ./fluent-bit.conf:/fluent-bit/etc/fluent-bit.conf:ro
depends_on:
- opensearch
Минимальный fluent-bit.conf с выводом в OpenSearch:
[INPUT]
Name tail
Path /var/lib/docker/containers/*/*.log
Parser docker
Tag docker.*
[OUTPUT]
Name opensearch
Match *
Host opensearch
Port 9200
Index docker-logs
HTTP_User admin
HTTP_Passwd ${OPENSEARCH_ADMIN_PASSWORD}
tls On
tls.verify Off
tls.verify Off здесь оправдан только потому что трафик идёт внутри приватной docker-сети до самоподписанного сертификата OpenSearch — для трафика через публичную сеть замените на нормальную проверку сертификата. Дальше в Dashboards заводится index pattern docker-logs*, и логи со всех контейнеров становятся доступны для фильтрации по полю, временному диапазону и полнотекстовому поиску по сообщению об ошибке.
Если задача — просто централизованный лог без сложных агрегаций и полнотекстового поиска, присмотритесь также к Grafana Loki: он заметно легче по требованиям к памяти для того же класса задач — сравнение подходов есть в статье про установку логов в Grafana Loki на VPS.
Нужен сервер под эту задачу?
Разверните VPS MAATRIX за пару минут: NVMe, AMD EPYC, root-доступ, локации UK, США, Франция и РФ. Оплата картой РФ и по СБП.
Арендовать серверНужны сами нейросети для контента?
Генерируйте изображения, видео и озвучку нейросетями на falapi.io — десятки моделей в одном окне. Оплата картой РФ и по СБП.
Частые вопросы
Обязательно ли поднимать Dashboards вместе с OpenSearch?
Нет, если доступ нужен только программно через REST API (например, бэкенд шлёт поисковые запросы) — уберите сервис opensearch-dashboards из compose и оставьте один узел OpenSearch, это сэкономит память.
Можно ли отключить security plugin для теста?
Технически да, через DISABLE_SECURITY_PLUGIN=true, но делать это стоит только в полностью изолированном тестовом окружении без доступа снаружи. Для прода отключать плагин безопасности — то же самое, что оставить базу данных без пароля.
Сколько диска реально нужно под индексы?
Индекс Lucene обычно занимает сопоставимый или чуть больший объём, чем исходные данные, из-за инвертированных индексов и метаданных. Точный коэффициент зависит от схемы полей — закладывайте запас, ориентировочно от 1 до 2 объёмов сырых данных, и уточняйте на своих документах, а не на общих цифрах из документации.
Контейнер падает сразу после старта с ошибкой про vm.max_map_count — что не так?
Это лимит хоста, а не контейнера. Примените sysctl -w vm.max_map_count=262144 на самом VPS (не внутри docker exec) и закрепите в /etc/sysctl.conf, иначе значение слетит после перезагрузки сервера.
Как обновить OpenSearch без потери данных?
Сначала снимите снапшот текущего индекса через _snapshot API (раздел выше), затем поднимите новый тег образа и проверьте _cluster/health — если версия образа скачет через несколько минорных релизов сразу, читайте release notes на предмет breaking changes в формате индексов, а не просто меняйте тег вслепую.
Обсудить статью, задать вопрос или начать новую тему
Есть вопрос по этой статье, идея для обсуждения или просто хотите поделиться опытом? Сообщество MAATRIX ждёт. Для общения, пожалуйста, зарегистрируйтесь в нашем личном кабинете.
Перейти в сообщество →