Сбор логов в OpenSearch: настройка агентов, индексов и поиска | AdminWiki

Сбор логов в OpenSearch: настройка агентов, индексов и поиска

11 сентября 2026 12 мин. чтения
Содержание статьи

Сбор логов в OpenSearch строится по одной схеме: агент читает источник (файл, journald, Event Log, stdout контейнера), отправляет документы по HTTP на порт 9200, кластер раскладывает их по индексам согласно шаблону, а вы ищете события в Dashboards или через API. Для базового сценария хватает одного Filebeat: он закрывает логи Linux, Nginx и приложений, которые пишут в файл.

Большинство проблем после запуска связано не с агентом, а с маппингом полей, отсутствием write-алиаса и политики хранения. Рабочий порядок настройки: шаблон индексов, ролловер и ISM-политика, затем агент, затем проверка поступления событий. Кластер уже готов принять поток, а ошибки маппинга видны на первом же документе, поэтому отладка занимает минуты, а не часы.

Дальше: конфигурации Filebeat, Logstash, Fluent Bit и Vector, структура индексов logs-APP-YYYY.MM.DD, шаблоны и маппинг, ISM-политики, диагностика ошибок и готовые поисковые запросы для логов приложений, Linux, Windows, Docker и Kubernetes.

Архитектура сбора логов в OpenSearch: от источника до индекса

Пайплайн состоит из четырех зон: сбор, обработка, хранение, визуализация. Источник формирует поток событий, агент читает его и отправляет по сети, обработчик приводит поля к общему виду, OpenSearch хранит документы в индексах, Dashboards показывает результаты поиска.

источник            агент        обработка         хранилище         визуализация
/var/log/nginx      Filebeat     Logstash          OpenSearch        Dashboards
journald            Fluent Bit   Vector            индексы + ISM     Discover
Windows Event Log   Vector       ingest pipeline   снапшоты          визуализации
stdout контейнера

OpenSearch появился как форк Elasticsearch 7.10.2 и Kibana 7.10.2 под лицензией Apache 2.0. Для логирования это означает: агенты семейства Beats 7.17 OSS работают с кластером без переделок, а версии 8.x формально совместимы через output.elasticsearch, но поддержки от Elastic у такой связки нет. В продакшне с OpenSearch чаще берут Filebeat OSS 7.17, Fluent Bit, Vector и Data Prepper, собственный ingest-инструмент проекта OpenSearch.

Если вы только выбираете стек, начните с обзора архитектуры централизованного логирования: централизованный сбор и анализ логов в 2026. Там разобраны варианты хранилищ и критерии выбора.

Ключевые компоненты: агенты, обработчики, хранилище, визуализация

КомпонентЗадачаИнструментыЧто настроить
Агентычитать источник и доставить событияFilebeat, Fluent Bit, Vector, Logstash inputinputs/sources, очереди, TLS, лимиты буфера
Обработчикипарсить, обогащать, маршрутизироватьLogstash filters, Vector remap, ingest pipelines, Data Preppergrok/VRL, маппинг дат, ветвление потоков
Хранилищепринять, разложить, хранитьOpenSearchшаблоны, шарды, реплики, ISM, снапшоты
Визуализацияпоиск и анализOpenSearch Dashboardsindex pattern, saved search, алерты, роли

Зоны ответственности задают направление диагностики: агент отвечает за чтение файла и доставку, обработчик за корректность полей, кластер за прием и хранение, Dashboards за доступ к данным.

Когда достаточно Filebeat, а когда нужен Logstash или Vector

Filebeat читает файлы и журналы, умеет базовые processors (add_fields, drop_event, decode_json_fields) и отправляет данные напрямую в OpenSearch. Потребляет 50-100 МБ памяти. Этого хватает, если логи уже структурированы или парсятся готовым модулем (nginx, system, docker).

Logstash нужен для тяжелой обработки: grok по десяткам шаблонов, geoip, translate, несколько выходов, условные ветки. Цена: JVM и heap 1-2 ГБ на инстанс.

Vector дает гибкость Logstash при потреблении 50-150 МБ: трансформации описываются на VRL, конфиг компактный. Fluent Bit самый легкий (10-30 МБ) и стандартен для Kubernetes: DaemonSet, парсер CRI, multiline, метаданные подов.

АгентПамятьСильные стороныКогда брать
Filebeat50-100 МБмодули, winlog, container input, поля ECSLinux, Windows и Docker без сложного парсинга
Fluent Bit10-30 МБCRI, Kubernetes, низкое потреблениеKubernetes, edge-узлы, sidecar
Vector50-150 МБVRL, агрегация, несколько sinksзамена Logstash, фильтрация на источнике
Logstash1-2 ГБgrok, плагины, маршрутизациясложный парсинг, legacy-пайплайны

Правило выбора простое: один источник и простой формат - Filebeat; много источников и сложный парсинг - Logstash или Vector; контейнеры - Fluent Bit.

Подключение агентов: Beats, Logstash, Fluent Bit и Vector

Примеры рассчитаны на OpenSearch 2.x и 3.x с включенным security plugin и TLS. Для каждого агента создайте отдельного пользователя с правами только на запись в нужные индексы: общий admin-аккаунт в конфиге агента приводит к неконтролируемому доступу к кластеру.

Filebeat: лёгкий агент для Linux, Windows и Docker

Ставьте OSS-сборку 7.17 (deb/rpm или архив). Отключите управление шаблонами и ILM: механизмы Elastic с OpenSearch не работают, индексы создаст ваш шаблон.

filebeat.inputs:
- type: filestream
  id: nginx
  paths:
    - /var/log/nginx/*.log
  fields:
    service.name: nginx

- type: container
  paths:
    - /var/lib/docker/containers/*/*.log

output.elasticsearch:
  hosts: ["https://os-node1.example.com:9200"]
  username: "beats-writer"
  password: "${OS_PASSWORD}"
  ssl.certificate_authorities: ["/etc/filebeat/ca.pem"]
  index: "logs-nginx"

setup.ilm.enabled: false
setup.template.enabled: false

В index указан write-алиас logs-nginx: за ежедневные индексы и переключение алиаса отвечает ISM. Без ролловера подставляйте дату: logs-nginx-%{+yyyy.MM.dd}.

Для Windows вместо filestream подключают winlog:

- type: winlog
  name: Security
  event_id: 4624, 4625, 4634
  ignore_older: 72h

Проверка: ./filebeat test config -c filebeat.yml, затем ./filebeat test output -c filebeat.yml. Для наблюдения за отправкой запустите ./filebeat -e -d "publish" -c filebeat.yml: в логе появятся строки PublishEvents и ack. Если PublishEvents есть, а ack нет, проблема на стороне кластера: права, TLS или маппинг.

Logstash: обработка и маршрутизация логов перед отправкой

Для OpenSearch нужен плагин logstash-output-opensearch: bin/logstash-plugin install logstash-output-opensearch.

input {
  beats { port => 5044 }
}
filter {
  grok {
    match => { "message" => "%{IPORHOST:client.ip} - %{DATA:user.name} \[%{HTTPDATE:log.timestamp}\] \"%{WORD:http.request.method} %{DATA:url.original}\" %{NUMBER:http.response.status_code:int}" }
  }
  date {
    match => [ "log.timestamp", "dd/MMM/yyyy:HH:mm:ss Z" ]
    target => "@timestamp"
  }
  mutate { remove_field => [ "log.timestamp" ] }
}
output {
  opensearch {
    hosts => ["https://os-node1.example.com:9200"]
    user => "logstash-writer"
    password => "${OS_PASSWORD}"
    ssl => true
    ssl_certificate_verification => true
    index => "logs-nginx"
  }
}

Проверка конфига: bin/logstash -f nginx.conf --config.test_and_exit. Если grok не сработал, Logstash ставит тег _grokparsefailure: отправляйте такие события в отдельный индекс logs-nginx-unparsed и разбирайте отдельно.

Схему с Elasticsearch и Kibana для сравнения разбираем в материале готовое решение ELK-стека для сбора и анализа логов.

Fluent Bit: сбор логов в Kubernetes и контейнерах

[SERVICE]
    Flush        1
    Log_Level    info

[INPUT]
    Name         tail
    Path         /var/log/containers/*.log
    Parser       cri
    Tag          kube.*
    Mem_Buf_Limit 50MB

[FILTER]
    Name         kubernetes
    Match        kube.*
    Merge_Log    On
    Keep_Log     Off

[OUTPUT]
    Name         opensearch
    Match        *
    Host         os-node1.example.com
    Port         9200
    HTTP_User    fluentbit
    HTTP_Passwd  ${OS_PASSWORD}
    tls          On
    tls.verify   On
    Logstash_Prefix logs-k8s
    Logstash_DateFormat %Y.%m.%d
    Suppress_Type_Name On

В Kubernetes Fluent Bit ставят DaemonSet через Helm. Плагин kubernetes добавляет имена подов, namespace и labels. Задайте Mem_Buf_Limit и Storage.type filesystem, чтобы не потерять события при перезапуске пода.

Про маршрутизацию потоков и совместную работу с метриками: маршрутизация логов и метрик в DevOps.

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

[sources.app_logs]
type = "file"
include = ["/var/log/app/*.log"]

[transforms.parse_app]
type = "remap"
inputs = ["app_logs"]
source = '''
. = parse_json!(.message)
.level = upcase(string!(.level))
.ts = parse_timestamp!(.ts, format: "%+")
'''

[sinks.opensearch]
type = "elasticsearch"
inputs = ["parse_app"]
endpoints = ["https://os-node1.example.com:9200"]
mode = "bulk"
bulk.index = "logs-app"
auth.strategy = "basic"
auth.user = "vector-writer"
auth.password = "${OS_PASSWORD}"
compression = "gzip"
request.retry_attempts = 10

Проверка: vector validate /etc/vector/vector.toml и vector top для наблюдения за потоками. Трансформации на VRL отбрасывают шум до отправки: debug-события health-check в продакшне не нужны и только занимают место в индексах.

Настройка индексов, шаблонов и политик хранения

Индекс в OpenSearch это набор шардов с документами; шаблон задает настройки и маппинг для всех новых индексов по маске; ISM-политика управляет жизненным циклом. Без шаблона кластер создаст индекс с динамическим маппингом, и первое строковое значение в числовом поле заблокирует запись.

Структура индексов и именование: logs-app-YYYY.MM.DD

Рабочая схема именования: logs-ИСТОЧНИК-YYYY.MM.DD, например logs-nginx-2026.09.11, logs-app-2026.09.11, logs-k8s-2026.09.11. Для каждого потока заведите write-алиас (logs-nginx) и используйте его в конфиге агента. Чтение в Dashboards идет по маске logs-* через index pattern.

Плюсы схемы: старые индексы удаляются целиком без дорогого delete_by_query; роли в security plugin выдаются по маске (аналитик видит logs-app-*, но не logs-audit-*); ролловер и ISM работают предсказуемо.

Шаблоны индексов и маппинг полей

Шаблон создается один раз через API или Dashboards:

PUT _index_template/logs-app-template
{
  "index_patterns": ["logs-app-*"],
  "priority": 200,
  "template": {
    "settings": {
      "number_of_shards": 3,
      "number_of_replicas": 1,
      "refresh_interval": "30s"
    },
    "mappings": {
      "dynamic_templates": [
        {
          "strings_as_keywords": {
            "match_mapping_type": "string",
            "mapping": { "type": "keyword", "ignore_above": 1024 }
          }
        }
      ],
      "properties": {
        "@timestamp": { "type": "date" },
        "level": { "type": "keyword" },
        "message": { "type": "text" },
        "service.name": { "type": "keyword" },
        "http.response.status_code": { "type": "integer" },
        "client.ip": { "type": "ip" }
      }
    }
  }
}

Что здесь важно: dynamic_templates переводят строки в keyword с ignore_above, чтобы поля вида request_id не раздували индекс; @timestamp объявлен как date; level и service.name это keyword для точных фильтров; message остается text для полнотекстового поиска. Для критичных индексов включайте dynamic: strict, тогда неизвестное поле не появится в маппинге незаметно.

Типовые конфликты: поле, которое в одном сервисе строка, а в другом число; вложенный JSON с меняющейся структурой; поля с сотнями уникальных ключей (например, http.headers.*). Лечится явным маппингом и отключением индексации для отладочных полей: "index": false экономит место и ускоряет запись.

Если логи приходят из внешних систем и требуют нормализации на входе, посмотрите разбор ingest pipeline и ограничений OpenSearch в статье корпоративный поиск: от файлового сервера до S3 и Elasticsearch.

Политики хранения и ролловер индексов (ISM)

ISM-политика описывает состояния hot, warm, delete и условия переходов. Пример: ролловер при 50 ГБ или сутках, переход в warm через два дня, удаление через 30 дней.

PUT _plugins/_ism/policies/logs-app-policy
{
  "policy": {
    "description": "rollover 50gb или 1 день, удаление через 30 дней",
    "default_state": "hot",
    "states": [
      {
        "name": "hot",
        "actions": [
          { "rollover": { "min_index_age": "1d", "min_primary_shard_size": "50gb" } }
        ],
        "transitions": [
          { "state_name": "warm", "conditions": { "min_index_age": "2d" } }
        ]
      },
      {
        "name": "warm",
        "actions": [
          { "force_merge": { "max_num_segments": 1 } },
          { "replica_count": { "number_of_replicas": 1 } }
        ],
        "transitions": [
          { "state_name": "delete", "conditions": { "min_index_age": "30d" } }
        ]
      },
      {
        "name": "delete",
        "actions": [ { "delete": {} } ]
      }
    ],
    "ism_template": [
      { "index_patterns": ["logs-app-*"], "priority": 100 }
    ]
  }
}

Политику привязывают к шаблону через блок ism_template, тогда каждый новый индекс получит ее автоматически. Первый индекс под write-алиас создают вручную:

PUT logs-app-000001
{
  "aliases": {
    "logs-app": { "is_write_index": true }
  }
}

Дальше ISM создает logs-app-000002, logs-app-000003 и переключает алиас. Проверить состояние: GET _plugins/_ism/explain/logs-app-*.

Проверка поступления событий и типовые ошибки

Проверка строится от кластера к агенту: сначала смотрим, создан ли индекс и растет ли счетчик документов, затем сверяем маппинг, затем проверяем агент.

Проверка через OpenSearch Dashboards и API

GET _cluster/health
GET _cat/indices/logs-*?v&s=index
GET _cat/aliases/logs-app?v
GET logs-app-*/_count
GET logs-app-2026.09.11/_mapping
GET _plugins/_ism/explain/logs-app-*

В Dashboards создайте index pattern logs-* с полем времени @timestamp, откройте Discover и отсортируйте по времени. Сравните @timestamp документа с системным временем хоста: расхождение в часы означает неверный часовой пояс или пропущенный date-фильтр.

Типовые ошибки при сборе логов и их решение

СимптомПричинаРешение
Индекс не создается, агент пишет 403у пользователя нет прав create_index и index на маскувыдать роль с index_permissions на logs-* в security plugin
mapper_parsing_exceptionконфликт типов в маппингепоправить шаблон, переиндексировать, включить dynamic: strict
@timestamp 1970 года или время агентадата не распарсенадобавить grok и date, указать формат и timezone
401 или 403 при подключениинет пароля, неверный сертификат, закрыт _bulkпроверить filebeat test output, роль, настройки TLS
429 Too Many Requestsпереполнена очередь bulk, кластер не успеваетснизить размер батча, включить retry, увеличить heap
Поля есть, поиск по ним не работаетстроки стали text, а не keywordзадать маппинг и dynamic_templates
Диск быстро заполняетсянет ISM, индексы не удаляютсяприменить политику с ролловером и delete

Отдельно про доступ: в security plugin OpenSearch роли собираются из backend roles и прав на индексы. Аналитику достаточно read на logs-*, агентам нужны create_index и index, администратору manage. В Dashboards разграничение делают через tenants, чтобы дашборды одной команды не меняли другие.

Поиск и анализ логов: приложения, Linux, Windows, Docker

В Dashboards доступны KQL (level: ERROR and service.name: checkout) и Lucene (level:ERROR AND http.response.status_code:[500 TO 599]). Для агрегаций и точных фильтров используйте DSL через API.

Поиск логов приложений: ошибки, исключения, тайминги

POST logs-app-*/_search
{
  "size": 0,
  "query": {
    "bool": {
      "filter": [
        { "term": { "level": "ERROR" } },
        { "range": { "@timestamp": { "gte": "now-24h" } } }
      ]
    }
  },
  "aggs": {
    "errors_per_hour": {
      "date_histogram": { "field": "@timestamp", "fixed_interval": "1h" }
    }
  }
}

Для расследования ищите по request_id или trace.id: один запрос покажет всю цепочку событий. График ошибок по часам строится агрегацией date_histogram с фильтром level: ERROR, а топ проблемных сервисов - terms-агрегацией по service.name.

Анализ логов Linux: system.auth и системные события

Модуль system в Filebeat кладет события аутентификации в dataset system.auth. Полезные поля: system.auth.user, system.auth.sudo.command, system.auth.sudo.tty, event.outcome. Запрос для неудачных входов: dataset: "system.auth" and event.outcome: failure. Топ sudo-команд строится terms-агрегацией по system.auth.sudo.command.

Без включенного модуля system логи sudo и sshd не попадут в индекс, даже если Filebeat запущен и отправляет другие события.

Логи Windows: Event Log и безопасность

Сбор идет через winlog input. Ключевые поля: winlog.event_id, winlog.provider_name, winlog.event_data.*. Вход в систему: event.code 4624, неудачный вход 4625, выход 4634. Запрос по неудачным входам: event.code: 4625 and winlog.event_data.TargetUserName: admin. Распределение событий по кодам дает быстрый обзор состояния парка Windows.

Логи Docker и Kubernetes: контейнеры и поды

Filebeat container input или Fluent Bit добавляют поля container.name, container.id, kubernetes.pod.name, kubernetes.namespace. Поиск ошибок в конкретном сервисе: container.name: "checkout-api" and level: ERROR. Топ контейнеров по числу ошибок строится terms-агрегацией по container.name с фильтром level: ERROR.

Когда потоков становится много, ручной разбор уступает автоматике: анализ логов с помощью ИИ и LLM описывает связку OpenSearch и моделей. Для доступа к моделям без VPN и с оплатой в рублях подойдет агрегатор API AiTunnel: единый интерфейс к GPT, Gemini и Claude, бюджеты и ключи по проектам.

Подготовка кластера к росту объема данных

Кластер деградирует не от объема логов, а от количества мелких шардов и отсутствия ролловера. Планируйте шарды и tier'ы заранее, пока данных немного.

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

  • Размер первичного шарда: 10-50 ГБ. Меньше 10 ГБ дает лишний overhead, больше 50 ГБ усложняет восстановление.
  • Количество шардов на узел: до 20 на каждый 1 ГБ heap. Узел с heap 16 ГБ держит около 320 шардов.
  • Реплики: минимум 1 для отказоустойчивости. Для горячих данных с высокой нагрузкой чтения добавьте вторую.
  • Расчет: 100 ГБ логов в сутки при ролловере по 50 ГБ дают два индекса в день. С тремя первичными шардами и одной репликой это около 360 шардов за 30 дней хранения, что укладывается в кластер из трех узлов с heap 16 ГБ.

Для размещения кластера подходит облачная инфраструктура: Timeweb Cloud дает серверы, VDS/VPS и хранилище, ресурсы меняются по мере роста потока логов.

Tiered storage: hot-warm-cold архитектура

Узлам задают атрибуты node.attr.temp: hot, warm, cold. Свежие индексы живут на SSD (hot), данные старше двух дней переезжают на HDD (warm), архив уходит в cold или в снапшот объектного хранилища. Переходы описываются в ISM-политике: force_merge, reduce_replicas и allocation по атрибуту temp.

{
  "name": "warm",
  "actions": [
    { "allocation": { "require": { "temp": "warm" }, "wait_for": "false" } }
  ]
}

Снапшоты в S3 дешевле дисков и позволяют восстановить архив за минуты. Регулярность: раз в сутки и перед массовыми изменениями маппинга.

Мониторинг: GET _cluster/health, GET _cat/nodes?v&h=name,heap.percent,disk.used_percent, GET _cat/shards/logs-*?v&s=store:desc. Порог для реакции: heap выше 75% или disk выше 80%. Начните с шаблона и ISM-политики, подключите первый агент, проверьте счетчик документов через _count, и только потом расширяйте поток на все хосты.

Поделиться:
Сохранить гайд? В закладки браузера