Ведение журнала приложений Docker с помощью Filebeat и Logstash

У меня есть набор докеризованных приложений, разбросанных по нескольким серверам, и я пытаюсь настроить централизованное ведение журнала на уровне производства с помощью ELK. Я в порядке с самой частью ELK, но я немного запутался в том, как пересылать журналы в мои журналы. Я пытаюсь использовать Filebeat из-за его функции балансировки нагрузки. Я также хотел бы избежать упаковки Filebeat (или чего-либо еще) во все мои докеры и хранить его отдельно, докеризованным или нет.

Как я могу продолжить?

Я пробовал следующее. Мои Dockers входят в стандартный вывод, поэтому, если Filebeat без докеров настроен для чтения из стандартного ввода, я делаю:

журналы докеров -f mycontainer | ./filebeat -e -c filebeat.yml

Похоже, что вначале это работает. Первые журналы отправляются в мой журнал. Кэшированный, наверное. Но в какой-то момент он застревает и продолжает отправлять одно и то же событие

Это просто ошибка или я иду не в том направлении? Какое решение у вас есть?


person Gianluca    schedule 30.10.2015    source источник
comment
Я только что попробовал то же самое со старым logstash-forwarder: docker logs -f mycontainer | ./logstash-forwarder_linux_amd64 -config forwarder.conf И работает. Подозреваю баг Filebeat. Единственная проблема остается в том, что есть просто случайное подключение к logstash без балансировки нагрузки.   -  person Gianluca    schedule 30.10.2015
comment
Какую версию filebeat вы используете? Это похоже на потенциальную ошибку. Не стесняйтесь открывать проблему здесь, чтобы мы могли глубже разобраться в проблеме. Для справки: некоторые дополнительные обсуждения реализации докеров можно найти здесь: github.com/elastic/libbeat/ вопросов / 37   -  person ruflin    schedule 20.11.2015


Ответы (6)


Docker позволяет указать используемый logDriver. Этот ответ не заботится о Filebeat или балансировке нагрузки.

В презентации я использовал syslog для пересылки журналов в экземпляр Logstash (ELK), прослушивающий порт 5000. Следующая команда постоянно отправляет сообщения через syslog в Logstash:

docker run -t -d --log-driver=syslog --log-opt syslog-address=tcp://127.0.0.1:5000 ubuntu /bin/bash -c 'while true; do echo "Hello $(date)"; sleep 1; done'
person michaelbahr    schedule 30.10.2015
comment
Я посмотрел на logDriver, но как насчет высокой доступности? Потребуется ли мне балансировщик нагрузки TCP для маршрутизации запросов в мой кластер logstash? - person Gianluca; 30.10.2015
comment
Я не уверен в масштабе вашей системы. Используя ~ 200 производителей журналов (команда в моем ответе), я не заметил никаких проблем. Однако я не думал о высокой доступности или распределении нагрузки / кластеризации. - person michaelbahr; 30.10.2015
comment
Меня не так беспокоит количество логов, а наличие logstash. Мне нужно как минимум 2 или 3 из них для обеспечения хорошей отказоустойчивости, а затем какой-то механизм для переключения с одного на другой. - person Gianluca; 30.10.2015
comment
Что ж .. Я не могу тебе с этим помочь. Но я оставлю свое решение, чтобы помочь другим. Может быть, вы хотите указать в своем вопросе на балансировку нагрузки. - person michaelbahr; 30.10.2015
comment
Использование docker swarm или kubernetes должно предоставить решения ваших проблем с балансировкой нагрузки. В docker swarm вы указываете, какая служба должна получать сообщение, а докер перенаправляет на одну из реплик (это может быть циклический перебор или другие возможности) - person herm; 21.04.2017

Вот один из способов перенаправить docker logs в стек ELK (для драйвера журнала gelf требуется docker> = 1.8):

  1. Запустите контейнер Logstash с помощью плагина ввода gelf для чтения из gelf и вывода на хост Elasticsearch (ES_HOST: порт):

    docker run --rm -p 12201:12201/udp logstash \
        logstash -e 'input { gelf { } } output { elasticsearch { hosts => ["ES_HOST:PORT"] } }'
    
  2. Теперь запустите контейнер Docker и используйте драйвер ведения журнала Docker gelf. . Вот глупый пример:

    docker run --log-driver=gelf --log-opt gelf-address=udp://localhost:12201 busybox \
        /bin/sh -c 'while true; do echo "Hello $(date)"; sleep 1; done'
    
  3. Загрузите Кибану, и теперь видны вещи, которые могли бы приземлиться в docker logs. исходный код gelf показывает что для вас созданы удобные поля (подсказка: Кристоф Лабуисс): _container_id, _container_name, _image_id, _image_name, _command, _tag, _created.

Если вы используете docker-compose (убедитесь, что используете docker-compose> = 1.5) и добавьте соответствующие настройки в docker-compose.yml после запуска контейнера logstash:

log_driver: "gelf"
log_opt:
  gelf-address: "udp://localhost:12201"
person Pete    schedule 20.11.2015
comment
Я думаю, что проблема с gelf в том, что он использует udp и может незаметно сбрасывать события журнала. - person urso; 20.11.2015
comment
Хорошее замечание, @urso. Драйвер ведения журнала syslog можно использовать аналогичным образом для доставки журналов по TCP. вот пример. В документах расширенного формата Graylog (GELF) упоминаются потенциальные проблемы с использованием TCP, в отличие от UDP, незаметно прерывающего ведение журнала. События. - person Pete; 20.11.2015
comment
В этой статье объясняются проблемы с gelf (как с UDP, так и с TCP): claudiokuenzler.com/blog/845/ - person Mathieu Rollet; 27.05.2021

Используя filebeat, вы можете просто передать docker logs вывод, как вы описали. Поведение, которое вы видите, определенно звучит как ошибка, но также может быть поражающей вас конфигурацией частичного чтения строки (повторно отправляйте частичные строки, пока не будет найден символ новой строки).

Проблема, которую я вижу с трубопроводом, - это возможное противодавление в случае, если logstash недоступен. Если filebeat не может отправлять какие-либо события, он будет буферизовать события внутри и в какой-то момент прекратит чтение из stdin. Не знаю, как / если докер защищает от зависания stdout. Другая проблема с конвейером может заключаться в поведении перезапуска filebeat + docker, если вы используете docker-compose. docker-compose по умолчанию повторно использует изображения + состояние изображения. Поэтому при перезапуске вы снова отправите все старые журналы (учитывая, что базовый файл журнала еще не был повернут).

Вместо того, чтобы использовать конвейер, вы можете попытаться прочитать файлы журнала, записанные докером, в хост-систему. Драйвер журнала Docker по умолчанию - это драйвер журнала json. Вы можете и должны настроить драйвер журнала json для ротации журналов + сохранения некоторых старых файлов (для буферизации на диске). См. Параметры max-size и max-file. Драйвер json помещает одну строку данных json для каждой строки, которая будет записана. В системе хоста докеров файлы журнала записываются в /var/lib/docker/containers/container_id/container_id-json.log. Эти файлы будут отправлены filebeat в logstash. Если logstash или сеть становятся недоступными или перезапускается filebeat, он продолжает пересылать строки журнала туда, где они остались (данные файлы не были удалены из-за ротации журналов). Никакие события не будут потеряны. В logstash вы можете использовать кодек или фильтр json_lines для анализа строк json и фильтр grok, чтобы получить дополнительную информацию из ваших журналов.

Было некоторое обсуждение использования libbeat (используется filebeat для доставки файлов журналов) для добавления новый драйвер журнала для докера. Возможно, в будущем можно будет собирать журналы через dockerbeat, используя api журналов докеров (я не хотя известно о каких-либо планах по использованию API журналов).

Также можно использовать системный журнал. Возможно, вы можете получить какое-то реле syslog для событий журнала балансировки нагрузки хоста докера. Или пусть syslog записывает файлы журнала и использует filebeat для их пересылки. Я думаю, что в rsyslog есть хоть какой-то режим аварийного переключения. Вы можете использовать модуль ввода logstash syslog и rsyslog для пересылки журналов в logstash с поддержкой аварийного переключения на случай, если активный экземпляр logstash станет недоступен.

person urso    schedule 20.11.2015
comment
Что касается json-файла, github.com/moby/moby/issues/17763 указывает, что json-файлы докеров считаются внутренними данными и не предназначены для использования другими процессами. - person Jason Martin; 12.05.2017

Я создал свой собственный образ докера, используя Docker API, чтобы собрать журналы контейнеров, запущенных на машине, и отправить их в Logstash благодаря Filebeat. Не нужно ничего устанавливать или настраивать на хосте.

Проверьте это и скажите, соответствует ли он вашим потребностям: https://hub.docker.com/r/bargenson/filebeat/.

Код доступен здесь: https://github.com/bargenson/docker-filebeat

person Brice Argenson    schedule 07.02.2016

Просто чтобы помочь другим, которые в этом нуждаются, вы можете просто использовать Filebeat для отправки журналов. Я бы использовал контейнер от @ brice-argenson, но мне нужна была поддержка SSL, поэтому я выбрал локально установленный экземпляр Filebeat.

Поисковик из filebeat (повторите для других контейнеров):

- input_type: log
  paths:
    - /var/lib/docker/containers/<guid>/*.log
  document_type: docker_log
  fields:
    dockercontainer: container_name

Немного отстойно, что вам нужно знать идентификаторы GUID, поскольку они могут измениться при обновлениях.

На сервере logstash настройте обычный источник ввода filebeat для logstash и используйте такой фильтр:

filter {
  if [type] == "docker_log" {
    json {
      source => "message"
      add_field => [ "received_at", "%{@timestamp}" ]
      add_field => [ "received_from", "%{host}" ]
    }
    mutate {
      rename => { "log" => "message" }
    }
    date {
      match => [ "time", "ISO8601" ]
    }
  }
}

Это проанализирует JSON из журналов Docker и установит метку времени на ту, о которой сообщает Docker.

Если вы читаете логи из образа nginx Docker, вы также можете добавить этот фильтр:

filter {
  if [fields][dockercontainer] == "nginx" {
    grok {
      match => { "message" => "(?m)%{IPORHOST:targethost} %{COMBINEDAPACHELOG}" }
    }
    mutate {
      convert => { "[bytes]" => "integer" }
      convert => { "[response]" => "integer" }
    }
    mutate {
      rename => { "bytes" => "http_streamlen" }
      rename => { "response" => "http_statuscode" }
    }
  }
}

Преобразование / переименование не является обязательным, но исправляет упущение в выражении COMBINEDAPACHELOG, когда оно не приводит эти значения к целым числам, что делает их недоступными для агрегирования в Kibana.

person Kenneth    schedule 25.11.2016
comment
Спасибо за это! Что касается вашего намека на идентификаторы GUID, я согласен, однако вы, вероятно, не захотите делать такую ​​конфигурацию вручную, а лучше использовать что-то вроде Ansible. Тогда просто докер ps | grep имя_контейнера | awk '{print $ 1}', затем шаблон результата в конфигурации и перезапуск filebeat. - person Thomas Hirsch; 06.01.2017
comment
Согласно документации, вы должны иметь возможность использовать такой шаблон в ваших prospectors.paths: /var/lib/docker/containers/*/*.log - person erewok; 19.04.2017

Я проверил то, что erewok написал выше, в комментарии:

Согласно документации, вы должны иметь возможность использовать такой шаблон в ваших prospectors.paths: /var/lib/docker/containers/*/*.log - erewok 18 апр в 21:03

Руководства контейнера докеров, представленные как первый символ "*", правильно разрешаются при запуске filebeat. Я не знаю, что происходит при добавлении контейнеров.

person Arun Gupta    schedule 13.09.2017