Сбор и анализ логов Kubernetes кластера с помощью EFK-стека
Mar 15, 2020 10:18 · 648 words · 4 minute read
На дворе уже 2020 год, а стандартного решения для агрегации логов в Kubernetes
до сих пор нет. В данной статье рассмотрим особенности сбора и анализа логов в кластере Kubernetes
с помощью EFK-стека. Давайте разберемся!
С ростом популярности Kubernetes
известная проблема сбора логов стала еще болезненнее: многие старые сервисы начали миграцию на микросервисную инфраструктуру. В контексте логирования это проявилось в растущем числе источников логов, их различном жизненном цикле, и, самое главное, в необходимости отслеживать через логи взаимосвязь всех компонентов системы…
Сейчас, к сожалению, нет стандартизированного варианта логирования для Kubernetes
, который бы работал во всех без исключения случаях или имел существенные преимущества. Наиболее популярные варианты на сегодня:
- стек EFK (Elasticsearch, Fluentd, Kibana);
- Loki или Logging operator;
- собственная разработка (типа loghouse).
В нашем случае было решено использовать стек 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 стоит делать минимальными, чтобы не создавать излишнюю нагрузку на всю цепочку;
- логи должны быть машиночитаемыми, нормализованными и иметь строгий формат (иначе у вас будут только проблемы, а не логи);
- будет плюсом критичные логи отправлять отдельным потоком, который должен быть отделён от остальных.