Подключаем Grafana и Fluentbit к Loki.
Вобщем, как то это заработало. Чудеса.
Всё, что связано с мониторингом, логами и прочим.
Подключаем Grafana и Fluentbit к Loki.
Вобщем, как то это заработало. Чудеса.
Продолжение серии про установку Loki в кластер k8s.
Поставил, думаю, что он работает.
Файлы, используемые в видео: https://github.com/BigKAA/youtube/tree/master/loki/01-loki
Первое видео из серии «Как запустить Loki в k8s»
План на дальнейшие видео.
Что мы должны получить в итоге.
Если вы видите loki в первый раз, рекомендую посмотреть вот это видео: https://www.youtube.com/watch?v=vkdNY8QaCug
Мне по работе надо получать события когда в логах приложений (логи у нас попадают в elasticsearch) появляются определённые сообщения. Zabbix умеет делать запросы по http. Но у нас Victoriametrics (ибн Prometheus) и хотелось бы, что бы alertmanager генерировал алерты. Но для того, что бы он смог это сделать, нужно что бы соответствующая метрика появилась в victoriametrics. Грубо говоря, нужно что бы Prometheus (или что на него похожее — vmagent) обратился к elasticsearch с определённым запросом и создал метрику. Но он зараза такое не умеет :(.
Да, можно попросить программеров, что бы они сделали нужную нам метрику у приложения. Но это хорошо когда программеры рядом и идут на встречу. Если их нет или это какое то старое приложение то ФСЁ — приплыли и рядом с victoriametrics надо ставить zabbix или что то такое…
Я честно искал приложение, которое может посылать необходимые мне запросы в elasticsearch и генерить метрики Prometheus. Но не нашёл. Возможно я плохо искал?
Вобщем мне надоело искать и я вспомнил, что нормальный сисадмин должен уметь писать вспомогательные утилиты. Я же типа нормальный сисадмин? 🙂
Заодно решил подучить go. Учить язык надо на каком то живом задании. Собственно вот, задание:
Итого. Программисты, не бейте меня ногами. Вы такого не написали, а мне очень надо. Поэтому родилась вот такая утилита: https://github.com/BigKAA/metrics-from-logs
Это первая версия, написанная по быстрому, на коленке. Там ещё много чего надо дописывать. Научится нормально работать с Redis и много ещё чего.
Жили мы жили. И вроде не плохо. Но на тесте решили поставить Kubernetes при помощи kubespray v2.11.
Накатили в новый кластер fluent-bit, с отлаженным ранее деплоем. И… обломс.
Ругается при деплое DaemonSet на:
securityContext: privileged: true volumeMounts: - name: varlog mountPath: /var/log - name: varlibdockercontainers mountPath: /var/lib/docker/containers readOnly: true
В итоге оказалось, что в новом облачке включена PodSecurityPolicy. И поскольку наш DaemonSet не использует (подключен) к существующим политикам случился облом.
Решение проблемы – Добавить PodSecurityPolicy и применить её к DaemonSet.
Определяем новую политику:
--- apiVersion: extensions/v1beta1 kind: PodSecurityPolicy metadata: name: psp.td-agent spec: allowPrivilegeEscalation: true allowedCapabilities: - '*' allowedUnsafeSysctls: - '*' fsGroup: rule: RunAsAny hostIPC: true hostNetwork: true hostPID: true hostPorts: - max: 65535 min: 0 privileged: true runAsUser: rule: RunAsAny seLinux: rule: RunAsAny supplementalGroups: rule: RunAsAny volumes: - 'hostPath' - 'configMap' - 'emptyDir' - 'secret'
Стандартно создаем ServiceAccount:
--- apiVersion: v1 kind: ServiceAccount metadata: name: fluent-bit namespace: logging
Определяем ClusterRole и в ней определяем используемый PodSecurityPolicy (выделено болдом):
--- apiVersion: rbac.authorization.k8s.io/v1beta1 kind: ClusterRole metadata: name: psp:fluent-bit-read rules: - apiGroups: - '*' # - policy resources: # - podsecuritypolicies - '*' resourceNames: - psp.td-agent verbs: # - use - '*'
Я еще не до конца разобрался какие apiGroups, rules и resources необходимо использовать. Поэтому написал все (что разумеется не правильно). Если вы имеете, что сказать по этому поводу, скажите это в комментах.
И в конце ClusterRoleBinding:
--- apiVersion: rbac.authorization.k8s.io/v1beta1 kind: ClusterRoleBinding metadata: name: psp:fluent-bit-read roleRef: apiGroup: rbac.authorization.k8s.io kind: ClusterRole name: psp:fluent-bit-read subjects: - kind: ServiceAccount name: fluent-bit namespace: logging
И естественно все собираем вместе:
--- apiVersion: rbac.authorization.k8s.io/v1beta1 kind: ClusterRoleBinding metadata: name: psp:fluent-bit-read roleRef: apiGroup: rbac.authorization.k8s.io kind: ClusterRole name: psp:fluent-bit-read subjects: - kind: ServiceAccount name: fluent-bit namespace: logging
В DaemonSet указываем созданный нами serviceAccountName:
serviceAccountName: fluent-bit
Сформированные по вашему запросу или автоматически отчёты, как и многое другое, хранятся непосредственно в индексах elasticsearch.
Удалить конкретный report из индекса нельзя. Несмотря на запросы от пользователей, разработчики эластика отказываются предоставить такой инструмент и предлагают удалять индекс целиком.
Индексы с отчетами начинаются с .reporting и удаляются либо в kibana в разделе Index Management либо при помощи утилиты curator.
Пример action удаления индекса для curator:
actions: 1: action: delete_indices options: ignore_empty_list: True disable_action: True filters: - filtertype: pattern kind: prefix value: .reporting- - filtertype: age source: name direction: older timestring: '%Y.%m.%d' unit: days unit_count: 30
После переполнения файловой системы, elastic автоматически переводит индекс в режим read only.
После увеличения свободного пространства на диске отключить это режим для конкретного индекса можно при помощи следующей команды:
PUT app-index-2019.08.12/_settings { "index": { "blocks": { "read_only_allow_delete": "false" } } }
Если в режим только для чтения переведены и системные индексы, например .kibana. Тогда команда будет выглядеть так:
PUT _settings { "index": { "blocks": { "read_only_allow_delete": "false" } } }
Эти команды можно ввести в Kibana в разделе Dev Tools
Или в командной строке:
curl -XPUT -H "Content-Type: application/json" http://localhost:9200/_all/_settings -d '{"index.blocks.read_only_allow_delete": false}'
Input plugin tail имеет возможность обрабатывать многострочные лог файлы. Достаточно создать PARSER, при помощи которого можно определить первую строку такого сообщения. Все вроде бы работает, но мне захотелось не просто разобрать сообщение по тегам, но и в отдельном теге сохранить всё исходное сообщение.
По идее регулярное выражение, используемое в парсере для лога openfire
2018.04.16 10:48:15 ERROR [pool-2-thread-171]: org.jivesoftware.openfire.filetransfer.proxy.ProxyConnectionManager - Error processing file transfer proxy connection java.io.IOException: Only SOCKS5 supported at org.jivesoftware.openfire.filetransfer.proxy.ProxyConnectionManager.processConnection(ProxyConnectionManager.java:156) ~[xmppserver-4.3.2.jar:4.3.2] at org.jivesoftware.openfire.filetransfer.proxy.ProxyConnectionManager.access$200(ProxyConnectionManager.java:53) ~[xmppserver-4.3.2.jar:4.3.2] at org.jivesoftware.openfire.filetransfer.proxy.ProxyConnectionManager$1$1.run(ProxyConnectionManager.java:125) [xmppserver-4.3.2.jar:4.3.2] at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511) [?:1.8.0_202] at java.util.concurrent.FutureTask.run(FutureTask.java:266) [?:1.8.0_202] at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149) [?:1.8.0_202] at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624) [?:1.8.0_202] at java.lang.Thread.run(Thread.java:748) [?:1.8.0_202]
должно выглядеть как то так:
^(?<message>(?<time>\d{4}\.\d{2}\.\d{2} \d{2}:\d{2}:\d{2}) (?<loglevel>\w+) {1,2}(?<process>\[[^ ]*\]): .*)
Оказалось, что всё не так просто. Fluent bit находит первую строку, разбирает её, формирует теги. Вроде всё нормально.
Но остальные строки он тупо дописывает к последнему тегу регулярного выражения. И вместо того, что бы поместить их (как я ожидал) в тег message, он добавляет их к тегу process.
Поэтому, после обработки приходится «терять» теги time, loglevel и process используя следующее выражение вместо первого:
^(?<time>\d{4}\.\d{2}\.\d{2} \d{2}:\d{2}:\d{2}) (?<loglevel>\w+) {1,2}(?<process>\[[^ ]*\]): (?<message>.*)
Остальные строки многострочного лога будут добавляться к тегу message, но он не будет содержать время, loglevel и процесс.
Итого, первое регулярное выражение отлично работает с однострочными логами, но не будет работать с многострочными. Вот такая фича.
Fluent bit очень легкий агент по сбору логов и прочей информации для Ealstic Stack. Но, как и у всех программ, у него встречаются баги.
Например, существует проблема по формированию @timestamp различными input плагинами. Плагины tail и syslog считают, что текущее время компьютера находится во временной зоне UTC и добавляют к нему разницу между UTC и текущей тайм зоной. Это происходит даже в том случае, если текущее время системы сконфигурировано с учетом вашей временной зоны. А, например, плагин exec правильно читает текущее время системы.
В результате в индексах эластика записям присваивается не правильный @timestamp. И некоторые логи в индексе попадают в будущее 🙂
Бороться с этим можно только при помощи парсеров. В сообщениях, обрабатываемых fluent bit обязательно должно быть поле, содержащее время когда было сформировано событие. Обычно в лог файлах такое поле присутствует. Если его там нет, постарайтесь настроить программы так, что бы они помещали его в логи.
При определении секции [INPUT] используйте параметр Parser для однострочных логов. Или Parser_Firstline для многосторочных логов.
В секции PARSER, для проблемных плагинов явным образом указывайте параметр Time_Offset.
Например, для обработки информации получаемой из syslog используют стандартный парсер syslog-rfc3164. Поскольку input плагин syslog содержит баг, то придется в парсере добавить параметр Time_Offset. В результате конфигурация будет выглядеть как то так:
[INPUT] Name syslog Tag syslog Parser syslog-rfc3164 Listen 127.0.0.1 Port 5140 Mode tcp [PARSER] Name syslog-rfc3164 Format regex Regex /^\<(?<pri>[0-9]+)\>(?<time>[^ ]* {1,2}[^ ]* [^ ]*) (?<host>[^ ]*) (?<ident>[a-zA-Z0-9_\/\.\-]*)(?:\[(?<pid>[0-9]+)\])?(?:[^\:]*\:)? *(?<message>.*)$/ Time_Key time Time_Format %b %d %H:%M:%S Time_Format %Y-%m-%dT%H:%M:%S.%L Time_Keep On Time_Offset +0300
Итого, если вы видите, что события в эластике формируются в будущем, начинайте писать свой PARSER 🙂
Kibana конечно сама хорошо работает с клиентами. Но лучше спрятать её за нормальным http сервером. Там где мне пришлось развертывать ELK пользуются только Apache, поэтому в качеcтве примера используется данный web сервер.
Сначала в конфигурационном файле kibana.yml определим два параметра:
server.basePath: "/kibana" server.rewriteBasePath: true
Поскольку Apache выступает фронтендом для разных приложений, доступ к Kibana сделаем через путь http://any.body.com/kibana. В конфиге Apache определим Location.
<Location /kibana> ProxyPreserveHost On ProxyPass http://any.ip.com:5601/kibana ProxyPassReverse http://any.ip.com:5601/kibana </Location>
Собственно, все. Дальше можно включать необходимые вам плюшки web сервера.