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