В современном мире, меня, больше всего огорчает бесконтрольное использование памяти или вообще халатное к ней отношение. Я вырос в то время когда за каждый килобайт программисты боролись. А сейчас для многих 4 Gb это ерунда. Но сегодня речь не о разработке и памяти, а о логах. Тогда при чем тут память? А память тут вот при чем… В текущих реалиях для сбора, транспортировки и парсинга логов у нас есть следующие популярные инструменты: Logstash, Fluentd, Fluent Bit, Vector. Пожалуй первый самый популярный, так как входит в классический стек ELK ( Elasticsearch, Logstash, Kibana ). Но я сегодня хотел бы остановиться именно на Fluent Bit. Почему? Потому что: 1. Он требует всего 450 кб памяти для работы. 2. Он написан на С. 3. Он прекрасно работает в Docker контейнерах и самое главное в Kubernetes средах. 4. Конфигурационные файлы могут быть как в виде обычного текста так и в yaml формате. 5. У Docker есть встроенный драйвер Fluentd который сразу может писать в Fluent Bit. Это же прекрасно! Но, я хотел бы рассказать просто о сложном по этому начнем с простого, как установить и работать с ним.
Установка хорошо описана в официальной документации https://docs.fluentbit.io/manual/installation/linux/ubuntu по этому описывать её не имеет смысла, я же буду рассматривать в данном примере Docker образ и работу в Docker. Вы же можете установить его на голую систему с тем же конфигурационными файлами что бы получить тоже самое но на «железе». И так, если вы хотите так же как и я развернуть все это в Docker, то идем и читаем конфигурацию запуска и смотрим образ: https://hub.docker.com/r/fluent/fluent-bit/, все понятно, я сразу буду делать docker-compose.yml что бы в будущем добавить к нему Elasticsearch, Grafana или Kibana. По этому создаем следующую структуру файлов и каталогов:
.
├── conf
│ ├── fluent-bit.conf
│ └── parsers.conf
└── docker-compose.yml
1 directory, 3 files
Что это за файлы и для чего они? В каталоге conf я расположил 2 конфигурационных файла для самого Fluent Bit, основной файл конфигурации fluent-bit.conf и parsers.conf где будут храниться регулярные выражения или другие настройки парсеров. В корне каталога расположен docker-compose. Далее редактируем docker-compose.yml
# vim docker-compose.yml
version: '2.8'
services:
fluent-bit:
image: fluent/fluent-bit
container_name: fluent-bit
volumes:
- ./conf:/fluent-bit/etc
- /var/log:/var/log
Немного поясню, в секции volumes я «пробрасываю» локальные каталоги в docker, если вы устанавливаете fluent bit на голую машину, то файлы будут располагаться в каталоге /etc/fluent-bit. Но содержание будет тем же. Теперь сконфигурируем fluent bit для запуска, основной файл конфигурации fluent-bit.conf (все параметры файла, хорошо описаны в документации, не ленитесь её читать.):
# vim conf/fluent-bit.conf
[SERVICE]
flush 1
log_Level info
daemon off
parsers_File parsers.conf
http_server on
http_listen 127.0.0.1
http_port 2020
storage.metrics on
[INPUT]
# Указываем что нам необходимо следить за файлом.
Name tail
Path /var/log/fluent-bit.log
[FILTER]
# Говорим что нам необходимо обратиться к парсеру, парсить все что приходит в файл.
# для этого используем имя парсера yakunin который объявляется в файле parsers.conf
Name parser
Match *
Key_Name log
Parser yakunin
[OUTPUT]
# В данный момент все что будет после парсинга, нам необходимо выводить на экран.
name stdout
match *
Далее создадим и наполним наш файл парсинга, fluent bit прекрасно работает с регулярными выражениями без применения каких либо сторонних плагинов и прочего, просто из коробки. По этому создаем:
# vim conf/parsers.conf
[PARSER]
# Указываем имя парсера, по нему идет совпадение из основного файла конфигурации в
# директиве Parser
Name yakunin
# Говорим что формат парсинга это регулярные выражения.
# Входящие сообщения лога будут выглядеть так:
# Name: Yakunin V. Vasily Age: 42 City: Volgograd
Format regex
Regex /Name:\s(?<name>\w+.*)Age:.(?<age>\d+)\sCity:\s(?<city>\w+)\s+IP:\s+( <ip>\d+.\d+.\d+.\d+)/
# Говорим что группа age, должна быть как целое а не как текст.
Types age:integer
Все готово для запуска, можно запускать и смотреть:
# docker compose up
[+] Running 2/2
⠿ Network efk_default Created 0.1s
⠿ Container fluent-bit Created 0.1s
Attaching to fluent-bit
fluent-bit | Fluent Bit v1.9.3
fluent-bit | * Copyright (C) 2015-2022 The Fluent Bit Authors
fluent-bit | * Fluent Bit is a CNCF sub-project under the umbrella of Fluentd
fluent-bit | * https://fluentbit.io
fluent-bit |
fluent-bit | [2022/05/17 13:16:23] [ info] [fluent bit] version=1.9.3, commit=9eb4996b7d, pid=1
fluent-bit | [2022/05/17 13:16:23] [ info] [storage] version=1.2.0, type=memory-only, sync=normal, checksum=disabled, max_chunks_up=128
fluent-bit | [2022/05/17 13:16:23] [ info] [cmetrics] version=0.3.1
fluent-bit | [2022/05/17 13:16:23] [ info] [output:stdout:stdout.0] worker #0 started
fluent-bit | [2022/05/17 13:16:23] [ info] [http_server] listen iface=127.0.0.1 tcp_port=2020
fluent-bit | [2022/05/17 13:16:23] [ info] [sp] stream processor started
fluent-bit | [2022/05/17 13:16:23] [ info] [input:tail:tail.0] inotify_fs_add(): inode=1835058 watch_fd=1 name=/var/log/fluent-bit.log
Как видим все создалось и запустилось, в последней строке видим следующее: fluent-bit | [2022/05/17 13:16:23] [ info] [input:tail:tail.0] inotify_fs_add(): inode=1835058 watch_fd=1 name=/var/log/fluent-bit.log что говорит о том, что файл /var/log/fluent-bit.log взят на слежение и будет обработан как только туда что-то добавится. Ещё один плюс fluent bit в том, что выходные данные он предоставляет в формате json. А это просто прекрасно. И так добавим запись:
# echo "Name: Yakunin V. Vasily Age: 42 City: Volgograd IP: 10.10.10.10" >> /var/log/fluent-bit.log
И смотрим что получилось:
fluent-bit | [2022/05/17 13:16:23] [ info] [sp] stream processor started
fluent-bit | [2022/05/17 13:16:23] [ info] [input:tail:tail.0] inotify_fs_add(): inode=1835058 watch_fd=1 name=/var/log/fluent-bit.log
fluent-bit | [0] tail.0: [1652793708.089137491, {"name"=>"Yakunin V. Vasily ", "age"=>42, "city"=>"Volgograd", "ip"=>"10.10.10.10"}]
В последнем сообщении из вывода на экран мы видим что строка попавшая в файл, обработалась и вывелась, так как вывод мы указали как stdout, соответственно мы можем указать вывод например в Elasticsearch, а из него уже сможем визуализировать это в Grafana или в Kibana. Таким образом, можно парсить любые логи, любых программ. Так же fluent bit умеет фильтровать уже полученный результат скажем аналогично grep. Например, ниже приведен боевой конфиг который парсит логи sshd и если человеку или злоумышленнику не удалось авторизоваться, лог записывается и передается на сервер Elasticsearch.
[SERVICE]
flush 1
log_Level info
daemon off
parsers_File parsers.conf
http_server on
http_listen 127.0.0.1
http_port 2020
storage.metrics on
[INPUT]
name tail
path /var/log/auth.log
# Если у вас не один файл который обрабатывается, вы можете использовать
# теги, что бы помечать выход и по тегам обрабатывать дальше, в данном
# случае, мы обрабатываем все из файла и присваиваем этим данным тег gitlab.sshd.auth
tag gitlab.sshd.auth
[FILTER]
name parser
match gitlab.sshd.auth
key_name log
parser gitlab.sshd.parser
[FILTER]
# Из всех полученных данных после парсинга на интересует только те, которые
# содержат слово preauth. По этому грепаем это слово из полученных данных и
# передаем дальше.
name grep
match gitlab.sshd.auth
regex messages preauth
[OUTPUT]
# В данном случае мы говорим что будем использовать в качестве выхода Elasticsearch
name es
match gitlab.sshd.auth
host 127.0.0.1
port 9200
replace_dots on
index gitlab.sshd.auth
Retry_Limit False
Suppress_Type_Name On
На github можно найти не мало уже готовых парсеров под любые нужды в том числе и то что идет в комплекте с fluent bit. https://github.com/fluent/fluent-bit/blob/master/conf/parsers.conf Вот собственно и все. Каков итог?
- Мы имеем быстрый инструмент, который может отдавать данные в различные источники.
- Можно писать собственные регулярные выражения под любые типы логов, можем вычленять из результатов уже только то, что нам необходимо, а не писать все в базу.
- Интуитивно понятная настройка.
- Отличная документация.
- Интеграция из коробки с K8s и Docker.
- Выход по умолчанию в JSON.
В следующей статье, будем отдавать данные в Elasticsearch и визуализировать это в Kibana. Удачного парсинга друзья.
Спасибо за статью. Вот заинтересовало , а где тег log был объявлен? как фильтр поймет , что нужно отдавать на парсинг?