Grafana Labs: Loki – распределённая система, теги и фильтры

Автор: | 02/07/2019
 

Предыдущий пост серии — Grafana Labs: Loki – сбор и просмотр логов.

Задача на сейчас — попробовать собрать распределённую систему:

  1. Grafana будет работать на одном хосте
  2. Loki — на втором
  3. Promtail — на третьем

Суть идеи такова:

  • имеется рабочее окружение, скажем Dev, включает в себя 2 хоста
  • имеется окружение мониторинга — хост с Grafana/Prometheus/etc

На хостах Dev-окружения — запустим promtail, которые будут пушить собранные логи в Loki, а Grafana будет ходить к этому серверу с Loki, и получать данные от неё.

В общем картина должна получиться такая (рисовалось в https://cloudcraft.co):

Только S3 и DynamoDB — в следующий раз.

Вроде должно заработать, пробуем.

Запускаем три сервера:

И к ним привязываем три домена для удобства:

11 promtail.setevoy.org.ua     
12 loki.setevoy.org.ua     
13 grafana.setevoy.org.ua     

Promtail

Начнём с хоста с promtail.

Тут мы хотим собирать логи nginx, и затем пушить их на хост с Loki.

Создаём файл promtail-conf.yml:

server:

  http_listen_port: 9080
  grpc_listen_port: 0

positions:

  filename: /tmp/positions.yaml

client:

  url: http://loki.setevoy.org.ua:3100/api/prom/push

scrape_configs:

  - job_name: system
    entry_parser: raw
    static_configs:
    - targets:
        - localhost
      labels:
        job: varlogs
        __path__: /var/log/*log

  - job_name: nginx
    entry_parser: raw
    static_configs:
    - targets:
        - localhost
      labels:
        job: nginx
        __path__: /var/log/nginx/*log

Создаём для него Compose файл promtail-compose.yml:

version: "3"

services:

  promtail:
    image: grafana/promtail:master
    volumes:
      - /home/admin/promtail-conf.yml:/etc/promtail/docker-config.yaml
      - /var/log:/var/log
    command: -config.file=/etc/promtail/docker-config.yaml

Запускаем:

root@ip-172-31-32-175:/home/admin# docker-compose -f promtail-compose.yml up -d

Проверяем:

root@ip-172-31-32-175:/home/admin# curl -s localhost:9080/metrics | head -5
HELP go_gc_duration_seconds A summary of the GC invocation durations.
TYPE go_gc_duration_seconds summary
go_gc_duration_seconds{quantile="0.0"} 3.6174e-05
go_gc_duration_seconds{quantile="0.25"} 3.6174e-05
go_gc_duration_seconds{quantile="0.5"} 4.6829e-05

Loki

Переходим к хосту с Loki.

Созаём конфиг loki-conf.yml:

auth_enabled: false

server:
  http_listen_port: 3100

ingester:
  lifecycler:
    address: 0.0.0.0
    ring:
      store: inmemory
      replication_factor: 1
  chunk_idle_period: 15m

schema_config:
  configs:
  - from: 0
    store: boltdb
    object_store: filesystem
    schema: v9
    index:
      prefix: index_
      period: 168h

storage_config:
  boltdb:
    directory: /tmp/loki/index

  filesystem:
    directory: /tmp/loki/chunks

limits_config:
  enforce_metric_name: false

В address: 0.0.0.0 указываем прослушивать все доступные интерфейсы.

Создаём Compose-файл loki-compose.yml:

version: "3"

services:
  loki:
    image: grafana/loki:master
    volumes:
      - /home/admin/loki-conf.yml:/etc/loki/local-config.yaml
    ports:
      - "3100:3100"
    command: -config.file=/etc/loki/local-config.yaml

Запускаем:

root@ip-172-31-38-97:/home/admin# docker-compose -f loki-compose.yml up -d
Starting admin_loki_1 ... done

Проверяем:

root@ip-172-31-38-97:/home/admin# curl -s localhost:3100/metrics | head -5
HELP cortex_cache_corrupt_chunks_total Total count of corrupt chunks found in cache.
TYPE cortex_cache_corrupt_chunks_total counter
cortex_cache_corrupt_chunks_total 0.0
HELP cortex_chunk_store_chunks_per_query Distribution of #chunks per query.
TYPE cortex_chunk_store_chunks_per_query histogram

Работает, доступен.

Пробуем подключиться с promtail-хоста:

root@ip-172-31-32-175:/home/admin# curl -s loki.setevoy.org.ua:3100/metrics | head -5
HELP cortex_cache_corrupt_chunks_total Total count of corrupt chunks found in cache.
TYPE cortex_cache_corrupt_chunks_total counter
cortex_cache_corrupt_chunks_total 0.0
HELP cortex_chunk_store_chunks_per_query Distribution of #chunks per query.
TYPE cortex_chunk_store_chunks_per_query histogram

Работает, доступен.

Grafana

Создаём Compose-файл grafana-compose.yml:

version: "3"

services:
  grafana:
    image: grafana/grafana:master
    ports:
      - "3000:3000"

Запускаем:

root@ip-172-31-43-174:/opt/loki# docker-compose -f grafana-compose.yml up -d

Проверяем UI — http://grafana.setevoy.org.ua:3000:

Настраиваем data source Loki:

Так…

И где логи?

nginx группы нет…

Проверяем логи promtail-а:

root@ip-172-31-32-175:/home/admin# docker logs -f admin_promtail_1
...
l=error ts=2019-02-07T12:13:23.938327315Z caller=client.go:129 msg="error sending batch" error="Post http://loki.setevoy.org.ua:3100/api/prom/push: dial tcp 52.16.65.121:3100: connect: connection refused"
level=error ts=2019-02-07T12:13:24.946257437Z caller=client.go:129 msg="error sending batch" error="Post http://loki.setevoy.org.ua:3100/api/prom/push: dial tcp 52.16.65.121:3100: connect: connection refused"

И почему?

Перезапускаем promtail:

root@ip-172-31-32-175:/home/admin# docker-compose -f promtail-compose.yml restart

И всё появилось:

Теги

Это всё хорошо, но планируется, то хостов будет пачка, а значит — надо добавить возможность выбора логов по тегам.

Запускаем ещё один сервер с promtail, аналогичный первому.

Повторяем настройку аналогично первому — создаём файл promtail2-conf.yml:

server:

  http_listen_port: 9080
  grpc_listen_port: 0

positions:

  filename: /tmp/positions.yaml

client:

  url: http://loki.setevoy.org.ua:3100/api/prom/push

scrape_configs:

  - job_name: system
    entry_parser: raw
    static_configs:
    - targets:
        - localhost
      labels:
        job: varlogs
        __path__: /var/log/*log

  - job_name: nginx
    entry_parser: raw
    static_configs:
    - targets:
        - localhost
      labels:
        job: nginx
        host: promtail2
        __path__: /var/log/nginx/*log

Он идентичен конфигу первого сервера, но тут мы добавили новый тег —  host: promtail2.

Аналогично — добавляем его на первом хсоте, как  host: promtail1:

...
      labels:
        job: nginx
        host: promtail1
        __path__: /var/log/nginx/*log
...

Сначала думал добавить в global секцию через external_labels, как в Prometheus:

global:

  scrape_interval:     15s
  external_labels:
    monitor: 'monitoring-production'

...

Но global нет:

Creating admin_promtail_1 … done
Attaching to admin_promtail_1
promtail_1  | level=error ts=2019-02-07T13:32:36.436335841Z caller=main.go:36 msg=»error loading config» filename=/etc/promtail/docker-config.yaml err=»yaml: unmarshal errors:\n  line 1: field global not found in type api.Config»

Т.к. документация пока толком не описывает доступные варианты — смотрим в исходники>>>:

...

type Config struct {
  ServerConfig    server.Config    `yaml:"server,omitempty"`
  ClientConfig    client.Config    `yaml:"client,omitempty"`
  PositionsConfig positions.Config `yaml:"positions,omitempty"`
  ScrapeConfig    []ScrapeConfig   `yaml:"scrape_configs,omitempty"`
}

...

type ScrapeConfig struct {
  JobName                string                           `yaml:"job_name,omitempty"`
  EntryParser            EntryParser                      `yaml:"entry_parser"`
  RelabelConfigs         []*relabel.Config                `yaml:"relabel_configs,omitempty"`
  ServiceDiscoveryConfig sd_config.ServiceDiscoveryConfig `yaml:",inline"`
}

...

Тут есть две структуры, которые описывают допустимые в конфигурации поля — общие, и для scrape_configs.

Создаём Compose на хосте promtail2:

version: "3"

services:

  promtail:
    image: grafana/promtail:master
    volumes:
      - /home/admin/promtail2-conf.yml:/etc/promtail/docker-config.yaml
      - /var/log:/var/log
    command: -config.file=/etc/promtail/docker-config.yaml
    ports:
      - "9080:9080"

Тоже аналогичен файлу с первого хоста, только в контейнер мапим файл /home/admin/promtail2-conf.yml, а не /home/admin/promtail-conf.yml.

На первом хосте — перезапускаем контейнер:

root@ip-172-31-32-175:/home/admin# docker-compose -f promtail-compose.yml restart

На втором — просто запускаем:

root@ip-172-31-44-176:/home/admin# docker-compose -f promtail-compose.yml up -d

И в логах ошибка error=»watcher.Add: no such file or directory»:

promtail_1  | level=info ts=2019-02-07T13:55:05.004415129Z caller=filetargetmanager.go:165 msg=»Adding target» key=»{job=\»varlogs\»}»
promtail_1  | level=info ts=2019-02-07T13:55:05.004815207Z caller=filetargetmanager.go:165 msg=»Adding target» key=»{job=\»nginx\»}»
promtail_1  | level=error ts=2019-02-07T13:55:05.004981321Z caller=filetargetmanager.go:168 msg=»Failed to create target» key=»{job=\»nginx\»}» error=»watcher.Add: no such file or directory»

А на первом всё хорошо:

level=info ts=2019-02-07T13:43:52.153207236Z caller=filetargetmanager.go:165 msg=»Adding target» key=»{job=\»varlogs\»}»
level=info ts=2019-02-07T13:43:52.153657621Z caller=filetargetmanager.go:165 msg=»Adding target» key=»{host=\»promtail1\», job=\»nginx\»}»
2019/02/07 13:43:52 Seeked /var/log/alternatives.log — &{Offset:19515 Whence:0}

WTF?

А сам nginx я установил?

root@ip-172-31-44-176:/home/admin# dpkg -l | grep nginx

Нет.

А зачем?…)

root@ip-172-31-44-176:/home/admin# ls -l /var/log/nginx
ls: cannot access '/var/log/nginx': No such file or directory

Собственно — теперь понятна причина сообщения «No such file or directory«.

Устанавливаем NGINX, перезапускаем promtail:

root@ip-172-31-44-176:/home/admin# apt -y install nginx
Проверяем лог:

promtail_1  | level=info ts=2019-02-07T13:59:54.422710535Z caller=filetargetmanager.go:165 msg=»Adding target» key=»{job=\»varlogs\»}»
promtail_1  | level=info ts=2019-02-07T13:59:54.423260616Z caller=filetargetmanager.go:165 msg=»Adding target» key=»{host=\»promtail2\», job=\»nginx\»}»

promtail_1  | level=info ts=2019-02-07T13:59:54.426593486Z caller=filetarget.go:269 msg=»start tailing file» path=/var/log/nginx/access.log
promtail_1  | level=info ts=2019-02-07T13:59:54.426667134Z caller=filetarget.go:269 msg=»start tailing file» path=/var/log/nginx/error.log

Гуд.

Переходим в Grafana:

Отлично — теги появились.

Фильтры запросов

Последним — быстрый пример выборки по логам.

«Документация» — тут>>>.

Сделаем запрос с «левого» хоста, что бы в логах было что искать:

16:24:39 [setevoy@venti ~] $ curl http://54.194.13.55/someuri

Теперь ищем данные с тегом job == nginx и host == promtail2:

Или добавим фильтр по IP 77.120.103.20:

Или с регулярным выражением:

{job="nginx", host="promtail2"} 77.120.[\d\.]

Или с условием «IP по маске 77.120.[\d\.], либо если в запросе есть слово someuri«:

{job="nginx", host="promtail2"} (77.120.[\d\.]|someuri)

В целом на этом всё.

Завтра попробую подключить DynamoDB для хранения индексов и S3 для хранения данных, и можно пробовать разворачивать систему на Staging.