Files
homelab/kubernetes/apps/monitoring/helmrelease-prometheus.yaml
claude-botandClaude Opus 5.5 a35bc6b91d feat(monitoring): watch off-site Uptime Kuma from Prometheus
Add a blackbox-http probe of kuma.renner.uno with a dedicated critical
UptimeKumaUnreachable rule (excluded from BlackboxProbeFailing), scrape
Kuma /metrics with the API key from OpenBao, and alert on sustained slow
monitor response times. No egress policy exists in the monitoring
namespace, so no CNP change is needed.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-19 08:17:30 +02:00

728 lines
32 KiB
YAML

apiVersion: source.toolkit.fluxcd.io/v1
kind: HelmRepository
metadata:
name: prometheus-community
namespace: monitoring
spec:
interval: 24h
url: https://prometheus-community.github.io/helm-charts
---
apiVersion: helm.toolkit.fluxcd.io/v2
kind: HelmRelease
metadata:
name: kube-prometheus-stack
namespace: monitoring
spec:
interval: 30m
chart:
spec:
chart: kube-prometheus-stack
version: "92.3.0"
sourceRef:
kind: HelmRepository
name: prometheus-community
namespace: monitoring
install:
crds: CreateReplace
remediation:
retries: 3
upgrade:
crds: CreateReplace
remediation:
retries: 3
values:
# Prometheus
prometheus:
# prometheus.renner.uno is served by a standalone, Authentik-gated
# Ingress (prometheus-gate-ingress.yaml, class authentik-gate), NOT this Helm-generated one.
# Prometheus had no auth of its own — its full 30d metrics DB is an
# infra-recon oracle (hostnames, IPs, service inventory). Gated behind
# the Authentik gate, same as Alertmanager (#146).
# The chart Ingress is disabled here — two Ingresses for one host would
# conflict. tls (prometheus-tls) and externalUrl below are unchanged;
# the standalone Ingress reuses the same secret.
#
# Grafana is unaffected: its datasource targets the ClusterIP Service
# (kube-prometheus-stack-prometheus.monitoring:9090), not this ingress.
# Blackbox uptime probes that target https://prometheus.renner.uno
# follow the gate's 302 to the Authentik login (200), so they stay
# green — but now measure Authentik reachability, not Prometheus. Same
# already-true for alerts.renner.uno since #146. Repointing those probes
# to an internal endpoint is a separate follow-up.
ingress:
enabled: false
prometheusSpec:
# Scrape ServiceMonitors/PodMonitors regardless of their labels.
#
# By default kube-prometheus-stack sets these selectors to
# `matchLabels: {release: kube-prometheus-stack}`. ServiceMonitors
# created at RUNTIME by third-party operators — Rook, goauthentik.io —
# carry that operator's own labels instead and can never be made to
# match: no static manifest or Kustomize patch can relabel an object
# the operator creates itself. Left strict, those targets are dropped
# silently, with no failed resource and no gap visible anywhere except
# in the missing series.
#
# Because these two are empty, the `release` label on this repo's own
# ServiceMonitors and PodMonitors is decorative. It is still set for
# consistency, and the comments there say so.
#
# ruleSelector, probeSelector and scrapeConfigSelector are deliberately
# NOT loosened: nothing outside this repo creates those, so keeping the
# selectors strict stops a stray third-party rule from firing here. For
# PrometheusRules and Probes the label IS required.
serviceMonitorSelectorNilUsesHelmValues: false
podMonitorSelectorNilUsesHelmValues: false
# Rewrites the in-cluster URL embedded in alert generatorURLs and
# status pages so click-through from emails works.
externalUrl: https://prometheus.renner.uno
# Time retention is the one in force; the size cap is a backstop
# (REN-107, owner decision: 30 days on the existing disk). This was
# 90d/30GB, and at ~1 GB/day of ingest the 30GiB cap bound first: the
# real window was a 20.5-27 day saw-tooth while this line said 90d.
# 30d peaks near 38 GB (30d plus up to one 3-day block awaiting
# deletion, plus WAL), under 40GiB (42.9 GB), under the 50Gi PVC.
# Raising either number means re-doing that arithmetic against the
# PVC; PrometheusRetentionNotInForce (monitoring-rules) fires if the
# size cap starts binding again.
retention: 30d
retentionSize: 40GiB
scrapeInterval: 30s
evaluationInterval: 30s
storageSpec:
volumeClaimTemplate:
spec:
storageClassName: ceph-block
accessModes: ["ReadWriteOnce"]
resources:
requests:
storage: 50Gi
# Mounted at /etc/prometheus/secrets/<name>/; see
# ./uptime-kuma-metrics-externalsecret.yaml.
secrets:
- uptime-kuma-metrics
# Additional scrape targets not covered by ServiceMonitors.
additionalScrapeConfigs:
# Gitea exposes /metrics anonymously on its main HTTP port. The
# `gitea_*` metrics power the gitea-overview dashboard. Scrapes the
# ClusterIP Service of the StatefulSet in the gitea namespace.
- job_name: gitea
static_configs:
- targets: ["gitea.gitea.svc.cluster.local:3000"]
metrics_path: /metrics
# Off-site Uptime Kuma. /metrics takes the API key as the basic-auth
# password with an empty username.
- job_name: uptime-kuma
scheme: https
metrics_path: /metrics
basic_auth:
username: ""
password_file: /etc/prometheus/secrets/uptime-kuma-metrics/api_key
static_configs:
- targets: ["kuma.renner.uno:443"]
# Grafana
grafana:
defaultDashboardsEnabled: true
defaultDashboardsTimezone: Europe/Berlin
# Single replica on an RWO (ceph-block) PVC — RollingUpdate deadlocks
# on Multi-Attach because both Pods would need the volume simultaneously.
deploymentStrategy:
type: Recreate
ingress:
enabled: true
ingressClassName: traefik
annotations:
cert-manager.io/cluster-issuer: letsencrypt-cluster
hosts:
- dash.renner.uno
tls:
- secretName: grafana-tls
hosts:
- dash.renner.uno
# Postgres backend (gf-pg CNPG cluster). SQLite-on-RBD made schema
# migrations on cold rebuilds blow past the liveness probe budget;
# Postgres batches WAL fsyncs server-side and migrations finish in
# seconds. The gf-pg-app secret is auto-created by CNPG.
env:
GF_DATABASE_TYPE: postgres
GF_DATABASE_HOST: gf-pg-rw.monitoring.svc:5432
GF_DATABASE_NAME: grafana
GF_DATABASE_SSL_MODE: require
envValueFrom:
GF_DATABASE_USER:
secretKeyRef:
name: gf-pg-app
key: username
GF_DATABASE_PASSWORD:
secretKeyRef:
name: gf-pg-app
key: password
# PVC no longer needed for the DB; keep it for the (small) Grafana
# plugin/data dir which Grafana still writes to.
persistence:
enabled: true
storageClassName: ceph-block
size: 5Gi
replicas: 1
additionalDataSources:
- name: Loki
uid: loki
type: loki
url: http://loki.monitoring.svc.cluster.local:3100
access: proxy
isDefault: false
# Legacy Prometheus (LXC 227) — bridges host-level dashboards
# (Alloy, docker, pve host node_exporter, etc.) that depend on
# metrics only the legacy Prom scrapes. Retires at global cut-over.
- name: Prometheus (legacy)
uid: prometheus-legacy
type: prometheus
url: http://prometheus.srv.renner.uno:9090
access: proxy
isDefault: false
jsonData:
httpMethod: POST
# In-cluster InfluxDB (chart `influxdb-influxdb2`). Both HA (write)
# and Grafana (read) hit this directly via Service DNS. The
# `grafana-influxdb-token` Secret is created by the Job
# `influxdb-grafana-token` (kubernetes/apps/influxdb/grafana-
# token-job.yaml), which mints a read-only auth on the
# suedstr28 bucket every fresh deploy.
- name: InfluxDB
uid: influxdb
type: influxdb
url: http://influxdb-influxdb2.influxdb.svc.cluster.local:8086
access: proxy
isDefault: false
jsonData:
version: Flux
organization: renner
defaultBucket: suedstr28
tlsSkipVerify: true
secureJsonData:
token: ${INFLUXDB_TOKEN}
sidecar:
resources:
requests:
cpu: 10m
memory: 128Mi
limits:
memory: 256Mi
dashboards:
enabled: true
label: grafana_dashboard
labelValue: "1"
searchNamespace: monitoring
folder: /tmp/dashboards
provider:
foldersFromFilesStructure: true
dashboardProviders:
dashboardproviders.yaml:
apiVersion: 1
providers:
- name: default
orgId: 1
folder: ""
type: file
disableDeletion: false
editable: true
options:
path: /var/lib/grafana/dashboards/default
dashboards:
default:
velero:
gnetId: 11055
revision: 2
datasource: Prometheus
plugins:
- volkovlabs-echarts-panel
envFromSecrets:
- name: grafana-oauth
- name: grafana-influxdb-token
grafana.ini:
server:
root_url: https://dash.renner.uno
auth.generic_oauth:
enabled: true
name: Authentik
allow_sign_up: true
scopes: openid profile email
auth_url: https://auth.renner.uno/application/o/authorize/
token_url: https://auth.renner.uno/application/o/token/
api_url: https://auth.renner.uno/application/o/userinfo/
use_pkce: true
role_attribute_path: "contains(groups[*], 'Infrastructure Admins') && 'Admin' || 'Viewer'"
resources:
requests:
cpu: 100m
memory: 768Mi
limits:
memory: 1Gi
# Alertmanager
alertmanager:
# alerts.renner.uno is served by a standalone, Authentik-gated Ingress
# (alertmanager-gate-ingress.yaml) instead of this Helm-generated one.
# Alertmanager has no auth and its v2 API is writable (silence-all),
# so it must sit behind the Authentik gate (class authentik-gate). The
# chart Ingress is disabled here — two Ingresses for the same host
# would conflict. The tls secret (alertmanager-tls) and externalUrl
# below are unchanged; the standalone Ingress reuses the same secret.
ingress:
enabled: false
alertmanagerSpec:
# Embedded in the AlertmanagerURL footer of every email plus
# the "Source" link of every grouped alert, so the email link
# resolves externally instead of pointing at the in-cluster
# service hostname.
externalUrl: https://alerts.renner.uno
storage:
volumeClaimTemplate:
spec:
storageClassName: ceph-block
accessModes: ["ReadWriteOnce"]
resources:
requests:
storage: 1Gi
# Mounts ./alertmanager-smtp Secret keys at
# /etc/alertmanager/secrets/alertmanager-smtp/<key>. The
# smtp_auth_password_file below references that path.
secrets:
- alertmanager-smtp
# SMTP via Proton submission (smtp.protonmail.ch:587 STARTTLS).
# The Proton SMTP token is a per-account credential generated in
# the Proton webapp (Settings -> IMAP/SMTP). It is stored in
# OpenBao at apps/monitoring/alertmanager-smtp and projected into
# the alertmanager-smtp Secret by
# ./alertmanager-smtp-externalsecret.yaml — rotating the token
# means updating OpenBao, not this repo. Outbound 587 from the
# monitoring ns is not blocked by any CNP today.
config:
global:
smtp_smarthost: 'smtp.protonmail.ch:587'
smtp_from: 'cluster@renner.uno'
smtp_auth_username: 'cluster@renner.uno'
smtp_auth_password_file: '/etc/alertmanager/secrets/alertmanager-smtp/smtp-password'
smtp_require_tls: true
resolve_timeout: 5m
route:
receiver: email
group_by: ['alertname', 'cluster', 'namespace']
group_wait: 30s
group_interval: 5m
repeat_interval: 12h
routes:
# The kube-prometheus-stack chart ships a `Watchdog` alert
# designed to fire continuously as a heartbeat for an
# external dead-man's-switch. We don't have a DMS endpoint,
# and a 12h-repeat email of an always-firing alert is
# noise — drop it.
- matchers:
- alertname = "Watchdog"
receiver: 'null'
# KubeCPUOvercommit fires whenever the cluster cannot
# tolerate the failure of one node — by design unsatisfiable
# on a small homelab cluster regardless of actual load.
- matchers:
- alertname = "KubeCPUOvercommit"
receiver: 'null'
# Helper alert that only drives the info inhibit rule below.
- matchers:
- alertname = "InfoInhibitor"
receiver: 'null'
# Flux notification-controller error events (flux-failures Alert).
- matchers:
- source = "flux"
receiver: email
group_wait: 1m
group_interval: 10m
repeat_interval: 24h
# From kube-prometheus manifests/alertmanager-secret.yaml.
inhibit_rules:
- source_matchers:
- severity = "critical"
target_matchers:
- severity =~ "warning|info"
equal: ['namespace', 'alertname']
- source_matchers:
- severity = "warning"
target_matchers:
- severity = "info"
equal: ['namespace', 'alertname']
- source_matchers:
- alertname = "InfoInhibitor"
target_matchers:
- severity = "info"
equal: ['namespace']
receivers:
- name: email
email_configs:
- to: 'admin@renner.uno'
send_resolved: true
- name: 'null'
# Node Exporter (DaemonSet on all K8s nodes)
nodeExporter:
enabled: true
# kube-state-metrics
kubeStateMetrics:
enabled: true
# Resources pass-through to the kube-state-metrics subchart.
# Top-level `kubeStateMetrics.resources` is not honored — must
# be set under the subchart name (with hyphens).
kube-state-metrics:
resources:
requests:
cpu: 50m
memory: 64Mi
limits:
memory: 256Mi
# Groups Velero maintenance Jobs per repository in prometheusrule-jobs.yaml.
metricLabelsAllowlist:
- jobs=[velero.io/repo-name]
# --- Flux per-object readiness (REN-102) ---------------------------------
#
# WHY THIS IS HERE AND NOT ON THE FLUX CONTROLLERS. `gotk_reconcile_condition`
# and `gotk_suspend_status` are still declared by fluxcd/pkg/runtime, but no
# controller populates them at the tags pinned in
# clusters/homelab/flux-system/gotk-components.yaml, and an unpopulated
# GaugeVec exports no series. Verified after the REN-97 scrape landed: the
# PodMonitor now yields 2,273 `gotk_*` series and
# `count({__name__=~"gotk_reconcile_condition|gotk_suspend_status"})` is still
# empty. Upstream moved per-object readiness to kube-state-metrics
# custom-resource-state, which is what this block turns on.
#
# DO NOT ADD `collectors: []` OR `--custom-resource-state-only=true`.
# Upstream's reference config (fluxcd/flux2-monitoring-example,
# monitoring/controllers/kube-prometheus-stack/kube-state-metrics-config.yaml)
# opens with exactly those two lines. Copying them replaces every built-in
# collector with the Flux ones: measured 2026-09-26, that would take
# `count({job="kube-state-metrics"})` from 27,792 series to ~183 and blind
# every `Kube*` alert in the chart. Both keys are deliberately absent below.
#
# ROLLS THE kube-state-metrics POD. Enabling customResourceState adds an arg
# and a volume to the Deployment, so the pod is replaced once on apply. That
# is the whole blast radius of this block — KSM only watches, it writes
# nothing, and no Flux object or workload is touched. During the roll (a few
# seconds) `kube_*` has a gap; `KubeStateMetricsDown` and `TargetDown` cover
# it if it does not come back.
#
# NOTE FOR LATER EDITS: the subchart puts no config checksum on the
# Deployment's pod template, so editing `config` below changes the ConfigMap
# WITHOUT rolling the pod, and KSM keeps serving the old config until it
# restarts for another reason. Any future change to this config needs a
# deliberate pod restart to take effect.
#
# RBAC: list + watch, nothing else. `get` is NOT needed — KSM drives
# reflectors, never point-reads. Also note the chart adds
# `customresourcedefinitions: [list, watch]` on its own via
# rbac.customResourceState.createClusterRoleRules (default true); KSM needs it
# to resolve the GVKs below. Both land in the existing
# kube-prometheus-stack-kube-state-metrics ClusterRole rather than a second
# binding to maintain. No write verb, no wildcard, no cluster-admin.
rbac:
extraRules:
- apiGroups:
- source.toolkit.fluxcd.io
- kustomize.toolkit.fluxcd.io
- helm.toolkit.fluxcd.io
- image.toolkit.fluxcd.io
# Trimmed to the kinds this cluster actually has. Omitted from
# upstream's list, each for a checked reason:
# buckets, receivers — zero objects exist in this repo.
# helmcharts — created by source-controller at
# runtime, one per HelmRelease; a not-Ready HelmChart is always
# mirrored by the HelmRelease that owns it, so alerting on both
# is one failure reported twice.
# alerts, providers — notification.toolkit.fluxcd.io/v1beta3
# Alert and Provider have NO status.conditions in the CRD at all
# (checked in gotk-components.yaml), so there is no readiness to
# report; upstream omits `ready` for them for the same reason. A
# series carrying only `suspended` is not alertable.
resources:
- gitrepositories
- helmrepositories
- ocirepositories
- kustomizations
- helmreleases
- imagerepositories
- imagepolicies
- imageupdateautomations
verbs: ["list", "watch"]
customResourceState:
enabled: true
config:
spec:
resources:
# Label set is upstream's, minus the churning ones. Dropped
# deliberately: `revision` (status.artifact.revision /
# lastAppliedRevision — a git SHA, so every commit that reaches a
# Kustomization mints a NEW SERIES), and `url` / `endpoint`.
# Prometheus is at 84.5% of its 30GB retentionSize cap (REN-107);
# a label that changes several times a day across 33 Kustomizations
# is exactly what must not be added. Kept: `ready` and `suspended`
# (what the alerts read), `name` / `exported_namespace` (identity),
# `source_name` (the join key, see prometheusrule-flux.yaml), plus
# `image` and `tag` on the two image kinds, which are stable enough
# to ride the existing series.
#
# `exported_namespace` is upstream's own label name for this, not a
# Prometheus collision rename: the chart's KSM ServiceMonitor runs
# honorLabels, so `namespace` on a KSM series is already the
# OBJECT's namespace and nothing gets renamed. The rules use the
# same string because the config declares it.
#
# KSM adds `customresource_group`, `customresource_version` and
# `customresource_kind` to every custom-resource-state series by
# itself. The rules key off `customresource_kind`.
- groupVersionKind:
group: kustomize.toolkit.fluxcd.io
version: v1
kind: Kustomization
metricNamePrefix: gotk
errorLogV: 10
metrics:
- name: "resource_info"
help: "The current state of a Flux Kustomization resource."
each:
type: Info
info:
labelsFromPath:
name: [metadata, name]
labelsFromPath:
exported_namespace: [metadata, namespace]
ready: [status, conditions, "[type=Ready]", status]
suspended: [spec, suspend]
source_name: [spec, sourceRef, name]
- groupVersionKind:
group: helm.toolkit.fluxcd.io
version: v2
kind: HelmRelease
metricNamePrefix: gotk
errorLogV: 10
metrics:
- name: "resource_info"
help: "The current state of a Flux HelmRelease resource."
each:
type: Info
info:
labelsFromPath:
name: [metadata, name]
labelsFromPath:
exported_namespace: [metadata, namespace]
ready: [status, conditions, "[type=Ready]", status]
suspended: [spec, suspend]
- groupVersionKind:
group: source.toolkit.fluxcd.io
version: v1
kind: GitRepository
metricNamePrefix: gotk
errorLogV: 10
metrics:
- name: "resource_info"
help: "The current state of a Flux GitRepository resource."
each:
type: Info
info:
labelsFromPath:
name: [metadata, name]
labelsFromPath:
exported_namespace: [metadata, namespace]
ready: [status, conditions, "[type=Ready]", status]
suspended: [spec, suspend]
- groupVersionKind:
group: source.toolkit.fluxcd.io
version: v1
kind: HelmRepository
metricNamePrefix: gotk
errorLogV: 10
metrics:
- name: "resource_info"
help: "The current state of a Flux HelmRepository resource."
each:
type: Info
info:
labelsFromPath:
name: [metadata, name]
labelsFromPath:
exported_namespace: [metadata, namespace]
ready: [status, conditions, "[type=Ready]", status]
suspended: [spec, suspend]
- groupVersionKind:
group: source.toolkit.fluxcd.io
version: v1
kind: OCIRepository
metricNamePrefix: gotk
errorLogV: 10
metrics:
- name: "resource_info"
help: "The current state of a Flux OCIRepository resource."
each:
type: Info
info:
labelsFromPath:
name: [metadata, name]
labelsFromPath:
exported_namespace: [metadata, namespace]
ready: [status, conditions, "[type=Ready]", status]
suspended: [spec, suspend]
- groupVersionKind:
group: image.toolkit.fluxcd.io
version: v1
kind: ImageRepository
metricNamePrefix: gotk
errorLogV: 10
metrics:
- name: "resource_info"
help: "The current state of a Flux ImageRepository resource."
each:
type: Info
info:
labelsFromPath:
name: [metadata, name]
labelsFromPath:
exported_namespace: [metadata, namespace]
ready: [status, conditions, "[type=Ready]", status]
suspended: [spec, suspend]
image: [spec, image]
# The repository-side half of the Ready-but-frozen signal. A
# policy can hold Ready=True forever while its filter excludes
# every new release, so readiness cannot see it; what can is
# "the repository kept finding tags and the policy's answer
# never moved". .status.lastScanResult.tagCount is a required
# field on this CRD, so the series exists for every object.
- name: "image_repository_tag_count"
help: "Number of tags found by the ImageRepository's last scan."
each:
type: Gauge
gauge:
path: [status, lastScanResult]
valueFrom: [tagCount]
# `name` belongs HERE and not under `gauge`. KSM resolves
# `gauge.labelsFromPath` RELATIVE TO `gauge.path`, so
# [metadata, name] under there would be looked up inside
# status.lastScanResult and silently yield no label. The
# metric-level labelsFromPath resolves from the object root.
labelsFromPath:
name: [metadata, name]
exported_namespace: [metadata, namespace]
- groupVersionKind:
group: image.toolkit.fluxcd.io
version: v1
kind: ImagePolicy
metricNamePrefix: gotk
errorLogV: 10
metrics:
- name: "resource_info"
help: "The current state of a Flux ImagePolicy resource."
each:
type: Info
info:
labelsFromPath:
name: [metadata, name]
labelsFromPath:
exported_namespace: [metadata, namespace]
ready: [status, conditions, "[type=Ready]", status]
suspended: [spec, suspend]
# The resolved tag, as a label on the Info series that
# already exists — so the policy-side half of the frozen
# signal costs zero extra series at rest. It retires one
# series and starts another only when the tag actually
# changes: 191 automated bumps in 147 days across 34
# policies is ~1.3 new series/day, which ages out with
# retention.
tag: [status, latestRef, tag]
# The join key. `metadata.name` happens to equal
# `spec.imageRepositoryRef.name` for all 34 pairs in this
# repo today, but the REFERENCE is this field, and a policy
# named differently from its repository would silently drop
# out of a name-based join.
source_name: [spec, imageRepositoryRef, name]
- groupVersionKind:
group: image.toolkit.fluxcd.io
version: v1
kind: ImageUpdateAutomation
metricNamePrefix: gotk
errorLogV: 10
metrics:
- name: "resource_info"
help: "The current state of a Flux ImageUpdateAutomation resource."
each:
type: Info
info:
labelsFromPath:
name: [metadata, name]
labelsFromPath:
exported_namespace: [metadata, namespace]
ready: [status, conditions, "[type=Ready]", status]
suspended: [spec, suspend]
source_name: [spec, sourceRef, name]
# Drop the apiserver's high-cardinality latency histograms.
#
# Measured 2026-08-20: Prometheus held 298,236 active series, of which
# ~108,000 — over a third — were apiserver/etcd `_bucket` histograms. Each
# histogram fans out to one series per bucket x verb x resource x scope, so
# a single metric like apiserver_request_duration_seconds_bucket was 23,810
# series on its own. For comparison, every Home Assistant metric combined is
# ~4,100.
#
# That cardinality is the dominant driver of Prometheus write volume
# (~11 GB/day of TSDB + WAL, doubled again by Ceph replication), and none of
# it is observable value here: per-bucket apiserver latency quantiles are for
# clusters with SLAs and dozens of nodes, not three.
#
# ONLY metrics with ZERO rule dependencies are dropped. Verified against the
# live rule set before writing this — each metric checked individually for
# references in alert and recording rules:
#
# apiserver_request_duration_seconds_bucket rules=0 23,810 series
# apiserver_request_body_size_bytes_bucket rules=0 23,200
# etcd_request_duration_seconds_bucket rules=0 15,840
# apiserver_watch_list_duration_seconds_bucket rules=0 11,538
# apiserver_watch_cache_read_wait_seconds_bucket rules=0 8,232
# apiserver_response_sizes_bucket rules=0 7,592
# apiserver_watch_events_sizes_bucket rules=0 5,220
#
# apiserver_request_sli_duration_seconds_bucket is DELIBERATELY KEPT: 23
# recording rules depend on it, including the apiserver availability SLO and
# the KubeAPIErrorBudgetBurn burn-rate series. Dropping it would silently
# break that alerting, which is the failure mode this comment exists to
# prevent if someone later "tidies up" this regex.
#
# Only the `_bucket` series are dropped. The matching `_count` and `_sum`
# remain, so request rates and mean latencies still work — what is lost is
# per-bucket quantiles.
#
# etcd_* appears here even though kubeEtcd is disabled below: these are the
# apiserver's own etcd-client metrics, exposed on the apiserver endpoint, not
# a separate etcd scrape.
kubeApiServer:
serviceMonitor:
metricRelabelings:
- action: drop
sourceLabels: [__name__]
regex: (apiserver_request_duration_seconds_bucket|apiserver_request_body_size_bytes_bucket|etcd_request_duration_seconds_bucket|apiserver_watch_list_duration_seconds_bucket|apiserver_watch_cache_read_wait_seconds_bucket|apiserver_response_sizes_bucket|apiserver_watch_events_sizes_bucket)
# Disable etcd/scheduler/controller-manager scraping (Talos doesn't expose them by default)
kubeEtcd:
enabled: false
kubeScheduler:
enabled: false
kubeControllerManager:
enabled: false
kubeProxy:
enabled: false
# Replaced by monitoring-rules/prometheusrule-jobs.yaml.
defaultRules:
disabled:
KubeJobFailed: true
KubeContainerWaiting: true