Prometheus

Introduction

Introduction

Overview

Особенности

Компоненты

Экосистема Prometheus содержит множество компонентов, многие из которых опциональны

Архитектура

architecture.png

Prometheus собирает метрики с каждой цели отдельно или через промежуточный push gateway (нужен для short-lived jobs)
Сохраняет их данные локально и запускает правила поверх этих данных для аггрегации или генерации алертов
Grafana или что-то другое работающее через API, может быть использовано для визуализации данных


Так как prometheus автономный, он не зависит от сетевых хранилищ (БД) или других удаленных сервисов
На него можно положиться когда вся инфраструктура сломана
А еще он не требует большой инфраструктуры для себя

Introduction

First steps

Скачай: https://prometheus.io/download/
По ссылке полно всего разного

Можно сразу запускать

root@two:~/prometheus-2.27.1.linux-amd64# ./prometheus
...

В этом случае конфиг возьмется из файла-примера prometheus.yml


Сам конфиг с комментариями

Секция global отвечает за глабальную конфигурацию prometheus сервера. Например интервал сбора данных

# my global config
global:
  scrape_interval:     15s # Set the scrape interval to every 15 seconds. Default is every 1 minute.
  evaluation_interval: 15s # Evaluate rules every 15 seconds. The default is every 1 minute.
  # scrape_timeout is set to the global default (10s).

Частоту сбора можно переопределить на уровне таргета
evaluation_interval - отвечает за частоту запуска правил для алретинга и создания новых временных рядов

# Alertmanager configuration
alerting:
  alertmanagers:
  - static_configs:
    - targets:
      # - alertmanager:9093

rule_files - содержит расположение файлов с правилами которые подгрузит prometheus

# Load rules once and periodically evaluate them according to the global 'evaluation_interval'.
rule_files:
  # - "first_rules.yml"
  # - "second_rules.yml"

Последний раздел содержит информацию о том какие ресурсы мониторит prometheus
Также после запуска он автоматически мониторит сам себя
На 9090 вместе с вебней доступны и метрики на http://prometheus.host:9090/metrics

# A scrape configuration containing exactly one endpoint to scrape:
# Here it's Prometheus itself.
scrape_configs:
  # The job name is added as a label `job=<job_name>` to any timeseries scraped from this config.
  - job_name: 'prometheus'

    # metrics_path defaults to '/metrics'
    # scheme defaults to 'http'.

    static_configs:
    - targets: ['localhost:9090']

Текущий конфиг можно смотреть тут - http://prometheus.host:9090/config

Concepts

Concepts

Data model

Prometheus хранит все данные как timeseries потоки timestamped значений пренадлежащих некоторой метрике и некоторому набору labeled dimensions
Кроме того он может генерировать временные timeseries из результатов запросов

Каждый временной ряд уникально идентифицируется по имени метрики и опциональных key-value пар, называемых labeles

Имя метрики может содержать буквы, цифры, подчеркивания и точки
[a-zA-Z_:][a-zA-Z0-9_:]*

Изменение лейбла включает добавление или удаление лейблов, а это влечет создание нового временного ряда
На картинке ниже видно как после изменения лейбла vandud_label, графиков стало два

Screenshot_2021_02_02-12_49_03-2021-06-20-at-0.png

Имя лейбла может содержать буквы, цифры и подчеркивание
[a-zA-Z_][a-zA-Z0-9_]*
Начинающиеся с двойного подчеркивания лейблы считаются зарезервированными для внутреннего использования

Значение лейбла может содержать любые unicode символы

Concepts

Metric types

Concepts

Jobs and instances

В терминах prometheus'a, эндпоинты, с которых он собирает метрики, называются instance
Инстансы могут иметь реплики, например для масштабируемости или доступности. Коллекция таких инстансов называется - job

scrape_configs:
  # The job name is added as a label `job=<job_name>` to any timeseries scraped from this config.
  - job_name: 'prometheus' # <--- job

    # metrics_path defaults to '/metrics'
    # scheme defaults to 'http'.

    static_configs:
    - targets: ['localhost:9090']
      labels:
        vandud_label: 'vandud2'

scrape_configs:
  - job_name: 'test1'
    static_configs:
    - targets: ['localhost:9091', 'localhost:9092']
      labels:
        vandud_label: 'vandud2'
  - job_name: 'test2'
    static_configs:
    - targets: ['localhost:9093']
      labels:
        vandud_label: 'vandud2'
    - targets: ['localhost:9094']
      labels:
        vandud_label: 'vandud2'

Разницы нет:
Screenshot_2021_02_02-12_49_03-2021-06-20-at-1008.png


Когда prometheus собирает данные, он вешает некоторые лейблы автоматически

Если какой-то из этих лейблов уже есть на метрике, то поведение зависит от опции honor_labels

Для каждого инстанса хранятся такие таймсерии:

Prometheus

Prometheus

Getting started

# Start Prometheus.
# By default, Prometheus stores its database in ./data (flag --storage.tsdb.path).
./prometheus --config.file=prometheus.yml

Добавляем в конфиг

scrape_configs:
  - job_name: 'prometheus'
    static_configs:
    - targets: ['localhost:9090']
  - job_name: 'vandud'
    static_configs:
    - targets: ['two.vandud.ru:80']

Рестартуем
И действительно появилось
Screenshot_2021_02_02-12_49_03-2021-05-21-at-0aoeu.png


Пока у нас не много метрик, нет проблемы в вычислении функций для них
Но когда их становится много, то вычисление начнет занимать значительное время
В таких случаях можно настроить особые правила по которым prometheus будет аггрегировать данные в новые time series

Эти правила можно описать в отдельном файле

groups:
- name: cpu-node
  rules:
  - record: job_instance_mode:node_cpu_seconds:avg_rate5m
    expr: avg by (job, instance, mode) (rate(node_cpu_seconds_total[5m]))

И подключать в основной конфиг так

rule_files:
  - 'prometheus.rules.yml'
Prometheus

Configuration/Configuration

https://prometheus.io/docs/prometheus/latest/configuration/configuration/


Prometheus настраивается через флаги командной строки и конфигурационный файл
Флагами указываются неизменяемые параметры
Конфигом настриваются джобы, инстансы, правила и прочее

./prometheus -h - покажет доступные флаги

Prometheus может релоадить конфигурацию во время работы. Если она нерабочая, то она не применится

Для инициализации релоада есть два пути:

Файлы с правилами также будут перезагружены


global:
  # How frequently to scrape targets by default.
  [ scrape_interval: <duration> | default = 1m ]

  # How long until a scrape request times out.
  [ scrape_timeout: <duration> | default = 10s ]

  # How frequently to evaluate rules.
  [ evaluation_interval: <duration> | default = 1m ]

  # The labels to add to any time series or alerts when communicating with
  # external systems (federation, remote storage, Alertmanager).
  external_labels:
    [ <labelname>: <labelvalue> ... ]

  # File to which PromQL queries are logged.
  # Reloading the configuration will reopen the file.
  [ query_log_file: <string> ]

# Rule files specifies a list of globs. Rules and alerts are read from
# all matching files.
rule_files:
  [ - <filepath_glob> ... ]

# A list of scrape configurations.
scrape_configs:
  [ - <scrape_config> ... ]

# Alerting specifies settings related to the Alertmanager.
alerting:
  alert_relabel_configs:
    [ - <relabel_config> ... ]
  alertmanagers:
    [ - <alertmanager_config> ... ]

# Settings related to the remote write feature.
remote_write:
  [ - <remote_write> ... ]

# Settings related to the remote read feature.
remote_read:
  [ - <remote_read> ... ]




Prometheus

Configuration/Recording rules

В prometheus'e есть два вида правил которые могут быть сконфигурированы и выполняться через регулярные интервалы:

Правила нужно описать в отдельном файле в yaml и включить в основной конфиг через директиву rule_files

Файлы и правилами не подгружаются автоматически в процессе работы. Чтобы prometheus обновил зарелоадил их, нужно отправить ему SIGHUP
Все правила из файла применятся только если все они описаны корректно. Если хоть где-то ошибка (хоть в одном из множетства файлов), то не применится ни одно

Syntax-checking rules

Проверить файл с правилами на корректность можно утилитой promtool

root@two:~/prometheus-2.27.1.linux-amd64# ./promtool check rules prometheus.rules.yaml
Checking prometheus.rules.yaml
  SUCCESS: 1 rules found
  
root@two:~/prometheus-2.27.1.linux-amd64# echo $?
0
root@two:~/prometheus-2.27.1.linux-amd64# ./promtool check rules prometheus.rules.incorrect.yaml
Checking prometheus.rules.incorrect.yaml
  FAILED:
prometheus.rules.incorrect.yaml: yaml: unmarshal errors:
  line 1: field gros not found in type rulefmt.RuleGroups

root@two:~/prometheus-2.27.1.linux-amd64# echo $?
1

Как видно выше, она выдает человекочитаемый результат, а так же соответствующий exitcode

Recording rules

Recording rules позволяют предподсчитывать частые или вычислительносложные выражения и сохранять их результат как новую таймсерию
Это особенно полезно для дашбордов (потому что они постоянно запрашивают одно и то же)

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

groups:
  [ - <rule_group> ]

Пример:

groups:
  - name: example
    rules:
    - record: job:http_inprogress_requests:sum
      expr: sum by (job) (http_inprogress_requests)
Prometheus

Configuration/Alerting rules

Правила алертинга позволяют определять состояния используя prometheus expression language и слать алерты во внешние сервисы

Когда выражение возвращает один или несколько элементов на протяжении указанного периода, алерт считается активным для набора лейблов

Defining alerting rules

Правила алертинга конфигурируются так же как и правила записи

groups:
- name: example
  rules:
  - alert: HighRequestLatency
    expr: job:request_latency_seconds:mean5m{job="myjob"} > 0.5
    for: 10m
    labels:
      severity: page
    annotations:
      summary: High request latency

Templating

Лейблы и аннотации могут быть затемплейчены через console templates
Переменная $labels содержит пары key/value алерт инстанса
Внешние лейблы могут вызываться через $externalLabels
Переменная $value содержит значение

groups:
- name: example
  rules:

  # Alert for any instance that is unreachable for >5 minutes.
  - alert: InstanceDown
    expr: up == 0
    for: 5m
    labels:
      severity: page
    annotations:
      summary: "Instance {{ $labels.instance }} down"
      description: "{{ $labels.instance }} of job {{ $labels.job }} has been down for more than 5 minutes."

  # Alert for any instance that has a median request latency >1s.
  - alert: APIHighRequestLatency
    expr: api_http_request_latencies_second{quantile="0.5"} > 1
    for: 10m
    annotations:
      summary: "High request latency on {{ $labels.instance }}"
      description: "{{ $labels.instance }} has a median request latency above 1s (current value: {{ $value }}s)"

Inspecting alerts during runtime

После того как у нас сработал алерт, мы можем получить time series по этому алерту, в метрике хранится состояние проверки
Screenshot_2021_02_02-12_49_03-2021-06-20-at-14aou.png
Screenshot_2021_02_02-12_49_03-2021-06-20-ataaa.png

Time Series ALERTS хранит только активные и ожидающие алерты
На скрине видно что данные перестали писаться в этот временной ряд после того как алерт перешел в ОК
Единица туда пишется просто так, чтобы писать хоть что-то, смысловой нагрузки единица не несет
Screenshot_2021_02_02-12_49_03-2021-06-20-at-14aeo.png

Sending alert notifications

Страница с алертами это хороший способ узнать что сломано прямо сейчас
Но это не полноценное решение для нотификаций
Следующим уровнем нужно добавить суммирование, ограничение скорости уведомлений, приглушение и зависимости алертов
В экосистеме prometheus эту роль занимает alertmanager

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

Прометеус может быть настроен на автодискаверинг доступных алертменеджеров

Prometheus

Configuration/Template examples

Прометеус поддерживает шаблонизирование в аннотациях и лейблах алертов
Темплейтирование основано на go templating

Simple alert field templates

alert: InstanceDown
expr: up == 0
for: 5m
labels:
  severity: page
annotations:
  summary: "Instance {{$labels.instance}} down"
  description: "{{$labels.instance}} of job {{$labels.job}} has been down for more than 5 minutes."

Шаблонизирование выполняется во время каждой итерации во время каждого правила, поэтому шаблонизирование должно быть простым
Если нужно сложное, то рекомендуется переходить по ссылке на консоль

Simple iteration

Консоли можно смотреть тут prometheus.server.local:9090/consoles

{{ range query "up" }}
  {{ .Labels.instance }} {{ .Value }}
{{ end }}

Например это отобразит список инстансов и подняты ли они

Переменная по имени . содержит текущие значения

https://prometheus.io/docs/prometheus/latest/configuration/template_examples/

Prometheus

Configuration/Template reference

Прометеус позволяет шаблонизировать аннотации и лейблы для алертов

https://prometheus.io/docs/prometheus/latest/configuration/template_reference/

Prometheus

Configuration/Unit Testing for Rules

Можно использовать утилиту promtool для тестирования правил

# For a single test file.
./promtool test rules test.yml

# If you have multiple test files, say test1.yml,test2.yml,test2.yml
./promtool test rules test1.yml test2.yml test3.yml

https://prometheus.io/docs/prometheus/latest/configuration/unit_testing_rules/

Prometheus

Configuration/HTTPS and authentication

Через ключ --web.config.file можно указать веб конфиг
Файл перечитывается на каждый http запрос
Поэтому изменения принимаются автоматически (без релоадов)


https://prometheus.io/docs/prometheus/latest/configuration/https/

Пример:

tls_server_config:
  cert_file: server.crt
  key_file: server.key

basic_auth_users:
  alice: $2y$10$mDwo.lAisC94iLAyP81MCesa29IzH37oigHC/42V2pdJlUprsJPze
  bob: $2y$10$hLqFl9jSjoAAy95Z/zw8Ye8wkdMBM8c5Bn1ptYqP/AXyV0.oy0S8m
Prometheus

Querying/Basics

Прометеус предоставляет язык запросов PromQL (Premetheus Query Language), который позволяет выбирать и аггрегировать timeseries данные в реальном времени
Резльтат запроса может быть отображен в виде графика или таблицы интерфейсе прометеуса или может быть "поглащен" внешней системой через HTTP API

Expression language data types

Выражения и подвыражения в promql могут выполняться в один из четырех типов данных:

Разные типы подходят для разных кейсов
Например только instant vector может быть отображен на графике

Literals

Строки можно определять в одинарных, двойных кавычках или тиках
PromQL следует принципам из Go, поэтому в двойных или одинарных кавычках \ начинает escape-sequence, за \ может следовать a, b, f, n, r, t, v или \
Конкретный символ может быть задан в octal формате \nnn или hexadecimal \xnn, \unnnn, \Unnnnnnnn
В тиках escape последовательности не обрабатываются. В отличие от Go, Prometheus не отменяет новую строку внутри тиков

"this is a string"
'these are unescaped: \n \\ \t'
`these are not unescaped: \n ' " \t`

Time series Selectors

gsih1_1lvftmhzgzuze3ctrlhci.png

Instant vector selectors

Instant vector selectors позволяют выбирать нобор timeseries и одно значение из каждого на какой-то конкретный timestamp

Пример ниже выберет все time series которые имеют http_requests_total в metric name:

http_requests_total

Можно сделать более тонкую фильтрацию добавив лейблы в фигрных скобках http_requests_total{label1="label1", label2="label2"}

Кроме знака равенства лейблы можно матчить следующими операторами:

Пример:

http_requests_total{environment=~"staging|testing|development",method!="GET"}

Vector selector должен иметь имя или не менее одного лейбла который матчится с непустой строкой

{job=~".*"}              # Bad! Потому что ".*" матчится с пустой строкой

{job=~".+"}              # Good!
{job=~".*",method="get"} # Good!

Screenshot_2021_02_02-12_49_03-2021-06-21-at-0aoeuaue.png

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

Каждая таймсерия идентифицируется именем и набором лейблов. Поэтому если собирать данные по какому-то имени и лейблам, а потом собирать по ним же другие данные, то график просто продолжит строиться из новых данных

Имена метрик можно матчить через внутренний лейбл __name__ (как на скрине выше)
http_requests_total -> {__name__="http_requests_total"}

А на следующем скриншоте наоборот
После изменения лейбла появился новый график
Screenshot_2021_02_02-12_49_03-2021-06-21-at-1aeuaeu.png

Имя метрики не может одним из этих слов:

on{} # Bad!

Обходной путь - использовать лейбл __name__ (если уж очень нужны метрики с такими именами)

Range Vector Selectors

Range Vector selection работает точно так же как и instant vector selection, только возвращается массив значений вместо одного значения

В квадратных скобках указывается за какой промежуток нужно выбрать значения

http_requests_total{job="prometheus"}[5m] # за последние 5 минут

Time Durations

Диапазон указывается числом с приставкой (unit):

Эти юниты (единицы измерения) можно комбинировать, конкатенируя их (1h20m30s), но юниты должны идти от большего к меньшему и могут встречаться лишь однажды (то есть нельзя 10h2h)

Offset modifier

offset позволяет сдвинуть таймсерию на указанное время назад и получить значение метрики из прошлого

http_requests_total offset 5m

Важно отметить что модификатор offset всегда должен идти после селектора
То есть это валидно:

sum(http_requests_total{method="GET"} offset 5m) // GOOD.

А это нет:

sum(http_requests_total{method="GET"}) offset 5m // INVALID.

Пример, питимунтный диапазон неделю назад:

rate(http_requests_total[5m] offset 1w)

Задавая отрицательный offset можно двигаться вперед во времени
Но для этого нужно включить этот функционал опцией --enable-feature=promql-negative-offset

@ modifier

Позволяет изменить evaluation time для конкретного instant или range vector в запросе
В векторе будут значения на момент времени указанный через @ в timestamp
Другими словами этот модификатор позволяет вернуть значение метирки на конкретный момент времени

Но его тоже нужно включать опцией --enable-feature=promql-at-modifier

И он так же должен следовать за селектором

sum(http_requests_total{method="GET"} @ 1609746000) // GOOD.
sum(http_requests_total{method="GET"}) @ 1609746000 // INVALID.

Работает и с range vectors
От указанного таймстемпа отступит указанное количество времени и покажет значения которые входят в диапазон между timestamp и timestamp-period
Screenshot_2021_02_02-12_49_03-2021-06-21-at-1timestamp.png

В следующих двух примерах это так же видно

Screenshot_2021_02_02-12_49_03-2021-06-21-at-12exst.png
Screenshot_2021_02_02-12_49_03-2021-06-21-at-12nonexist.png

At модификатор можно комбинировать с offset
Не важно в каком порядке
offset будет применяться относительно timestamp
Запросы ниже вернут одно и то же

# offset after @
http_requests_total @ 1609746000 offset 5m
# offset before @
http_requests_total offset 5m @ 1609746000

Так же можно использовать функции start() и end()

Subquery

Подзапрос позволяет запустить мгновенный (istant) запрос для заданного диапазона и разрешения (resolution)
Результат подзапроса это range vector

instant_query [<range>:<resolution>]

Пример

quantile_over_time(0.95, rate(node_network_receive_bytes_total[5m])[1h:1m])

Comments

Комменты начинаются с решетки, коммент - вся строка

# This is a comment

Gotchas

https://prometheus.io/docs/prometheus/latest/querying/basics/#gotchas

Prometheus

Querying/Operators

Язык запросов поддерживает логические и арифметические операторы

Arithmetic binary operators

Их можно указывать между scalar/scalar, vector/scalar, и vector/vector парами
(vector - instant vector)

При vector/scalar оператор применяется к каждому элементу вектора и числу

При vector/vector оператор применяется к каждому элементу из левого вектора и сответствующему из правого (матчинг по лейблам, лейблы должны совпадать полностью)
Результатом становится результирующий вектор без имени
Несовпавшие элементы отбрасываются
vector-matching-1.png

Prometheus

Storage

Прометеус включает в себя локальную TSDB на диске
Но также может интегрироваться с remote стораджами

Local storage

TSDB хранит данные в кастомном высокоэффективном формате на локальном диске

Группирует блоки данных по два часа
Каждый двухчасовой блок состоит из директории которая хранит:

Сэмплы в папке chunks сгруппированы в один или более файл размером до 512М каждый

Когда таймсерии удаляются через API, команда удаления записывается в tombstone файл (вместо немедленного удаления данных /querying/api/#delete-series) до следующего сжатия (compaction)

Текущий двухчасовой блок данных хранится в памяти и не является полностью постоянным. WAL (write ahead log) защитит от потери данных из памяти (при рестарте), этот лог может быть проигран (replayed) для восстановления данных
WAL файлы хранятся в папке wal сегментами по 128M. В них хранятся сырые данные, которые не могут быть сжаты и их размер значительно больше обычных блоков данных
Хранится не менее трех wal-файлов, высоконагруженные серверы могут иметь больше трех чтобы уместить двухчасовой период

./data
├── 01BKGV7JBM69T2G1BGBGM6KB12
│   └── meta.json
├── 01BKGTZQ1SYQJTR4PB43C8PD98
│   ├── chunks
│   │   └── 000001
│   ├── tombstones
│   ├── index
│   └── meta.json
├── 01BKGTZQ1HHWHV8FBJXW1Y3W0K
│   └── meta.json
├── 01BKGV7JC0RY8A6MACW02A2PJD
│   ├── chunks
│   │   └── 000001
│   ├── tombstones
│   ├── index
│   └── meta.json
├── chunks_head
│   └── 000001
└── wal
    ├── 000000002
    └── checkpoint.00000001
        └── 00000000

Прометеус не кластеризуется и не реплицируется, поэтому с ним нужно обходиться как с single node database
Рекомендуется использовать raid и snapshots
При правильной архитектуре данные можно хранить годами

Как альтернатива может быть использован какой-либо external storage через remote read/write API

Compaction

Двухчасовые блоки компануются в фоне в долгие блоки данных

У Прометеуса есть несколько флагов для конфигурирования локального хранилища
Вот наиважнейшие из них:

Prometheus хранит 1-2 байта на сэмпл. Поэтому для расчета того сколько дискового пространства потребуется для сервера, нужно воспользоваться этой формулой:
needed_disk_space = retention_time_seconds * ingested_samples_per_second * bytes_per_sample

Чтобы снизить размер поглощаемых данных можно снизить количество таймсерий (убавить целей или убавить таймсерий на цель), или можно увеличить scrape interval

Если локальный сторадж повредился по каким-то причинам, то лучшая стратегия это выключить prometheus и удалить storage directory, также можно попробовать удалить только какой-то конкретный блок данных или wal
Этот метод имеет место быть потому что Прометей не годится для длительного хранения данных, внешние решения сделают это лучше и надежнее

Если одновременно указаны и time и size retention, то будет использован тот кто сработает первее

Блоки удаляются целиком за раз после того как их срок действия полностью истек (поэтому это может занять до двух часов)

Remote storage integrations

Локальный сторадж имеет ограничения, поэтому вместо того чтобы решать проблемы кластеризации, прометеус предоставляет интерфейс для интеграции с remote storages

Screenshot_2021_02_02-12_49_03-2021-06-24-at-2aoeuaoeu.png

Чтобы включить приемник (receiver) нужно указать флаг --enable-feature=remote-write-receiver, тогда дефолтный эндпоинт для remote write будет /api/v1/write

Note that on the read path, Prometheus only fetches raw series data for a set of label selectors and time ranges from the remote end. All PromQL evaluation on the raw data still happens in Prometheus itself. This means that remote read queries have some scalability limit, since all necessary data needs to be loaded into the querying Prometheus server first and then processed there. However, supporting fully distributed evaluation of PromQL was deemed infeasible for the time being.

Backfilling for Recording Rules

Когда создается новое recording rules, для него еще нет исторических данных, они появляются со временем
Утилита promtool позволяет сгенерировать исторические данные для правила

$ promtool tsdb create-blocks-from rules --help

Добавил правило

$ cat rule
groups:
  - name: example
    rules:
    - record: job:load_average_1:rate
      expr: rate(node_load1[60m])

Данных пока нет

Screenshot_2021_02_02-12_49_03-2021-06-24-at-aueeouaeoua.png

$ sudo !!
sudo promtool tsdb create-blocks-from rules --start 1624547613 --end 1624554804 --url http://84.201.175.11:9090 --output-dir=/var/lib/prometheus/metrics2 /etc/prometheus/rule
level=info backfiller="new rule importer from start" 24Jun2115:13UTC=" to end" 24Jun2117:13UTC=(MISSING)
level=info backfiller="processing group" name=/etc/prometheus/rule;example
level=info backfiller="processing rule" id=0 name=job:load_average_1:rate

И данные появились

Screenshot_2021_02_02-12_49_03-2021-06-24-at-2nthnthnth.png

Но с этим нужно быть аккуратным
https://prometheus.io/docs/prometheus/latest/storage/#usage-0

Prometheus

Federation

Федерация позволяет скрейпить выбранные таймсерии другим прометеус сервером

Use cases

Обычно федерация используется для масштабируемых сетапов или для переноса связанных метрик из одного прометеуса в другой

Hierarchical federation

Иерархическая федерация позволяет масштабироваться до сред с десятками ДЦ и миллионами нод
В таком случае топология похожа на дерево, где наверху находятся high-level prometheus собирающие агрегированные данные и имеющие большое количество подчиненных серверов
Низкоуровневые сервера могут собирать и хранить высокоточные данные, и отправлять вышестоящему серверу обработанные
Тогда у нас будет общая картина со всех ДЦ в одном главном прометеусе, и высокоточная картина в каждом ДЦ по отдельности

Cross-service federation

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

Configuring federation

Любой прометеус сервер дает доступ к данным по эндпоинту /federate

Screenshot_2021_02_02-12_49_03-2021-06-24-at-23stsnnnn.png

Чтобы настроит федерацию, нужно указать серверу назначения что нужно скрейпить метрики с сервера источника с эндпоинта /federate
Также нужно указать honor_labels чтобы сервер назначения не перезаписывал лейблы
Пример конфига

scrape_configs:
  - job_name: 'federate'
    scrape_interval: 15s

    honor_labels: true
    metrics_path: '/federate'

    params:
      'match[]':
        - '{job="prometheus"}'
        - '{__name__=~"job:.*"}'

    static_configs:
      - targets:
        - 'source-prometheus-1:9090'
        - 'source-prometheus-2:9090'
        - 'source-prometheus-3:9090'
Prometheus

HTTP SD

Прометеус предоставляет http service discovery который дополняет поддерживаемые sd механизмы и является альтернативой для file based sd

Comparison between File-Based SD and HTTP SD

Item File SD HTTP SD
Event Based Yes, via inotify No
Update frequency Instant, thanks to inotify Following refresh_interval
Format Yaml or JSON JSON
Transport Local file HTTP/HTTPS
Security File-Based security TLS, Basic auth, Authorization header, OAuth2

inotify - функционал ядра позволяющий приложениям подписываться на события связанные с файлами

HTTP SD сервис нужно писать самостоятельно на основе этой доки
https://prometheus.io/docs/prometheus/latest/http_sd/

Prometheus

Management API

Прометеус предоставляет набор APIs для упрощения автоматизации и интеграции


Возвращают 200 когда все ок доступны из коробки

GET /-/healthy
GET /-/ready

Включаются флагом --web.enable-lifecycle

PUT  /-/reload # SIGHUP
POST /-/reload

PUT  /-/quit # SIGTERM
POST /-/quit
Prometheus

Disabled Features

Список выключенных по умолчанию фич
Kоторые включаются через ключ --enable-feature=feature1,feature2:

--enable-feature=promql-at-modifier - Позволяет указать таймстемп на который нужно обсчитать запрос
--enable-feature=expand-external-labels - Заменяет ${var} или $var в external_labels на текущие переменные окружения
--enable-feature=promql-negative-offset - Позволяет перемещаться по таймсерии в будущее чтобы делать предикты
--enable-feature=remote-write-receiver - Разрешает remote write реквесты от других прометеусов
--enable-feature=exemplar-storage - /disabled_features/#exemplars-storage

Visualization

Visualization

Expression browser

Экспрешн браузер доступен на эндпоинте /graph у прометеуса
Он позволяет вводить выражения и просматривать результат их выполнения в виде таблицы или графика
Screenshot_2021_02_02-12_49_03-2021-07-01-at-0aoeuaou.png

Прежде всего это используется для ad-hoc запросов и отладки

Для нормальных графиков рекомендуется использовать графану

Visualization

Grafana

О том как подключить графану к прометеусу:
https://prometheus.io/docs/visualization/grafana/

Visualization

Console templates

Консольные шаблоны позволяют делать дашборды
Для шаблонизации используется язык шаблонов из golang
В качестве веб-сервера выступает сам прометеус

Getting started

The example consoles have 5 parts:

  1. A navigation bar on top
  2. A menu on the left
  3. Time controls on the bottom
  4. The main content in the center, usually graphs
  5. A table on the right

Example
Screenshot_2021_02_02-12_49_03-2021-07-01-at-1thththt.png


Очень прикльная штука, подробнее читать тут: https://prometheus.io/docs/visualization/consoles/

Exposition formats

Метрики выставляются для прометеуса в простом текстовом формате
Раньше можно было использовать protobuf, но больше нельзя (из-за некоторых особенностей это работает менее эффективно)

Text-based format

Начиная со 2 версии все приложения, которые предоставляют метрики в формате пригодном для прометеуса, должны предоставлять их в текстовом формате

Basic Info:

Аспект Описание
Транспорт HTTP
Кодировка UTF-8, \n - конец строки
HTTP Content-Type text/plain; version=0.0.4
Optional HTTP Content-Encoding gzip
Преимущества - Человекочитаемый
- Быстро обрабатывается
- Читается построчно (вместе с docstrings)
Ограничения - Многословность
- Типы не являются неотъемлемой частью синтаксиса, поэтому проверять их почти невозможно
- Издержки на парсинг
Поддерживаемые примитивы - Counter
- Gauge
- Histogram
- Summary
- Untyped

Текстовый формат метрик premetheus строко-ориентированный
Строки разделяются символом переноса строки \n
Последняя строка так же заканчивается этим символом
Screenshot_2021_02_02-12_49_03-2021-07-01-at-17shshsh.png

Пустые строки игнорируются


Внутри строки ее элементы разделются более чем одним пробелом или табом
Начальные и конечные пробелы/табы игнорируются

Строки у которых первый непустой символ это решетка # - комментарии

# HELP go_goroutines Number of goroutines that currently exist.
# TYPE go_goroutines gauge
go_goroutines 9

Если после решетки идет не HELP или TYPE, то комментарий полностью игнорируется (то есть он для людей)

Каждая строка описывается следующим EBNF выражением:

metric_name [
  "{" label_name "=" `"` label_value `"` { "," label_name "=" `"` label_value `"` } [ "," ] "}"
] value [ timestamp ]

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

histogram и summary сложно представимы в текстовом виде, поэтому применяются следующие соглашения:


Ниже пример представления метрик

# HELP http_requests_total The total number of HTTP requests.
# TYPE http_requests_total counter
http_requests_total{method="post",code="200"} 1027 1395066363000
http_requests_total{method="post",code="400"}    3 1395066363000

# Escaping in label values:
msdos_file_access_time_seconds{path="C:\\DIR\\FILE.TXT",error="Cannot find file:\n\"FILE.TXT\""} 1.458255915e9

# Minimalistic line:
metric_without_timestamp_and_labels 12.47

# A weird metric from before the epoch:
something_weird{problem="division by zero"} +Inf -3982045

# A histogram, which has a pretty complex representation in the text format:
# HELP http_request_duration_seconds A histogram of the request duration.
# TYPE http_request_duration_seconds histogram
http_request_duration_seconds_bucket{le="0.05"} 24054
http_request_duration_seconds_bucket{le="0.1"} 33444
http_request_duration_seconds_bucket{le="0.2"} 100392
http_request_duration_seconds_bucket{le="0.5"} 129389
http_request_duration_seconds_bucket{le="1"} 133988
http_request_duration_seconds_bucket{le="+Inf"} 144320
http_request_duration_seconds_sum 53423
http_request_duration_seconds_count 144320

# Finally a summary, which has a complex representation, too:
# HELP rpc_duration_seconds A summary of the RPC duration in seconds.
# TYPE rpc_duration_seconds summary
rpc_duration_seconds{quantile="0.01"} 3102
rpc_duration_seconds{quantile="0.05"} 3272
rpc_duration_seconds{quantile="0.5"} 4773
rpc_duration_seconds{quantile="0.9"} 9001
rpc_duration_seconds{quantile="0.99"} 76656
rpc_duration_seconds_sum 1.7560473e+07
rpc_duration_seconds_count 2693

Alerting

Alerting

Alertmanager

Алертинг в прометеусе разделен на две части
У прометеуса есть alerting rules, которые шлют алерты в alertmanager
Alertmanager управляет приходящими в него алертами, silence'ит их, подавляет, аггрегирует и шлет алерты наружу через email, звонки и чаты

Настройка состоит из трех шагов:


Alertmanager подбирает алерты которые в него прислали, и может дедублицировать их, группировать и роутить у нужные места

Grouping

Группировка категоризирует алерты по их природе и объединяет в один алерт
Это очень полезно при больших авариях, когда фэйлится множество систем и одномоментно может быть отправлено от сотни или тысячи алертов (например при network partitioning)

Inhibition

Подавление позволяет сдерживать нотификации для некоторых алертов если некоторые другие алерты уже были отосланы (незачем слать сообщения о недоступности сервиса в кластере если было отослано сообщение о недоступности самого кластера)

Это конфигурируется в конфиге alertmanager'a

Silences

Сайленсинг позволяет замьютить некоторые алерты на определенное время
Silence'ы конфигурируются на основе матчеров как routing tree

Пришедший алерт проверяется, подходит ли он для какого-либо матчера среди активных silences. Если подходит то алерт не будет отослан

Silences конфигурируются в web-интерфейсе alertmanager'a

Client behavior

Alertmanager имеет специальные требования по поведению клиентов, это полезно в сложных кейсах, когда в качестве клиента используется не prometheus

High Availability

Alertmanager может быть собран в HA кластер. Это конфигурируется через флаги --cluster-*
Здесь важно не балансировать трафик между прометеусом и алертменеджерами, а вместо этого указать прометеусу список всех алертменеджеров

Alerting

Configuration

Alertmanager конфигурируется через флаги и конфиг
Через флаги конфигурируются неизменяемые параметры, а через конфиг настраиваются правила подавления, роутинг и ресиверы
Routing tree editor поможет построить правильный routing tree

Чтобы зарелоадить конфиг алертменеджера во время работы, нужно послать SIGHUP или послать POST на /-/reload

Configuration file

./alertmanager --config.file=alertmanager.yml - через ключ можно указать конкретный конфиг

Конфиг пишется в YAML


Секция global в конфиге определяет параметры которые валидны во всех других конфигурационных контекстах

Различные настройки SMTP (для email алертов)
Доступы к популярным каналам (slack, pagerduty, opsgenie, wechat or webhook)

https://prometheus.io/docs/alerting/latest/configuration/

Также тут настраиваются http (авторизация, tls, oauth), notification templates, routing tree, notification receivers, inhibition rules, mute time intervals


A route block defines a node in a routing tree and its children. Its optional configuration parameters are inherited from its parent node if not set

Every alert enters the routing tree at the configured top-level route, which must match all alerts (i.e. not have any configured matchers). It then traverses the child nodes. If continue is set to false, it stops after the first matching child. If continue is true on a matching node, the alert will continue matching against subsequent siblings. If an alert does not match any children of a node (no matching child nodes, or none exist), the alert is handled based on the configuration parameters of the current node

В этом блоке указывается куда слать алерты, разные таймауты и интервалы и прочее
А также могут быть вложенные роуты


mute_time_interval позволяет мьютить алерты в определенное время (например по выходным)


receiver - это именованная конфигурация notification интеграции
То есть там настраивается подключение к слаку, pagerduty, webhook и другим (несколько сразу), и вся эта пачка как-то именуется, а дальше по этому имени алерт передается сразу во все перечисленные каналы


matcher - это строка состоящая из трех элементов: labelname, оператор сравнения как в promql, и какая-то строка

Подробнее: https://prometheus.io/docs/alerting/latest/configuration/#matcher

Alerting

Notification Template

Prometheus генерирует алерт и шлет его в Alertmanager, a alertmanager шлет его в какой-то ресивер (на основе лейблов)
Ресивер это одна и более интеграция (слак, вебхук и тд)

Нотификации отсылаемые в ресиверы формируются через шаблоны

https://prometheus.io/docs/alerting/latest/notifications/

Пример того как нотификация из prometheus в alertmanager выглядит в wireshark:
Screenshot_2021_02_02-12_49_03-2021-07-02-at-hhhhhhhh.png

JSON из двух запросов со скрина:

[
  {
    "annotations": {
      "description": "CPU load is > 80%\n  VALUE = 100\n  LABELS = map[instance:localhost:9100]",
      "summary": "Host high CPU load (instance localhost:9100)"
    },
    "endsAt": "2021-07-02T15:23:30.321Z",
    "startsAt": "2021-07-02T15:22:45.321Z",
    "generatorURL": "http://interview:9090/graph?g0.expr=node_load1+%3E+1&g0.tab=1",
    "labels": {
      "alertname": "HostHighCpuLoad",
      "instance": "localhost:9100",
      "severity": "warning"
    }
  },
  {
    "annotations": {
      "description": "CPU load is > 80%\n  VALUE = 1.18\n  LABELS = map[__name__:node_load1 instance:localhost:9100 job:prometheus]",
      "summary": "Host high CPU load (instance localhost:9100)"
    },
    "endsAt": "2021-07-02T15:36:15.321Z",
    "startsAt": "2021-07-02T15:32:15.321Z",
    "generatorURL": "http://interview:9090/graph?g0.expr=node_load1+%3E+1&g0.tab=1",
    "labels": {
      "alertname": "HostHighCpuLoad",
      "instance": "localhost:9100",
      "job": "prometheus",
      "severity": "warning"
    }
  }
]

[
  {
    "annotations": {
      "description": "CPU load is > 80%\n  VALUE = 1.16\n  LABELS = map[__name__:node_load1 instance:localhost:9100 job:prometheus]",
      "summary": "Host high CPU load (instance localhost:9100)"
    },
    "endsAt": "2021-07-02T15:33:00.321Z",
    "startsAt": "2021-07-02T15:32:15.321Z",
    "generatorURL": "http://interview:9090/graph?g0.expr=node_load1+%3E+1&g0.tab=1",
    "labels": {
      "alertname": "HostHighCpuLoad",
      "instance": "localhost:9100",
      "job": "prometheus",
      "severity": "warning"
    }
  }
]
Alerting

Management API

Alertmanager предоставляет АПИ для управления (как prometheus)

200 когда все ок

GET /-/healthy

200 когда готов

GET /-/ready

Релоадит когфиг (доп ключей для включения этой функции не нужно)

POST /-/reload