Централизованное логирование
Tags: graylog, infra, nomad, traefik
Published: 2023-04-10 17:26:43

---
title: Централизованное логирование
published: true
date: 2023-03-10 13:12:06 UTC
tags: graylog, infra, nomad, traefik
canonical_url: https://blog.rnds.pro/033-centralized-logging?utm_source=devto&utm_medium=post&utm_campaign=rnds
---

![](https://img1.teletype.in/files/03/12/0312a760-6c68-4a3d-bad1-4f9a6838e1eb.png)

В данной статье мы рассмотрим вопрос централизованного логирования с использованием filebeat (гребаный спойлер) и graylog. В какой-то момент мы заметили, что машин в нашей инфраструктуре стало достаточно много, и чтобы посмотреть логи, приходилось иногда заходить на несколько машин и мучительно искать по множеству контейнеров, в этот момент мы поняли, что дальше жить так нельзя.

Постановка задачи стояла следующим образом:

1. Чем собирать логи
2. Где хранить
3. Нужно не потерять логи при недоступности центрального хранилища логов

Ранее мы уже пробовали централизованно собирать логи vector ([https://vector.dev/](https://vector.dev/)), но нам хотелось иметь удобный веб-интерфейс для просмотра. А vector веб-интерфейса не имеет. В качестве веб-интерфейса, а заодно и местом централизованного хранения, мы выбрали graylog, фактически сейчас единственное open source решение, которое имеет обширный функционал и удобство использования. На момент написания статьи vector “из коробки” не умеет отправлять в gelf формате в graylog, но в одном из issues на github один энтузиаст смог сконфигурировать vector нужным образом ([https://github.com/vectordotdev/vector/issues/4868#issuecomment-782740639](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](https://www.elastic.co/guide/en/beats/filebeat/current/how-filebeat-works.html)), а graylog в качестве хранилища логов.

Еще один важный момент, который нужно учесть, это то, что graylog находится в другой инфраструктуре, т.е. прямой сетевой связности между graylog сервером и машинами, на которых запущен filebeat, нет. Т.о. мы связали это все через гейт, на котором у нас находится traefik (смотри диаграмму ниже). Теперь перейдем к настройке. Graylog мы запустили на отдельной машине в обычном docker-compose.yml файле.

![](https://lh3.googleusercontent.com/Wzf3hrjZEtkmygD2Myb8HKG9vYQdSzdaCS8UdGvXwEfO8hBBqSO_OVuI80kjGd3L26CzZSVCDNPCvxLLeVeIK-0HZS3NNW2dOCEOEtTp8VmHwu0MX--123ayMNjPmAKWZ0IC-2LgrNoAFt2VrbzPi3U7G0EMlDhIgANWdpcAHFQJQOnCAlcXivE_WQ)

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](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.

![](https://lh5.googleusercontent.com/mz-VGdWXCb8e5kk2aw3Qu8fcp3HrmCBv7BH5c2zlDJ2Q-B-xZfRd26WpeoDzCoQ-Kn9ldo7fMgoK6DUGWAvJjOLNhXmFQB5-MV_MMr9W2XtgGYeEclMuMV6i57VLj7wHUM-AjcoFKy0uvpg-zjRhauC1xRanuFZGUa62nnjt8mh0e9Lv_M7FiI_CSQ)

Конфигурация роутера для graylog осуществляется в tcp router, вот как это выглядит в веб-интерфейсе traefik:

![](https://lh4.googleusercontent.com/Ysxr2MAe0rRmJ0oB8A13O0N92gb8XX2kVsDvvRpEXv2PXxo6pWrK3o0TZthTuj9oXI61ZL7cULvAW4jpEGBaykShHkKOWJiO-lcL3D8xJOUEH7BTjLKtebrbUlw5ay41UNAXMVu9MiWNnYEdgM5ALW3yDkcMmPw0WpyXvcoIH51mfHj1ZywVkyPvgA)

Также нужно сконфигурировать сервис (в терминологии traefik), куда мы будем отправлять логи в graylog, именно в конфигурации сервиса в traefik указывается сам ip и порт graylog сервера.

![](https://lh6.googleusercontent.com/OCl-uznt6dQa2ele5-nFX-ONbgIOzshTwTHIGLklhWQRFmJJ6am12UCE2lvcwZ8VSWYkVWq5DsabEMRFFPAwhByOU8GzDvvCapa_8HiQ4k7CIDczWzHwugvFNcbq-A8nskxOIS_bTmBhAtWvVij2GXIK__IOunMQWz7jONWL-mGkrdHvPvbz1i38sg)

В веб-интерфейсе graylog вот так конфигурируется input (в терминологии graylog), т.е. входная точка, которая будет принимать логи на стороне graylog. Тут видно, что тип input у нас Beats.

![](https://lh5.googleusercontent.com/Re1exwPm1JsctCoXBboA5KERvxyg-4Z6t1KISMzWUIMIASXtHdF42_Y1sWljwjcRY4gUNaJMPVVQ4NNjNxjEYBoaqz1T4G-0KESu9WK7pErdLXupdtG4bIUX1yotqUAx2nwUEGc5QjAAa2XvN_erECMhvUl0ryFhEymRM-OFJJzba6ikGgPIKCvV3w)

Вот так выглядит сам веб-интерфейс graylog, где мы уже ищем конкретные логи:

![](https://lh3.googleusercontent.com/MvroZBekyjT2buttPXlO_nNx39TWqTxUdPz1hJr6CMhgAowPtyEoRo9TnD2eih88Z3ZwU0Ew8dD1KSXOuM2sIGzrX9QwD6Vw42f3G1Dg3NaY6otdBxQg4MF2gP461Il8dNu9PiNixWtIyB-IX7phrq-c8DOSF7dZ5MCJv2DuCCPSZEBC4R2fI100Ag)

Вот так достаточно просто (конечно после нескольких десятков часов исследований) настраивается централизованный сбор логов. Мы пойдем посмотрим, что у нас там в логах, а вы пока stay tuned.