Сегодня хочется поделиться о том как собирать и парсить логи в современном мире. На сайте уже есть одна статья о Fluent Bit, но сегодня речь пойдет о GrayLog. Мы не затрагиваем тему сравнений агрегации логов или сравнении программных продуктов для этого. Это в первую очередь опыт работы с конкретным продуктом.
Как уже повелось, все действия будем производить в контейнерах Docker. И первое с чего стоит начать, это непосредственно с установки GrayLog, идеально воспользоваться документацией с официального сайта: https://docs.graylog.org/docs/docker все очень подробно описано, в целом, я думаю, у вас не возникнет вопросов. В качестве хранения своей конфигурации и настроек GrayLog использует MongoDB, а в качестве хранения самих логов Elasticsearch при чем не старше определенной версии, но думаю вы об этом прочитали в документации. И так, что бы не ходить далеко создаем наш docker-compose.yaml с необходимыми для GrayLog компонентами.
version: '3.8'
services:
elasticsearch:
image: elasticsearch:6.8.23
container_name: elasticsearch
hostname: elasticsearch
restart: always
environment:
- "ES_JAVA_OPTS=-Xms512m -Xmx512m"
ulimits:
memlock:
soft: -1
hard: -1
cap_drop:
- NET_ADMIN
- SYS_ADMIN
volumes:
- ./elasticsearch:/usr/share/elasticsearch/data
networks:
- graylog
mongo:
image: mongo:5.0.11
container_name: mongodb
hostname: mongodb
restart: always
ulimits:
memlock:
soft: -1
hard: -1
cap_drop:
- NET_ADMIN
- SYS_ADMIN
volumes:
- ./mongodb:/data/db
networks:
- graylog
graylog:
image: graylog/graylog:4.2
container_name: graylog
hostname: graylog
entrypoint: /usr/bin/tini -- wait-for-it elasticsearch:9200 -- /docker-entrypoint.sh
restart: always
depends_on:
- mongo
- elasticsearch
ulimits:
memlock:
soft: -1
hard: -1
cap_drop:
- NET_ADMIN
- SYS_ADMIN
volumes:
- ./src/graylog/graylog.conf:/usr/share/graylog/data/config/graylog.conf
ports:
- 9000:9000
- 1514:1514
- 1514:1514/udp
- 12201:12201
- 12201:12201/udp
networks:
- graylog
Нужно помнить о тех требованиях которые предъявляют компоненты, в частности Elasticsearch будет требовать максимального количества областей памяти, которое вы можете задать с помощью: sysctl -w vm.max_map_count=262144
В данном примере я использую свой собственный файл конфигурации graylog.conf, он очень хорошо описан комментариями что бы понять какие настройки и как их производить, лучше всего перед редактированием прочитать: https://docs.graylog.org/docs/server-conf, если вы используете docker образ по-умолчанию, то все настройки будут по-умолчанию как в документации. Можно так же воспользоваться переменными окружения, и передать необходимые параметры контейнеру, но мне было интересно посмотреть сам файл и настроить его вручную, затем передать его в контейнер. Я не буду описать весь файл тут, он слишком велик для этого, но остановлюсь на самых основных моментах которые стоит поправить.
# Необходимо сгенерировать пароль длинной не менее 16 символов: pwgen -N 1 -s 96 password_secret = QTF370msQVFW5Sq0juYw8uYP # По-умолчанию имя пользователя будет admin, и таким же будет пароль, если ничего не менять, но лучше сделать это сразу, что бы мамкины хакеры не одолевали в процессе. root_username = yakunin # Так же, вы должны сгенерировать отпечаток пароля, это делается для сохранения сессии: echo -n yourpassword | shasum -a 256 root_password_sha2 = 2d27bea1609d7171a150a1a0f3ac8fc00edab138c6e2eafc4fa195711eb38541 # Укажите свой емалй и временную зону для удобства. root_email = "me@yakunin.dev" root_timezone = Europe/Moscow # Я бы рекомендовал спрятать GrayLog за Nginx и не выпускал бы сам интерфейс наружу, но в качестве примера мы будем слушать любые адреса на устройстве. http_bind_address = 0.0.0.0:9000 # Так как мы запускаем наш стек в docker, обращение к компонентам указываем по dns именам контейнеров. elasticsearch_hosts = http://elasticsearch:9200 # Тоже самое касается и MongoDB mongodb_uri = mongodb://mongo/graylog
Вообщем-то это самое главное для того что бы запустить наш стек, но я настоятельно рекомендую так же настроить размеры журналов и другие не маловажные параметры. Перед там как запустить наш проект, создадим папки для хранения данных и назначим на них необходимые права. А так же создадим папку src куда будем помещать необходимые нам файлы конфигурации для дальнейшей работы, я помещаю туда graylog.conf. И можно запускать проект, если вы копируете отсюда конфигурацию то вам необходимо создать свой файл graylog.conf и положить его в папку src/graylog/graylog.conf
> mkdir elasticsearch mongodb
> chmod 0775 elasticsearch mongodb
> mkdir -p src/graylog
> docker compose up --detach
[+] Running 4/4
⠿ Network graylog_graylog Created 0.0s
⠿ Container mongodb Started 0.7s
⠿ Container elasticsearch Started 0.6s
⠿ Container graylog Started
> docker ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
04b4fee88a91 graylog/graylog:4.2 "/usr/bin/tini -- wa…" 50 seconds ago Up 49 seconds (healthy) 0.0.0.0:1514->1514/tcp graylog
dba18a2fd378 elasticsearch:6.8.23 "/usr/local/bin/dock…" 50 seconds ago Up 49 seconds 9200/tcp, 9300/tcp elasticsearch
57c8958e643f mongo:5.0.11 "docker-entrypoint.s…" 50 seconds ago Up 49 seconds 27017/tcp mongodb
Наш стек запустился, если что-то пошло не так, смотрите логи docker logs -f $container_name. Если все хорошо, то на вашем хосте по порту 9000 будет доступен веб интерфейс GrayLog с тем логином и паролем который вы сделали на этапе генерации graylog.conf, заходим в веб интерфейс https://127.0.0.1:9000

После того как мы получили доступ, нам необходимо настроить приемник, их у GrayLog большое множество, но мы будем использовать GELF, это адаптированный json формат. Подробнее: https://docs.graylog.org/docs/gelf, для того что бы создать входящий поток переходим во вкладку System — > Inputs

Если все вышло правильно, то должно получится следующее:

Теперь Graylog готов к приему сообщений. Далее нам необходимо настроить отправку логов от контейнера в Graylog. У docker есть нативный драйвер fluentd, который далее распарсит и передаст логи в Graylog. Хорошо, доработаем наш docker-compose.yaml до следующего вида:
version: '3.8'
services:
elasticsearch:
image: elasticsearch:6.8.23
container_name: elasticsearch
hostname: elasticsearch
restart: always
environment:
- "ES_JAVA_OPTS=-Xms512m -Xmx512m"
ports:
- 9200:9200
ulimits:
memlock:
soft: -1
hard: -1
cap_drop:
- NET_ADMIN
- SYS_ADMIN
volumes:
- ./elasticsearch:/usr/share/elasticsearch/data
networks:
- graylog
mongo:
image: mongo:5.0.11
container_name: mongodb
hostname: mongodb
restart: always
ulimits:
memlock:
soft: -1
hard: -1
cap_drop:
- NET_ADMIN
- SYS_ADMIN
volumes:
- ./mongodb:/data/db
networks:
- graylog
graylog:
image: graylog/graylog:4.2
container_name: graylog
hostname: graylog
entrypoint: /usr/bin/tini -- wait-for-it elasticsearch:9200 -- /docker-entrypoint.sh
restart: always
depends_on:
- mongo
- elasticsearch
ulimits:
memlock:
soft: -1
hard: -1
cap_drop:
- NET_ADMIN
- SYS_ADMIN
volumes:
- ./src/graylog/graylog.conf:/usr/share/graylog/data/config/graylog.conf
ports:
- 9000:9000
- 1514:1514
- 1514:1514/udp
- 12201:12201
- 12201:12201/udp
networks:
- graylog
fluent-bit:
image: fluent/fluent-bit
container_name: fluent-bit
hostname: fluent-bit
ulimits:
memlock:
soft: -1
hard: -1
cap_drop:
- NET_ADMIN
- SYS_ADMIN
ports:
- 127.0.0.1:24224:24224
- 127.0.0.1:24224:24224/udp
volumes:
- ./src/fluentbit/fluent-bit.conf:/fluent-bit/etc/fluent-bit.conf
- ./src/fluentbit/parsers.conf:/fluent-bit/etc/parsers.conf
networks:
- graylog
nginx:
image: nginx
container_name: nginx
hostname: nginx
ulimits:
memlock:
soft: -1
hard: -1
cap_drop:
- NET_ADMIN
- SYS_ADMIN
ports:
- 80:80
depends_on:
- fluent-bit
logging:
driver: "fluentd"
options:
fluentd-address: 127.0.0.1:24224
tag: nginx.logs
networks:
- graylog
networks:
graylog:
driver: bridge
В первую очередь обратите внимание на то, что контейнеру nginx мы добавили драйвер вывода логов в fluentd, это очень удобно,а так же добавили ему тег, который он будет подписывать в начало. Это удобно когда у вас много сервисов. Далее. Мы запускаем сам fluent bit, который будет принимать логи от nginx обрабатывать их и отдавать в Graylog. Рассмотрим его конфигурационные файлы.
cat src/fluentbit/fluent-bit.conf
[SERVICE]
flush 1
log_Level info
daemon off
http_server on
http_listen 127.0.0.1
http_port 2020
storage.metrics on
parsers_File parsers.conf
[INPUT]
Name forward
Listen 0.0.0.0
Port 24224
Buffer_Chunk_Size 1M
Buffer_Max_Size 6M
[OUTPUT]
Name gelf
Match nginx.logs
Host graylog
Port 12201
Mode tcp
Gelf_Short_Message_Key log
[OUTPUT]
Name stdout
Match *
[FILTER]
Name record_modifier
Match nginx.logs
Allowlist_key log
[FILTER]
Name parser
Match nginx.logs
Key_Name log
Parser nginx_parser
Reserve_Data On
Preserve_Key On
[FILTER]
Name modify
Match nginx.logs
Add hostname ws.yakunin.dev
Add log_type nginx_logs
В целом описание всех директив можно прочитать на сайте fluent bit. И это нужно сделать! 😉 Но я поясню немного конфигурационный файл. И так, у нас есть входящий поток который мы вызываем по средствам forward, что подразумевает пересылку логов от контейнера к точке назначения. Далее мы говорим что мы хотим использовать в качестве точки назначения graylog, в формате gelf и обрабатывать все что приходит к нам с тегом nginx.logs. Далее мы пропускаем данные через фильтры, первый убирает все поля кроме поля log, это удобно что бы не забивать elasticsearch лишней информацией, далее второй фильтр парсит лог файл разбивая его на необходимые нам блоки. И последний добавляет 2 поля, с какого хоста мы отправляем наши логи и что это за логи. Теперь все готово, можно запускать стек.
> docker compose up --detach
[+] Running 6/6
⠿ Network graylog_graylog Created 0.1s
⠿ Container mongodb Started 0.8s
⠿ Container fluent-bit Started 0.8s
⠿ Container elasticsearch Started 0.7s
⠿ Container graylog Started 1.4s
⠿ Container nginx Started 1.2s
ws➜ graylog ᐅ docker ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
acb887ff0c8a graylog/graylog:4.2 "/usr/bin/tini -- wa…" 5 seconds ago Up 3 seconds (health: starting) 0.0.0.0:1514->1514/tcp, :::1514->1514/tcp, 0.0.0.0:9000->9000/tcp, 0.0.0.0:1514->1514/udp graylog
d8c66b6185e8 nginx "/docker-entrypoint.…" 5 seconds ago Up 4 seconds 0.0.0.0:80->80/tcp, :::80->80/tcp nginx
0c396307dc1a fluent/fluent-bit "/fluent-bit/bin/flu…" 5 seconds ago Up 4 seconds 2020/tcp, 127.0.0.1:24224->24224/tcp, 127.0.0.1:24224->24224/udp fluent-bit
a9aaeb34a082 elasticsearch:6.8.23 "/usr/local/bin/dock…" 5 seconds ago Up 4 seconds 0.0.0.0:9200->9200/tcp, :::9200->9200/tcp, 9300/tcp elasticsearch
6bcd4ac161e1 mongo:5.0.11 "docker-entrypoint.s…" 5 seconds ago Up 4 seconds 27017/tcp mongodb
Теперь делаем запрос к nginx, можно их командной строки:
> curl 127.0.0.1:80
После чего смотрим что у нас говорит fluent bit.
> docker logs -f fluent-bit
[0] nginx.logs: [1663857110.000000000, {"remote"=>"172.18.0.1", "host"=>"-", "user"=>"-", "time"=>"22/Sep/2022:14:31:50 +0000", "method"=>"GET", "path"=>"/", "code"=>"200", "size"=>"615", "referer"=>"-",
"agent"=>"curl/7.81.0", "log"=>"172.18.0.1 - - [22/Sep/2022:14:31:50 +0000] "GET / HTTP/1.1" 200 615 "-" "curl/7.81.0" "-"", "hostname"=>"ws.yakunin.dev", "log_type"=>"nginx_logs"}]
Отлично, тогда переходим в интерфейс, Search и в странице поиска выбираем временной промежуток, далее на изображение линзы, и видим наш запрос.

Если нажать на результат поиска, то можно увидеть как fluent bit добавил все необходимые поля а так же распарсил наш лог. Все это конечно же происходит на лету.

Вот собственно и все, таким образом вы можете добавить другие сервисы, добавить к ним теги, отдельно можно поговорить об парсинге, но об этом лучше написать другую статью. Надеюсь это было полезно. Как обычно, все файлы можно найти тут: https://git.yakunin.dev/yakunin/docker/-/tree/main/graylog-docker-fluentbit