kube-prometheus-stack на нескольких кластерах: Prometheus на spoke, центральный Grafana

Published: 2026-02-27

Каждый spoke-кластер запускает собственный Prometheus. Grafana работает в одном экземпляре — на infra — и собирает данные со всех кластеров. Никакого Thanos, никакого remote write, никаких лишних компонентов.


Зачем одна Grafana на infra

Grafana с несколькими Prometheus-источниками проще, чем remote write или Thanos, пока кластеров меньше десяти. Каждый Prometheus хранит данные за 7 дней локально, Grafana запрашивает их напрямую. Долгосрочное хранение логов — в VictoriaLogs; метрики старше 7 дней не нужны.

Подход Сложность Единый вид Долгосрочное хранение
Мульти-datasource Grafana Низкая Да (ручное переключение) Нет
Remote-write в центральный Средняя Да Да
Thanos Высокая Да Да

Для десяти и менее кластеров со сроком хранения 7 дней Grafana с несколькими источниками — прагматичный выбор.


HelmRelease для spoke: Grafana отключена

base/monitoring/kube-prom-stack/helmrelease.yaml применяется ко всем spoke:

yamlvalues:
  grafana:
    enabled: false

  defaultRules:
    create: false         # кастомные правила в отдельной Kustomization

  kubeScheduler:
    enabled: false        # k3s не публикует метрики scheduler
  kubeProxy:
    enabled: false
  kubeControllerManager:
    enabled: false

  alertmanager:
    enabled: false

defaultRules: create: false — кастомные PrometheusRules управляются в отдельной Kustomization, чтобы избежать конфликтов с правилами из чарта.


Образ Prometheus

Вместо стандартного образа используется prompp/prompp — drop-in замена с улучшениями PromQL:

yamlprometheus:
  prometheusSpec:
    image:
      registry: registry.deckhouse.io
      repository: prompp/prompp
      tag: "0.7.6"
    version: v3.2.1
    retentionSize: "18GB"
    securityContext:
      fsGroup: 64535
      runAsGroup: 64535
      runAsNonRoot: true

retentionSize предотвращает переполнение PVC при retention: 7d. Без него пик трафика может создать больше данных, чем ожидалось.


Патчи по окружениям

yaml# projects/dev/kustomization/hub/patches/kube-prom-stack.yaml
apiVersion: helm.toolkit.fluxcd.io/v2
kind: HelmRelease
metadata:
  name: kube-prom-stack
  namespace: dev
spec:
  values:
    prometheus:
      prometheusSpec:
        retention: 7d
        storageSpec:
          volumeClaimTemplate:
            spec:
              storageClassName: local-path
              resources:
                requests:
                  storage: 20Gi
        externalLabels:
          cluster: dev

externalLabels.cluster критически важен для мультикластерного Grafana — позволяет фильтровать дашборды по кластеру.

Для Yandex Cloud:

yamlstorageClassName: yc-network-ssd

Alertmanager только на infra

Alertmanager отключён в базовом HelmRelease, и только infra включает его через патч. Prometheus на spoke-кластерах отправляют алерты в Alertmanager на infra:

yamlprometheus:
  prometheusSpec:
    alerting:
      alertmanagers:
        - namespace: observability
          name: alertmanager-operated
          port: web

Соглашение по ServiceMonitor

Все экспортёры используют labels: { release: kube-prom-stack } на своих ServiceMonitor-ах:

yamlprometheus:
  prometheusSpec:
    serviceMonitorSelector:
      matchLabels:
        release: kube-prom-stack

Grafana на infra

yamlgrafana:
  enabled: true
  ingress:
    enabled: true
    hosts:
      - grafana.infra.test.antonnovikov.com
  additionalDataSources:
    - name: prometheus-dev
      type: prometheus
      url: http://prometheus-operated.observability.dev.svc.cluster.local:9090
    - name: prometheus-staging
      type: prometheus
      url: http://prometheus-operated.observability.staging.svc.cluster.local:9090

Каждый источник данных — один spoke-кластер. В дашбордах между кластерами переключаемся переменной datasource.


Проверка состояния

bash# Все Prometheus по всем spoke
kubectl get prometheus -A --context=infra-k8s

# Таргеты на конкретном spoke
kubectl port-forward -n observability svc/prometheus-operated 9090:9090 --context=dev-k8s
# открыть localhost:9090/targets

# Проверить externalLabels
curl -s http://localhost:9090/api/v1/labels | jq '.data[] | select(. == "cluster")'

# TSDB head stats (нагрузка на хранилище)
curl -s http://localhost:9090/api/v1/status/tsdb | jq .data.headStats