Infra3: Мониторинг, Event Logging и сбор логов

Сегодня поговорим про то, что мы используем для мониторинга, логирования 👀событий, а также централизованного сбора логов инфраструктуры RNDSOFT. Эта статья продолжение небольшого цикла Infra про нашу инфраструктуру.


Infra1: Service Discovery, Cloud Native Proxy и деплой - первая статья цикла про различные современные штуки, которые мы используем для запуска и масштабирования приложений.

Infra2: Consul Registrator - вторая статья про замечательный инструмент.

Infra3: Мониторинг, Event Logging и сбор логов - третья статья про мониторинг.

Infra4: ...дальше видно будет...

Что мы используем?

Prometheus давно стал стандартом de facto, если у вас микросервисная архитектура. Мы его используем в гармоничном сочетании с consul service discovery, чтобы смотреть за нашими инстансами в облаках, виртуалках на bare metal, colocation и микросервисами, просто микросервисами. Далее добавим сюда ELK и сбор логов. Поскольку основной стэк, используемый у нас, строится на Ruby, то некоторые сервисы у нас скорее "макро", чем "микро", но на инфраструктуру это не влияет. Для мониторинга мы используем:

  • consul + consul-registrator
  • node-exporter
  • Prometheus
  • ELK
  • Vector

Теперь поговорим про каждый инструкмент в отдельности и об их взаимосвязях.

node-exporter

Инстансы виртуальных машин в облаках и на bare metal серверах мы мониторим с помощью node-exporter, который первым делом раскатывается на все новые машины при их создании. В docker-compose файле node-exporter мы указываем тэг monitoring в environment для того чтоб случилась магия service discovery:

Конфигурация для запуска node-exporter и настройка service discovery для него
Про то как как с помощью environment передавать настройки в консул более подробно в прошлой статье https://blog.rnds.pro/009-infra2

В данном случае node-exporter при указании этого параметра попадает в Consul, а из Consul через механизм SD сразу в Prometheus. Как говорится, что попадает в prometheus, остается в prometheus. Вот так выглядит в нашем Consul зарегистрированный node-exporter:

node-exporter в Consul

Сервисы в Consul у нас регистрируются с помощью consul-registrator. Он, как и node-exporter, безусловно ставится на каждую машину и автоматически регистрирует сервисы в Consul, если у них есть открытые порты о которых знает docker.

Для примера рассмотрим еще один exporter, который мы используем, ping-exporter. Он слушает порт 9427 и у него в environment также выставлен тег monitoring:

Конфигурационный файл docker-compose для ping-exporter

ping.yml:

targets:
 - 1.1.1.1

ping:
 interval: 2s
 timeout: 3s
 history-size: 42
 payload-size: 120
ping-exporter в Consul

Prometheus

Аналогичным образом мониторятся и другие веб-приложения. Сам Prometheus узнает о том, что в Consul что-то появилось с помощью конфигурационной настройки consul_sd_config, которая позволяет Prometheus получать информацию по API из Consul Catalog.

Схема реализации service discovery для Prometheus

ELK

Тут дела обстоят немного иначе. Поскольку инфраструктурные моменты (диски, сеть память и пр.) у нас закрыты с помощью Prometheus, то на долю ELK остаются только бизнес метрики и Event Logging. Для Ruby on Rails приложений используем официальный гем elastic-apm. Поскольку это рельса, то никакой сложной настройки не требуется - всё что надо просто работает.

Vector

Следующая задача, которую нам надо решить в распределённой инфраструктуре - это сбор логов. Контейнеры, которые запускаются оркестратором, распределены по большому количеству узлов, и старый, добрый ручной docker logs уже не может нам помочь. Для сбора логов из докера и логов от системных процессов мы используем Vector (https://vector.dev). Он используется в централизованной топологии. В этом случае Vector-агенты, установленные на каждой машине, собирают и отправляют данные на Vector-агрегатор, где логи складываются. Более подробно о конфигурации Vector тут:

Конфигурация vector-agent

Конфигурационный файл docker-compose для vector-agent

vector.toml:

[sources.in]
 type = "docker_logs"

[sinks.out]
 inputs = ["in"]
 type = "vector"
 address = "192.168.1.1:10000"
 buffer.type = "disk"
 buffer.when_full = "block"
 buffer.max_size = 1049000
 
# Если надо забирать логи из древного проекта с файловой системы

[sources.rails]
 type = "file"
 include = ["/app/log/production.log"] or.log"]  

[sources.sidekiq]
 type = "file"
 include = ["/app/log/sidekiq.log"]  

[sinks.out]
 inputs = ["rails", "sidekiq"]
 type = "vector"
 address = "192.168.1.1:9098"
 buffer.type = "disk"
 buffer.when_full = "block"
 buffer.max_size = 1049000

Конфигурация vector-aggregator

В первом блоке описывается источник docker контейнеры для сбора логов из docker контейнеров, во втором блоке app-in описывается сбор логов из vector-агента, конфиг которого был показан выше. В блоке app-out описывается куда будет складывать файлы vector-агрегатор и какой будет у них шаблон названия файлов. В разделе out описана конфигурация, куда будут складываться файлы из докер контейнеров.

Конфигурационный файл docker-compose для vector-aggregator

vector.toml:

[sources.docker]
 type = "vector"
 address = "0.0.0.0:9099"

[sources.app-in]
 type = "vector"
 address = "0.0.0.0:9098"

[sources.app1-in]
 type = "vector"
 address = "0.0.0.0:10000"

[...]

[sinks.app-out]
 encoding.codec = "ndjson"
 inputs = ["app-in"]
 type = "file"
 path = "/etc/vector/logs/{{ host }}/{{ file }}-%Y-%m-%d.log"

[sinks.app1-out]
 encoding.codec = "ndjson"
 inputs = ["app1-in"]
 type = "file"
 path = "/etc/vector/logs/{{ host }}/{{ file }}-%Y-%m-%d.log"


[sinks.out]
 encoding.codec = "ndjson"
 inputs = ["docker", "local-docker"]
 type = "file"
 path = "/etc/vector/logs/{{ container_name }}-%Y-%m-%d.log"

В данный момент эти логи складываются в файлы для ручного просмотра. Сейчас это proof of concept, в дальнейшем планируем собирать логи контейнеров в ElasticSearch или что-то подобное для более удобного просмотра и поиска.