Настраиваем логирование с помощью Loki и Grafana

от автора

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

В моем случае сервисы запускаются в Docker-контейнерах, поэтому было решено использовать следующие инструменты:

  • Loki — система агрегации логов;

  • Loki Docker Driver Client — Docker плагин для доставки логов до Loki;

  • Grafana — интерфейс визуализации и создания запросов;

Loki

Официальная документация

Loki — открытое программное обеспечение, разработанное Grafana Labs, которое предназначено для хранения, индексирования и обработки логов.

В отличие от других систем агрегации логов, Loki индексирует не сами логи, а их метаданные, а именно метки. При этом сами логи сжимаются и хранятся в различных объектных хранилищах или в файловой системе. (из офиц. документации).

В нашем примере мы будем хранить логи в файловой системе.

Loki Docker Driver Client

Официальная документация

Данный плагин собирает логи контейнеров и доставляет их до Loki. Помимо доставки этот драйвер также позволяет настраивать конвейер для логов, в рамках которого можно добавлять новые метки для индексирования, однако это тема для отдельного поста.

И так, приступим к настройке

Чтобы реализовать данный пример вам потребуется следующее:

  • Docker

  • Docker-compose

  • Loki Docker Driver Client

  • Любимая IDE

Установка Docker и Docker-compose

При локальной работе вполне допустимо использовать официальный набор инструментов Docker Desktop, который содержит все необходимое, а также графический интерфейс, упрощающий взаимодействие с образами, контейнерами и файловыми объемами (docker volumes). При реализации данного примера на сервере лучше использовать вариант установки для соответствующего дистрибутива ОС, на которой работает ваш сервер.

Установка Docker Desktop.

Установка Docker Engine и Docker Compose (для случаев когда работаем с сервером).

Установка Loki Docker Driver Client

Для установки Loki Docker Driver Client воспользуемся командой:

docker plugin install grafana/loki-docker-driver:2.9.4 --alias loki --grant-all-permissions

Создадим docker-compose проект

Начнем с того, что создадим новый пустой проект в своей любимой IDE и добавим в корень файл с названием docker-compose.yml. Для нашего примера мы будем использовать контейнер nginx в качестве объекта логирования. Добавим compose-service для nginx:

version: "3.9" services:   nginx:     image: nginx     hostname: nginx-entrypoint     container_name: nginx-entrypoint     restart: unless-stopped     environment:       TZ: "Europe/Moscow"     ports:       - 80:80     healthcheck:       test: [ "CMD", "curl", "-f", "http://localhost" ]       interval: 10s       timeout: 10s       retries: 20

Если мы сейчас запустим наш compose файл, и посмотрим в логи контейнера, то увидим логи запуска nginx, и потом раз в 20 секунд будет писаться лог о том, что пришел GET-запрос проверки здоровья.

Рис. 1 Отображение логов контейнера в интерфейсе Intellij IDEA

Рис. 1 Отображение логов контейнера в интерфейсе Intellij IDEA

Теперь добавим compose-сервисы для Grafana и Loki.

loki:   hostname: loki   image: grafana/loki:latest   environment:     TZ: ${SYSTEM_TIMEZONE:-Europe/Moscow}   ports:     - "3100:3100"   command: -config.file=/etc/loki/local-config.yaml  grafana:   hostname: grafana   environment:     - GF_PATHS_PROVISIONING=/etc/grafana/provisioning     # Включим доступ без авторизации     - GF_AUTH_ANONYMOUS_ENABLED=true # Не используйте **ANONYMOUS** настройки в проде     # Дадим права администратора при анонимном входе     - GF_AUTH_ANONYMOUS_ORG_ROLE=Admin     - TZ=${SYSTEM_TIMEZONE:-Europe/Moscow}   image: grafana/grafana:latest   ports:     - "3000:3000"

Запустим обновленный compose-файл. Теперь в нашем распоряжении уже 3 контейнера:

Рис. 2 Контейнеры

Рис. 2 Контейнеры

Если мы сейчас перейдем по адресу http://localhost:3000 то попадем на приветственную страницу Grafana, однако в данный момент там нет ничего интересного, поскольку мы не настроили ни одного источника данных, да и логи в Loki мы пока тоже не пишем.

Для того чтобы Grafana знала куда ей смотреть ей нужно дать об этом знать. Для этого создадим новый каталог в нашем проекте и назовем его grafana/provisioning/datasources. В этот каталог мы добавим конфигурационный YAML файл под названием loki.yaml. Этот файл должен иметь примерно следующее содержание:

apiVersion: 1 datasources:   - name: Loki # Отображаемое имя нашего источника данных     type: loki # Тип источника     access: proxy #     orgId: 1 # Идентификатор организации (единица адм. деления в Grafana) которой будет доступен источник     url: http://loki:3100 # Адрес откуда получать данные (здесь мы используем имя сервиса loki, т. к. компоуз создаст свою сеть в которой к контейнерам можно обращаться по имени compose-сервиса)     basicAuth: false # Для удобства демонстрации в Loki отключена авторизация, поэтому и тут она не зачем     isDefault: true #     version: 1 #     editable: false # Зпретим редактирование через интерфейс Grafana

Теперь нам нужно сделать так, чтобы данная конфигурация была доступна контейнеру Grafana при запуске, для этого добавим пару строк в конфигурацию сервиса grafana:

grafana:   hostname: grafana   environment:     - GF_PATHS_PROVISIONING=/etc/grafana/provisioning     - GF_AUTH_ANONYMOUS_ENABLED=true     - GF_AUTH_ANONYMOUS_ORG_ROLE=Admin     - TZ=${SYSTEM_TIMEZONE:-Europe/Moscow}   # Добавим проброс каталога файловой системы в файловую систему контейнра    volumes:     - ./grafana/provisioning/datasources:/etc/grafana/provisioning/datasources   image: grafana/grafana:latest   ports:     - "3000:3000"

Теперь, после пересоздания контейнера grafana, в разделе Explore у вас должен появиться источник данных Loki:

Рис. 3 Интерфейс источника данных Loki

Рис. 3 Интерфейс источника данных Loki

Теперь нам нужно сказать докеру, что логи нужно писать не просто так, а с использованием Loki Docker Driver Clinet. Для этого добавим yml-anchor, в котором опишем необходимую конфигурацию (yml-anchor нужен для того, чтобы можно было переиспользовать эту конфигурацию логирования без необходимости копировать код).

x-def-logging: &default-logging   logging:     # Указываем, какой драйвер использовать     driver: "loki"     options:       # Адрес Loki, куда складывать логи       # Обратите внимание, что здесь используется не имя сервиса loki, а локальный хост, на который проброшен порт Loki,       # это сделано потому, что логи будет писать docker engine, котрый расположен на хостовой машине,       # и он не знает имени хоста контейнера Loki, которое ему присвоил compose во внутренней сети проекта.       loki-url: "http://localhost:3100/loki/api/v1/push"       loki-batch-size: "100"       loki-retries: 2       loki-max-backoff: 1000ms       loki-timeout: 1s

Теперь когда логирование настроено, мы можем добавить anchor в конфигурацию сервиса nginx.

nginx:   image: nginx   hostname: nginx-entrypoint   container_name: nginx-entrypoint   restart: unless-stopped   <<: *default-logging   environment:     TZ: "Europe/Moscow"   ports:     - 80:80   healthcheck:     test: [ "CMD", "curl", "-f", "http://localhost" ]     interval: 10s     timeout: 10s     retries: 20

Запустим наш проект. Loki Docker Driver Client начинает экспортировать логи docker-контейнеров в Loki, поэтому нам становятся доступны такие метки как:

  • compose_project;

  • compose_service;

  • container_name;

  • host;

  • source.

Выберем фильтр по метке compose_service и установим значение равное nginx (имя нашего compose-сервиса) и выполним запрос.

Рис. 4 Фильтр на основе меток

Рис. 4 Фильтр на основе меток
Рис. 5 Диаграмма и логи

Рис. 5 Диаграмма и логи

Ответственное отношение к ресурсам

Теперь, когда мы настроили экспорт логов, имеет смысл подумать об экономии ресурсов. Нам далеко не всегда нужно хранить все логи за все время работы приложения. Чтобы сэкономить место на диске имеет смысл настроить время жизни логов в системе индексирования. Для этого создадим каталог loki в нашем проекте и положим туда следующий файл:

auth_enabled: false  server:   http_listen_port: 3100  common:   path_prefix: /loki   storage:     filesystem:       chunks_directory: /loki/chunks       rules_directory: /loki/rules   replication_factor: 1   ring:     kvstore:       store: inmemory  schema_config:   configs:     - from: 2020-10-24       store: boltdb-shipper       object_store: filesystem       schema: v11       index:         prefix: index_         period: 24h  ruler:   alertmanager_url: http://localhost:9093  # Отличие от стандарного конфигурационного файла loki, который идет из коробки # заключается в строках ниже. Здесь мы указываем для менеджера по умолчанию, # что он может удалять старые логи, а также, что в нашем понимании "старые логи" # (те которые старше 168 часов). table_manager:   retention_deletes_enabled: true   retention_period: 168h  analytics:   reporting_enabled: false

Теперь требуется пробросить эту конфигурацию в контейнер Loki с помощью docker-volume, как мы ранее делали с контейнером Grafana:

loki:   hostname: loki   image: grafana/loki:2.9.0   environment:     TZ: ${SYSTEM_TIMEZONE:-Europe/Moscow}   # Мы пробросили конфигурацию в файловую систему контейнера   volumes:     - ./loki/retention-config.yaml:/etc/loki/retention-config.yaml   ports:     - "3100:3100"   # и модифицировали команду запуска, чтобы использовалась наша конфигурация   command: -config.file=/etc/loki/retention-config.yaml

Et voilà, теперь логи старше 1 недели будут сами очищаться.

Исходники демонстрационного проекта доступны в моем gitverse (да да, поддерживаем отечественное ?).

А что вы думаете по поводу логирования в контейнеризирвоанных средах?

Какие инструменты используете?

С какими интересными кейсами сталкивались?

Пожалуйста напишите в комментариях!


ссылка на оригинал статьи https://habr.com/ru/articles/800781/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *