VictoriaMetrics: Exporter в CronJob та пуш метрік без Pushgatway

Автор |  07/11/2023

В пості Prometheus: запуск Pushgateway у Kubernetes з Helm та Terraform писав про те, як для Prometheus додати Pushgateway, який дозволяє використовувати Push-модель замість Pull, тобто – експортер може відправити метрики прямо в базу замість того, щоб чекати, коли до нього прийде сам Prometheus або VMAgent.

У VictoriaMetrics з цим набагато простіше, бо ніякого Pushgateway не потрібно – VictoriaMetrics “з коробки” вміє приймати метрики, і це одна з тих чудовіих фіч, чому я вибрав VictoriaMetrics, і досі їй радуюсь.

Отже, що є і треба зробити:

  • в Kubernetes-кластері є VictoriaMetrics встановлена з VictoriaMetrics Operator – див. VictoriaMetrics: створення Kubernetes monitoring stack з власним Helm-чартом
  • у нас є новий експортер для Notion, який буде запускатись в Kubernetes по крону раз на добу
  • працювати він буде недовго, якісь секунди, тож “ходити” до нього з VMAgent ми не можемо
  • тому замість Pull-моделі з VMAgent – використаємо Push-модель – експортер буде відправляти метрики прямо в VictoriaMetrics

Тож спочатку давайте глянемо, як в VictoriaMetrics пушити метрики взагалі, а потім спробуємо написати простий експортер.

VictoriaMetrics API

Використовуємо ендпоінт /api/v1/import/prometheus.

Запускаємо тестовий под:

$ kubectl -n dev-monitoring-ns run pod --rm -i --tty --image ubuntu -- bash

Встановлюємо curl:

root@pod:/# apt update && apt -y install curl

Знаходимо Kubernetes Service для VMSingle:

$ kubectl -n dev-monitoring-ns get svc | grep vmsing
vmsingle-vm-k8s-stack                                  ClusterIP   172.20.45.38     <none>        8429/TCP                     35d

Виконуємо запит на ендпоінт http://vmsingle-vm-k8s-stack:8429/api/v1/import/prometheus:

root@pod:/# echo "some_metric 3.14" | curl --data-binary @- http://vmsingle-vm-k8s-stack:8429/api/v1/import/prometheus

І за півхвилини перевіряємо в VMui:

Все є, і ніяких тобі додаткових дій з Pushgateway!

Push метрик з експортера

Для тесту напишемо простий експортер:

#!/usr/bin/env python3

from time import sleep
from prometheus_client import Counter, CollectorRegistry, push_to_gateway

# local metrics "storage"
registry = CollectorRegistry()

# register a metric
counter = Counter('counter_metric', 'Example metric', ["label_name"], registry=registry)

while True:
  # increase metric's value to +1
  counter.labels(label_name="label_value").inc()
  # push to the VictoriaMetrics
  push_to_gateway('localhost:8428/api/v1/import/prometheus', job='test_job', registry=registry)
  # make sure this code is working :-)
  print("pushed")
  # wait before next run
  sleep(15)

Тут:

  • реєструємо “локальне сховище” для метрик (див. Overall structure)
  • реєструємо метрику counter_metric з типом Counter
  • виконуємо її інкремент з inc()
  • і відправляємо в localhost:8428/api/v1/import/prometheus – це я перевіряв на тестовому інтансі, де VictoriaMetrics запущена з Docker Compose, тому тут localhost

Перевіряємо в самій VictoriaMetrics:

prometheus_client, функції та grouping key

Трохи про те, як працює сам prometheus_client:

  • push_to_gateway() в registry будуть перезаписані всі метрики з однаковим набором labels (grouping key)
  • pushadd_to_gateway() перезапише метрики з однаковим іменем та grouping key
  • delete_from_gateway() видалить метрики з заданим job та grouping key

Див. Exporting to a Pushgateway та Prometheus Pushgateway.

Kubernetes CronJob та Prometheus Exporter

І приклад того, як воно все буде виглядати в Kubernetes.

Сам експортер, який буде ходити в Notion та збирати дані (код функцій не покажу, бо робочі моменти):

#!/usr/bin/env python3

import os
import requests

from datetime import datetime
from prometheus_client import Summary, CollectorRegistry, push_to_gateway

...

###################################################
### get_ios_ui_tests_status_and_failure_reasons ###
###################################################

registry = CollectorRegistry()

notion_ios_ui_test_last_100_results = Summary('notion_ios_ui_test_last_100_results',
                             'Status and failure reasons of last 100 iOS UI test runs',
                             labelnames=['reason'], registry=registry)

def get_last_ios_ui_test_results():
    print(f"\nStarted get_last_ios_ui_test_results() checks at {datetime.now()}")
    ...
    for reason in reasons.keys():
        notion_ios_ui_test_last_100_results.labels(reason=reason).observe(reasons[reason])
        print(f"collected reason: {reason} = {reasons[reason]} records")


def main():

    try:
        get_last_ios_ui_test_results()
        push_to_gateway('vmsingle-vm-k8s-stack:8429/api/v1/import/prometheus', job='notion-exporter', registry=registry)
    except Exception as e:
        print("Notion API error occurred:", str(e))


if __name__ == '__main__':
    print(f"\nStarting Notion Exporter at {datetime.now()}")
    main()

Kubernetes CronJob та Secret для нього – поки в тестуванні, тож запускається кожну хвилину:

apiVersion: batch/v1
kind: CronJob
metadata:
  name: notion-exporter-cronjob
spec:
  schedule: "*/1 * * * *"
  jobTemplate:
    spec:
      template:
        metadata:
          labels:
            app: notion-exporter
        spec:
          containers:
          - name: notion-exporter-container
            image: 492***148.dkr.ecr.us-east-1.amazonaws.com/notion-exporter:latest
            imagePullPolicy: Always
            env:
            - name: NOTION_REPORTS_DATABASE_ID
              value: "fc2***8e0"
            envFrom:
              - secretRef:
                  name: notion-exporter-secret
          restartPolicy: OnFailure
---
apiVersion: v1
kind: Secret
metadata:
  name: notion-exporter-secret
type: Opaque
stringData:
  NOTION_SECRET: "secret_SmX***ejH"

І дані в Grafana:

Готово.