Централизованное логирование
В данной статье мы рассмотрим вопрос централизованного логирования с использованием filebeat (гребаный спойлер) и graylog. В какой-то момент мы заметили, что машин в нашей инфраструктуре стало достаточно много, и чтобы посмотреть логи, приходилось иногда заходить на несколько машин и мучительно искать по множеству контейнеров, в этот момент мы поняли, что дальше жить так нельзя.
Постановка задачи стояла следующим образом:
Ранее мы уже пробовали централизованно собирать логи vector (https://vector.dev/), но нам хотелось иметь удобный веб-интерфейс для просмотра. А vector веб-интерфейса не имеет. В качестве веб-интерфейса, а заодно и местом централизованного хранения, мы выбрали graylog, фактически сейчас единственное open source решение, которое имеет обширный функционал и удобство использования. На момент написания статьи vector “из коробки” не умеет отправлять в gelf формате в graylog, но в одном из issues на github один энтузиаст смог сконфигурировать vector нужным образом (https://github.com/vectordotdev/vector/issues/4868#issuecomment-782740639). В общем vector в качестве решения для сбора логов отбросили.
Прежде чем отвечать на вопрос “Чем собирать”, нужно немного сказать про инфраструктуру: в инфраструктуре у нас все приложения запускаются в docker контейнерах, машин на момент написания у нас уже 16, а самих контейнеров около 60, контейнеры в свою очередь находятся под управлением nomad. Также важным моментом является то, что несколько машин в нашей инфраструктуре являются прерываемыми, они один раз в сутки перезапускаются. С инфраструктурой разобрались, теперь нужно определяться, чем собирать. Сперва наш взгляд упал на fluentd, fluentbit, т.к. они умели отправлять в gelf формате. В процессе настройки fluentd почему-то не слал больше 30 сообщений, разобраться, почему, нам так и не удалось, при использовании fluentbit в какой-то момент использования docker logging driver gelf и настройки fluentbit c output gelf выяснилось, что при недоступности fluentbit в момент рестарта контейнеров (на прерываемых машинах контейнеры один раз в сутки перезапускаются) они не запустятся и становятся неработоспособными. Мы поняли, что чем проще - тем лучше, и решили, что нужно собирать из файлов. В свою очередь настроили fluentbit для сбора из файлов, но fluentbit почему-то тоже доставлял не все логи, аналогично с fluentd определить, почему так происходит, не удалось. И в итоге мы остановились на filebeat в качестве сборщика логов (https://www.elastic.co/guide/en/beats/filebeat/current/how-filebeat-works.html), а graylog в качестве хранилища логов.
Еще один важный момент, который нужно учесть, это то, что graylog находится в другой инфраструктуре, т.е. прямой сетевой связности между graylog сервером и машинами, на которых запущен filebeat, нет. Т.о. мы связали это все через гейт, на котором у нас находится traefik (смотри диаграмму ниже). Теперь перейдем к настройке. Graylog мы запустили на отдельной машине в обычном docker-compose.yml файле.
version: "2.3" services: es01: image: elasticsearch:7.10.1 restart: unless-stopped network_mode: host ulimits: memlock: soft: -1 hard: -1 nofile: soft: 65536 hard: 65536 environment: - "ES_JAVA_OPTS=-Xms5g -Xmx5g" - ELASTIC_PASSWORD=test - discovery.type=single-node - cluster.max_shards_per_node=20000 - search.max_open_scroll_context=9000 volumes: - /etc/localtime:/etc/localtime:ro - /graylog/data/es01:/usr/share/elasticsearch/data labels: - "SERVICE_CHECK_HTTP=/_cluster/health" - "SERVICE_CHECK_INTERVAL=40s" - "SERVICE_CHECK_TIMEOUT=3s" - "SERVICE_CHECK_DEREGISTER_AFTER=10m" mongodb: image: mongo:4.2 restart: unless-stopped network_mode: host volumes: - /graylog/data/mongo:/data/db graylog: image: graylog/graylog:4.3.3 restart: unless-stopped depends_on: - mongodb - es01 network_mode: host volumes: - /graylog/data/graylog:/usr/share/graylog/data environment: - GRAYLOG_PASSWORD_SECRET=test - GRAYLOG_ROOT_PASSWORD_SHA2=shashasha - GRAYLOG_HTTP_EXTERNAL_URI=http://192.168.2.2:9000/ entrypoint: /usr/bin/tini -- wait-for-it 172.22.202.104:9200 -- /docker-entrypoint.sh labels: - "SERVICE_TAGS=hostname=graylog,traefik.enable=true,\ traefik.http.routers.graylog.rule=Host(`graylog.dev`),\ traefik.http.routers.graylog.entrypoints=websecure,\ traefik.http.routers.graylog.tls=true" - SERVICE_NAME=graylog - SERVICE_CHECK_DEREGISTER_AFTER=10m - SERVICE_9000_NAME=graylog - SERVICE_9000_CHECK_TCP=true - SERVICE_9000_CHECK_INTERVAL=40s - SERVICE_9000_CHECK_TIMEOUT=5s - SERVICE_9000_CHECK_DEREGISTER_AFTER=10m
Перейдем к конфигурации filebeat
Конфиг filebeat.yml у нас простейший
filebeat.inputs: - type: container paths: - /var/lib/docker/containers/*/*.log processors: - add_docker_metadata: match_source_index: 4 output.logstash: hosts: - graylog_host:graylog_port
Тут все просто, мы просто отправляем все логи всех контейнеров на хост graylog, в свою очередь graylog_host - это у нас хост с traefik, а в конфигурации traefik уже указан сам graylog, т.к. с гейта есть прямая сетевая связность с graylog.
Т.к. у нас nomad, то filebeat мы деплоим тоже в nomad, далее конфигурация filebeat job для деплоя его в nomad. Единственное, что здесь стоит отметить, так это то, что в env параметры GRAYLOG_HOST, GRAYLOG_PORT мы передаем ip адрес и порт на traefik. Также стоит обратить внимание, что тип job у нас system, что запускает filebeat на всех машинах. Также мы монтируем в контейнер filebeat каталог /filebeat/registry, для сохранения registry файла. Это необходимо для того, чтобы при отказе output filebeat отслеживал последние изменения, чтобы после того, как output вернется в рабочее состояние, доставить туда логи. Более подробно про registry файл можно почитать в документации filebeat https://www.elastic.co/guide/en/beats/filebeat/current/how-filebeat-works.html#_how_does_filebeat_keep_the_state_of_files
job "filebeat" { datacenters = ["a", "b", "c"] namespace = "default" update { max_parallel = 1 auto_revert = true auto_promote = false canary = 0 } type = "system" group "logging" { restart { mode = "delay" attempts = 2 interval = "1m" delay = "30s" } task "filebeat" { driver = "docker" kill_timeout = "30s" leader = true user = "root" env { SERVICE_IGNORE = "true" GRAYLOG_HOST = "GRAYLOG_HOST" GRAYLOG_PORT = "GRAYLOG_PORT" } config { auth { username = "test" password = "test" } image = "docker.registry/filebeat:7.10.1" force_pull = true volumes = [ "/var/lib/docker/containers:/var/lib/docker/containers:ro", "/var/run/docker.sock:/var/run/docker.sock:ro", "/filebeat/registry:/filebeat/registry:rw" ] } service { name = "filebeat" tags = [ "filebeat", "logging", "hostname=${attr.unique.hostname}" ] check_restart { grace = "1m" ignore_warnings = false } } logs { max_files = 3 max_file_size = 10 } resources { memory = 100 memory_max = 150 } } } }
Теперь перейдем к конфигурации traefik, здесь лишь фрагмент конфига traefik.yml, мы добавляем еще один entrypoint для graylog.
………… entryPoints: web: address: ":80" forwardedHeaders: insecure: true websecure: address: ":443" http: middlewares: - sslheader@file - trimwww@file graylog: address: ":11111" …………
Также не забываем сконфигурировать порт для работы с graylog в конфиге job nomad для traefik, ниже указан фрагмент traefik job.
job "traefik" { datacenters = [ "a", "b", "c"] ………………… type = "system" group "traefik" { network { mode = "host" port "http" { static = 80 to = 80 host_network = "private" } port "public" { static = 443 to = 443 host_network = "public" } port "ui" { to = 8080 host_network = "private" } port "graylog" { static = 11111 to = "11111" host_network = "private" } } task "traefik" { driver = "docker" kill_timeout = "30s" leader = true ……………… config { image = "traefik:v2.8.7" force_pull = true ports = ["http", "public", "ui", "graylog" ] ………………… }
Так выглядят entrypoints в веб-интерфейсе traefik.
Конфигурация роутера для graylog осуществляется в tcp router, вот как это выглядит в веб-интерфейсе traefik:
Также нужно сконфигурировать сервис (в терминологии traefik), куда мы будем отправлять логи в graylog, именно в конфигурации сервиса в traefik указывается сам ip и порт graylog сервера.
В веб-интерфейсе graylog вот так конфигурируется input (в терминологии graylog), т.е. входная точка, которая будет принимать логи на стороне graylog. Тут видно, что тип input у нас Beats.
Вот так выглядит сам веб-интерфейс graylog, где мы уже ищем конкретные логи:
Вот так достаточно просто (конечно после нескольких десятков часов исследований) настраивается централизованный сбор логов. Мы пойдем посмотрим, что у нас там в логах, а вы пока stay tuned.