Сбор и анализ логов Kubernetes кластера с помощью EFK-стека

Mar 15, 2020 10:18 · 648 words · 4 minute read kubernetes fluentbit elasticsearch logs

На дворе уже 2020 год, а стандартного решения для агрегации логов в Kubernetes до сих пор нет. В данной статье рассмотрим особенности сбора и анализа логов в кластере Kubernetes с помощью EFK-стека. Давайте разберемся!

С ростом популярности Kubernetes известная проблема сбора логов стала еще болезненнее: многие старые сервисы начали миграцию на микросервисную инфраструктуру. В контексте логирования это проявилось в растущем числе источников логов, их различном жизненном цикле, и, самое главное, в необходимости отслеживать через логи взаимосвязь всех компонентов системы…

Сейчас, к сожалению, нет стандартизированного варианта логирования для Kubernetes, который бы работал во всех без исключения случаях или имел существенные преимущества. Наиболее популярные варианты на сегодня:

В нашем случае было решено использовать стек EFK, во многом из-за простоты развертывания и наличия опыта работы с elasticsearch, однако Fluentd мы заменили на Fluent Bit. Причина такого решения вполне очевидна, если взглянуть на рекомендуемые значения .resources в манифестах для обоих утилит:

#Fluentd
resources:
  limits:
    memory: 500Mi
  requests:
    cpu: 100m
    memory: 200Mi
#Fluent Bit
resources:
  requests:
    cpu: 5m
    memory: 10Mi
  limits:
    cpu: 50m
    memory: 60Mi

Казалось бы, есть вполне пригодная документация и масса примеров установки данного стека, что же может пойти не так и зачем эта статья?

Дело в том, что все доступные материалы и примеры годятся только если ваш Kubernetes кластер использует в качестве исполняемой среды контейнеров (container runtimes) Docker, который долгое время оставался runtime'ом по умолчанию (а у некоторых облачных провайдеров остается и по сей день) и требовался для функционирования кластера. В нашем же случае используется containerd - независимая реализация runtime'а для Kubernetes через (Container Runtime Interface - CRI), и, как оказалось, без “велосипедов” не обойтись.

Задача: необходимо собирать логи из десятков java-приложений (особое внимание уделить стектрейсам) и доставлять их в elasticsearch. Причем, вариант просмотра логов через kubectl logs ... также должен остаться.

Fluent Bit с помощью input-плагина tail может читать файлы (например, /var/log/containers/*_default_*.log), обрабатывать их содержимое и помощью парсеров и фильтров отправлять в output (в нашем случае это кластер elasticsearch).

Проблема первая: формат логов у docker и containerd существенно отличается (первый вообще пишет в json). К счастью, после данного PR в github-репозитории она легко решается через изменение формата парсера.

Проблема вторая: java-стектрейсы попадают в лог-файл (/var/log/containers/*_default_*.log) так же, как вы можете их видеть с помощью команды kubectl logs ... - при этом каждая отдельная строка становится отдельным лог-сообщением, и попадает в elasticsearch тоже отдельно. Как правило, в java-приложениях для логов используется Logback или Log4j, поэтому вторую проблему можно устранить изменением формата логов - вполне достаточно будет заменить символ переноса строки (LF, \n) на символ разделителя строк (LS, \u2028).

В нашем случае было решено во всех java-приложениях использовать следующий формат логов:

...
%d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger{40} - %msg %replace(%xException){'\n','\u2028'}%nopex %n
...

Проблема третья: появилась после решения второй - в elasticsearch теперь логи попадают хоть и одним сообщением, но стектрейсы становятся нечитабельными из-за символов \u2028. К счастью, это тоже можно решить с помощью elastic pipelines - перед вставкой сообщения в мы выполним обратную замену символов \u2028 на \n (ну и попутно удалим несколько ненужных полей).

Kubernetes манифест со всеми необходимыми объектами (Namespace, ServiceAccount, ConfigMap, …) для тестирования fluent bit будет выглядеть так:

Примечание. Не забудьте изменить Deployment на DaemonSet при использовании в реальном кластере.

Elastic-пайплайн выглядит так:

Данный пайплайн добавляется с помощью следующей команды:

curl -k -XPUT -u user:passw0rd -H "Content-Type: application/json" -d @00-java-logs-pipeline.json "https://elasticsearch:9200/_ingest/pipeline/java-logs-pipeline"

Если у вас нет под рукой подходящего java-приложения для тестирования, можете воспользоваться этим, например с таким манифестом:

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

  • логировать подробно стоит только действительно критичные компоненты;
  • логи в production стоит делать минимальными, чтобы не создавать излишнюю нагрузку на всю цепочку;
  • логи должны быть машиночитаемыми, нормализованными и иметь строгий формат (иначе у вас будут только проблемы, а не логи);
  • будет плюсом критичные логи отправлять отдельным потоком, который должен быть отделён от остальных.
tweet Share