VictoriaMetrics: знайомство та використання замість Prometheus

Автор |  08/06/2023
 

Давно і багато чув про VictoriaMetrics, і нарешті настав час, коли її можна спробувати.

Отже, в двох словах – VictoriaMetrics це “Prometheus на стероідах”, і повністю з ним сумісна – може використовувати його файли конфігурації, експортери, PromQL тощо.

Тож як для людини, яка завжди користувалась Prometheus, перше питання – в чьому різниця? Єдине, що пам’ятаю, це те, що VictoriaMetrics начебто вміє в anomaly detection, чого не вистачало в Prometheus – давно хотілось додати.

В Google порівнянь не так багато, але знайшлись такі:

З цікавого:

  • підримує Pull та Push  моделі (на відміну від Prometheus, якому для push потрібен Pushgateway)
  • можна налаштувати Prometheus з remote write у VictoriaMetrics, тобто з Prometheus писати дані у VictoriaMetrics
  • VictoriaMetrics має концепцію “неймспейсів” – можна мати ізольовані середовища для метрик, див. Multitenancy
  • має власний MetricsQL з ширшими ніж у PromQL можливостями
  • для знайомства є VictoriaMetrics playground
  • для AWS є Managed VictoriaMetrics

Отже, сьогодні глянемо на архітектуру і компоненти, запустимо VictoriaMetrics з Docker Compose, налаштуємо збір метрик с Prometheus exporters, глянемо як там з алертами, і підключимо Grafana.

Prometheus з Alertmanager, пачка експортерів та Grafana вже є, наразі запущені просто через Docker Compose на AWS EC2, туди ж додамо інстанс VictoriaMetrics. Тобто основна ідея – замінити Prometheus на VictoriaMetrics.

З того, що побачив поки запускав VictoriaMetrics – виглядає прям дуже цікаво. Більше можливостей по функціям, по шаблонам алертів, сам UI дає більше можливостей для роботи з метриками. Спробуємо його використати замість Prometehus в нашому проекті, подивимось, як воно буде. Правда, якихось прикладів в тому ж Гуглі небагато, проте ChatGPT може допомогти.

Архітектура VictoriaMetrics

VictoriaMetrics має cluster version та single-node version. Для невеликих проектів до мільйона метрик в секунду рекомендується використовувати single node, але у кластер-версії гарно описана загальна архітектура.

Основні сервіси та компоненти VictoriaMetrics:

  • vmstorage: відповідає за зберігання даних та відповіді на запит даних клієнтами (vmselect)
  • vmselect: відповідає за обробку вхідних запитів на вибірку даних та збор даних з нод vmstorage
  • vminsert: відповідає за прийом метрик та розподіл даних по нодам vmstorage у відповідності до імен та лейбл цих метрик
  • vmui: Web UI для доступу к даним і параметрам конфігурацї
  • vmalert: обробляє алерти з файлу конфігурації та відправляє їх до Alertmanager
  • vmagent: займається збором метрик з різних джерел, таких як експортери Prometheus, їхнє фільтрування та релейбл, і зберігання у сховищі даних (самій VictoriaMetrics або через remote_write протокол Prometheus)
  • vmanomaly:  VictoriaMetrics Anomaly Detection – сервіс, який постійно сканує дані у VictoriaMetrics і за допомогою механізмів machine learning виявляє несподівані зміни, які можна використовувати у алертах
  • vmauth: простий auth proxy, роутер та лоад-балансер для VictoriaMetrics.

Запуск VictoriaMetrics з Docker Compose

Отже, як ми можемо використати VictoriaMetrics у випадку, якщо вже є Prometheus та його експортери?

  1. можемо налаштувати Prometheus слати метрики у VictoriaMetrics, див. Prometheus setup (майте на увазі, що remote_write на Prometheus-інстансі збільшить споживання ресурсів ЦПУ та диску на 25%) – не бачу сенсу в нашому випадку, але можливо буде корисним у разі використання якогось KubePrometehusStack
  2. можемо налаштувати VictoriaMetrics на збір даних з експортерів Prometheus, див. How to scrape Prometheus exporters such as node-exporter, тобто як раз зробити те, що хочеться зараз – замінити Prometheus на VictoriaMetrics з мінімальними змінами у конфігурації Prometheus

Приклад Docker Compose файлу – docker-compose.yml.

Що ми в ньому маємо:

  • vmagent: збирає метрики з експортерів,див опції у Advanced usage
  • victoriametrics: зберігає дані, див опції у List of command-line flags
  • vmalert: працює з алертами, див опції у Flags
  • alertmanager: старий знайомий) приймає алерти від vmalert

Давайте почнемо з контейнерів vmagent та victoriametrics, алерти підключимо пізніше.

Тут приклад з усіма сервісами окрім експортерів Prometheus:

version: '3.8'

volumes:
  prometheus_data: {}
  grafana_data: {}
  vmagentdata: {}
  vmdata: {}

services:
  prometheus:
    image: prom/prometheus
    restart: always
    volumes:
      - ./prometheus:/etc/prometheus/
      - prometheus_data:/prometheus
    command:
      - '--config.file=/etc/prometheus/prometheus.yml'
      - '--storage.tsdb.path=/prometheus'
      - '--web.console.libraries=/usr/share/prometheus/console_libraries'
      - '--web.console.templates=/usr/share/prometheus/consoles'
      - '--web.external-url=http://100.***.****.197:9090/'
    ports:
      - 9090:9090
      - alertmanager:alertmanager

  alertmanager:
    image: prom/alertmanager
    restart: always
    ports:
      - 9093:9093
    volumes:
      - ./alertmanager/:/etc/alertmanager/
    command:
      - '--config.file=/etc/alertmanager/config.yml'
      - '--storage.path=/alertmanager'

  grafana:
    image: grafana/grafana
    user: '472'
    restart: always
    environment:
      GF_INSTALL_PLUGINS: 'grafana-clock-panel,grafana-simple-json-datasource'
    volumes:
      - grafana_data:/var/lib/grafana
      - ./grafana/provisioning/:/etc/grafana/provisioning/
    env_file:
      - ./grafana/config.monitoring
    ports:
      - 3000:3000
    depends_on:
      - prometheus

  loki:
    image: grafana/loki:latest
    ports:
      - "3100:3100"
    command: -config.file=/etc/loki/local-config.yaml

  victoriametrics:
    container_name: victoriametrics
    image: victoriametrics/victoria-metrics:v1.91.2
    ports:
      - 8428:8428
    volumes:
      - vmdata:/storage
    command:
      - "--storageDataPath=/storage"
      - "--httpListenAddr=:8428"
#      - "--vmalert.proxyURL=http://vmalert:8880"

  vmagent:
    container_name: vmagent
    image: victoriametrics/vmagent:v1.91.2
    depends_on:
      - "victoriametrics"
    ports:
      - 8429:8429
    volumes:
      - vmagentdata:/vmagentdata
      - ./prometheus:/etc/prometheus/
    command:
      - "--promscrape.config=/etc/prometheus/prometheus.yml"
      - "--remoteWrite.url=http://victoriametrics:8428/api/v1/write"
...

Для victoriametrics поки що закоментуємо --vmalert.proxyURL, додамо його згодом.

До vmagent підключаємо каталог ./prometheus – в ньому маємо файл prometheus.yaml з конфігурацією srape_jobs, та файли параметрів експортерів (наприклад – ./prometheus/blackbox.yml та /prometheus/blackbox-targets/targets.yaml для Blackbox Exporter).

У --remoteWrite.url вказуємо, куди будемо писати отримані метрики – до інстансу VictoriaMetrics.

Запускаємо:

[simterm]

# docker compose up

[/simterm]

Якщо перейти без URI, тобто просто на domain.com/ – то видасть всі доступні шляхи, дуже прям зручно:

field evaluation_interval not found in type promscrape.GlobalConfig

Але vmagent не запустився:

[simterm]

2023-06-05T09:38:31.376Z        fatal   VictoriaMetrics/lib/promscrape/scraper.go:117   cannot read "/etc/prometheus/prometheus.yml": cannot parse Prometheus config from "/etc/prometheus/prometheus.yml": cannot unmarshal data: yaml: unmarshal errors:
  line 4: field evaluation_interval not found in type promscrape.GlobalConfig
  line 13: field rule_files not found in type promscrape.Config
  line 19: field alerting not found in type promscrape.Config; pass -promscrape.config.strictParse=false command-line flag for ignoring unknown fields in yaml config

[/simterm]

Окей, відключимо strictParse – додаємо --promscrape.config.strictParse=false:

...
    command:
      - "--promscrape.config=/etc/prometheus/prometheus.yml"
      - "--remoteWrite.url=http://victoriametrics:8428/api/v1/write"
      - "--promscrape.config.strictParse=false"

Перезапускаємо сервіси, та заглянемо на порт 8429, vmagent – теж є лінки :

Перевіряємо таргети – вони є, тобто vmagent зчитав файл prometheus.yaml, але не всі працюють, наприклад – Sentry експортер є, YACE є, а от blackbox, node_exporter та cAdvisor не бачить:

А чому?

Ага… Не бачить тих, у кого sd_configs, тобто динамічний сервіс-діскавері:

...  - job_name: 'cadvisor'

    # Override the global default and scrape targets from this job every 5 seconds.
    scrape_interval: 5s

    dns_sd_configs:
    - names:
      - 'tasks.cadvisor'
      type: 'A'
      port: 8080
...

Хоча наче має вміти – Supported service discovery configs.

Глянемо логи vmagent.

error in A lookup for “tasks.cadvisor”: lookup tasks.cadvisor on 127.0.0.11:53: no such host

А логи кажуть, що контейнер з vmagent не може отримати A-запис з DNS:

[simterm]

...
vmagent                                 | 2023-06-05T10:04:10.818Z      error   VictoriaMetrics/lib/promscrape/discovery/dns/dns.go:163 error in A lookup for "tasks.cadvisor": lookup tasks.cadvisor on 127.0.0.11:53: no such host
vmagent                                 | 2023-06-05T10:04:10.821Z      error   VictoriaMetrics/lib/promscrape/discovery/dns/dns.go:163 error in A lookup for "tasks.node-exporter": lookup tasks.node-exporter on 127.0.0.11:53: no such host
...

[/simterm]

Читаємо документацію по dns_sd_configs, де говориться про “# names must contain a list of DNS names to query“, але в мене зараз job описана з names = tasks.container_name, див. Container discovery.

Спробуємо вказати просто ім’я, тобто cadvisor замість tasks.cadvisor:

...
  - job_name: 'cadvisor'
    dns_sd_configs: 
    - names:
#      - 'tasks.cadvisor'
      - 'cadvisor'
      type: 'A'
      port: 8080
...

А job_name: 'prometheus' просто вимикаємо – вона нам не потрібна.

І тепер всі таргети з’явились:

VictoriaMetrics та Grafana

Тепер давайте спробуємо використати VictoriaMetrics як data source у Grafana.

В принципі, тут все робиться однаково з Prometheus, використовуючи той же тип data source.

Додаємо новий data source з типом Prometheus, в URL вказуємо http://victoriametrics:8428:

Обновлюємо графік – вибираємо щойно доданий data source:

 

І тепер можемо використовувати функцію sort_by_label_numeric, якої не вистачало у пості Prometheus: GitHub Exporter – пишемо власний експортер для GitHub API.

З Prometheus ця панель виглядає так:

А з VictoriaMetrics та sort_by_label_numeric – так:

Добре, наче все працює.

Можемо пробувати роботу з алертами.

VictoriaMetrics та Alertmanager

Отже, зара маємо запущений Alertmanager та Prometheus.

У prometheus.yaml маємо вказаний файл з алертами:

...
rule_files:
  - 'alert.rules'
...

Що нам треба – це запустити vmalert, якому вкажемо “бекенд” у вигляді Alertmanager, якому він буде слати алерти, та сам файл з алертами у форматі Prometheus.

Як і сама VictoriaMetrics, vmalert має дещо ширші можливості, ніж Prometheus, наприклад – зберігає статус алертів, тож рестарт контейнеру не сбиває silenced алерти. Ще є зручна змінна $for для шаблонів, в якій передається значення for з алерту, і можемо мати щось таке:

...
    for: 5m
    annotations:
      description: |-
        {{ if $value }} *Current latency*: `{{ $value | humanize }}` milliseconds {{ end }} during `{{ $for }}` minutes
...

Також є підримка httpAuth, є можливість виконати запит алерту з query та багато іншого, див. Template functions.

Додаємо vmalert в docker-compose.yaml:

...
  vmalert: 
    container_name: vmalert
    image: victoriametrics/vmalert:v1.91.2
    depends_on:
      - "victoriametrics"
      - "alertmanager"
    ports:
      - 8880:8880
    volumes:
      - ./prometheus/alert.rules:/etc/alerts/alerts.yml
    command:
      - "--datasource.url=http://victoriametrics:8428/"
      - "--remoteRead.url=http://victoriametrics:8428/"
      - "--remoteWrite.url=http://victoriametrics:8428/"
      - "--notifier.url=http://alertmanager:9093/"
      - "--rule=/etc/alerts/*.yml"

Тут у datasource.url вказуємо, звідки брати метрики для перевірки у алертах, remoteRead.url та remoteWrite.url – де зберігати стан алертів.

У notifier.url – куди будемо слати алерти (а вже Alertmanager через свій конфіг відправить їх у Slack/Opsgenie/etc). І у rule вказуємо сам файл з алертами, який підключаємо у volumes.

Перезапускаємо контейнери з docker compose restart, и заходимо на порт 8880:

Окей, є алерт-рули.

Спробуємо тригернути тестовий алерт – і маємо новий алерт у vmalert Alerts:

Та повідомлення в Slack від Alertmanager:

Все працює.

Тепер можна відключати контейнер з Prometheus, тільки оновити depends_on у Grafana – замість prometheus вказати victoriametrics, і замінити data sources у дашбордах.

Bonus: Alertmanager Slack template

І приклад шаблону для нотіфікацій в Slack. Він ще буде перероблюватись, поки що вся система більше в стані proof of concept, але в цілому буде якось так.

Файл alertmanager/notifications.tmpl з шаблоном:

{{/* Title of the Slack alert */}}
{{ define "slack.title" -}}
  {{ if eq .Status "firing" }} :scream: {{- else -}} :relaxed: {{- end -}}
  [{{ .Status | toUpper -}} {{- if eq .Status "firing" -}}:{{ .Alerts.Firing | len }} {{- end }}] {{ (index .Alerts 0).Annotations.summary }}
{{ end }}    

{{ define "slack.text" -}}

    {{ range .Alerts }}
        {{- if .Annotations.description -}}
        *Description*: {{ .Annotations.description }}
        {{- end }}
    {{- end }}

{{- end }}

Його використання в alertmanager/config.yml:

receivers:

- name: 'slack-default'
 
  slack_configs:
    - title: '{{ template "slack.title" . }}'
      text: '{{ template "slack.text" . }}'
      send_resolved: true
      actions:
        - type: button
          text: 'Grafana :grafana:'
          url: '{{ (index .Alerts 0).Annotations.grafana_url }}'
        - type: button
          text: 'Prometheus query :mag:'
          url: '{{ (index .Alerts 0).GeneratorURL }}'
        - type: button
          text: 'AWS dashboard :aws:'
          url: '{{ (index .Alerts 0).Annotations.aws_dashboard_url }}'

Темплейт для алерту – підключається в контейнер vmalerts, див. Reusable templates:

{{ define "grafana.filter" -}}
  {{- $labels := .arg0 -}}
  {{- range $name, $label := . -}}
    {{- if (ne $name "arg0") -}}
      {{- ( or (index $labels $label) "All" ) | printf "&var-%s=%s" $label -}}
    {{- end -}}
  {{- end -}}
{{- end -}}

І сам алерт:

- record: aws:apigateway_integration_latency_average_sum
  expr: sum(aws_apigateway_integration_latency_average) by (dimension_ApiName, tag_environment)

- alert: APIGatewayLatencyBackendProdTEST2
  expr: aws:apigateway_integration_latency_average_sum{tag_environment="prod"} > 100
  for: 1s
  labels:
    severity: info
    component: backend
    environment: test
  annotations:
    summary: "API Gateway latency too high"
    description: |-
      The time between when API Gateway relays a request to the backend and when it receives a response from the backend
      *Environment*: `{{ $labels.tag_environment }}`
      *API Gateway name*: `{{ $labels.dimension_ApiName }}`
      {{ if $value }} *Current latency*: `{{ $value | humanize }}` milliseconds {{ end }}
    grafana_url: '{{ $externalURL }}/d/overview/overview?orgId=1{{ template "grafana.filter" (args .Labels "environment" "component") }}'

А $externalURL отримується vmalerts з параметру --external.url=http://100.***.***.197:3000".

Корисні посилання