Tous les articles
Ven. 7 août 2026·7 min de lecture

☸️ Prometheus sur Kubernetes : déclarer ses cibles plutôt que d'éditer un YAML géant

Logos Prometheus, Grafana et Kubernetes

📚 Introduction

Sur une machine unique, configurer Prometheus est trivial : on liste ses cibles dans scrape_configs, on redémarre, c’est fini. Sur Kubernetes, ce modèle s’effondre pour deux raisons. D’abord les pods sont éphémères : leur IP change à chaque redéploiement, une liste statique de cibles n’a aucun sens. Ensuite le fichier devient un point de contention : si dix équipes déploient dans le même cluster, elles se retrouvent toutes à éditer le même ConfigMap, avec les conflits de merge et les rechargements ratés que ça implique.

Le Prometheus Operator répond aux deux d’un coup. Il introduit des ressources Kubernetes dédiées au monitoring : au lieu d’éditer une configuration centrale, chaque équipe crée un objet dans son propre namespace pour dire « scrape ce Service » ou « déclenche cette alerte ». L’Operator observe ces objets, régénère la configuration Prometheus et recharge l’instance. Personne ne touche plus au prometheus.yml.

En pratique, on installe rarement l’Operator seul : le chart Helm kube-prometheus-stack livre l’ensemble de la chaîne, dashboards et règles d’alerte compris. Ce post fait le tour de l’installation, des CRD qu’il faut connaître, et des réglages à ne pas oublier avant de mettre tout ça en production.

🔎 Pourquoi un Operator plutôt qu’un prometheus.yml ?

Parce qu’un Operator déplace la configuration là où vit l’application. Un Operator, c’est un contrôleur qui étend l’API Kubernetes avec ses propres types d’objets (des CRD, Custom Resource Definitions) et applique la boucle de réconciliation habituelle : on décrit l’état voulu, il fait converger le système.

Concrètement, l’équipe qui déploie une application livre son Deployment, son Service et son ServiceMonitor dans le même dossier de manifestes. L’Operator détecte l’ajout, reconstruit la configuration complète de Prometheus dans un Secret, et la recharge à chaud. La configuration reste une seule et même chose techniquement, mais elle n’est plus écrite à la main par personne — elle est calculée à partir des déclarations éparpillées dans le cluster.

🚀 Installer kube-prometheus-stack

Le chart est distribué comme artefact OCI et via le dépôt Helm classique. Avec Helm :

helm install prometheus \
  oci://ghcr.io/prometheus-community/charts/kube-prometheus-stack \
  --version 88.2.0 \
  --namespace monitoring --create-namespace

Épinglez la version : le chart bouge vite et les CRD évoluent avec. La version 88.2.0 embarque le Prometheus Operator v0.93.0.

Six composants arrivent d’un coup : le Prometheus Operator (qui surveille les CRD), Prometheus lui-même, Alertmanager pour le routage des notifications, Grafana avec ses dashboards Kubernetes préconfigurés, node-exporter pour les métriques système de chaque nœud, et kube-state-metrics pour l’état des objets Kubernetes.

Une fois les pods Running, un port-forward suffit pour regarder :

kubectl -n monitoring port-forward svc/prometheus-kube-prometheus-prometheus 9090
kubectl -n monitoring port-forward svc/prometheus-grafana 3000:80

🧱 Les CRD qui remplacent scrape_configs

Cinq types d’objets couvrent l’essentiel, et ils n’ont pas les mêmes propriétaires :

CRDRôleQui l’écrit
ServiceMonitorSélectionne un Service à scraperl’équipe applicative
PodMonitorSélectionne des pods, sans Service devantl’équipe applicative
PrometheusRuleAlertes et recording rulesl’équipe app ou SRE
PrometheusL’instance Prometheus elle-mêmel’équipe plateforme
AlertmanagerL’instance Alertmanagerl’équipe plateforme

Pour qu’un ServiceMonitor fonctionne, trois choses doivent s’aligner : le conteneur expose un port nommé, le Service reprend ce même nom de port, et le ServiceMonitor cible le Service par ses labels.

apiVersion: monitoring.coreos.com/v1
kind: ServiceMonitor
metadata:
  name: mon-api
  namespace: production
  labels:
    release: prometheus # sinon Prometheus l'ignore
spec:
  selector:
    matchLabels:
      app: mon-api
  endpoints:
    - port: metrics # le nom du port du Service, pas son numéro
      interval: 15s
      path: /metrics

Le label release: prometheus mérite qu’on s’y arrête : c’est le piège du chart. Par défaut, serviceMonitorSelectorNilUsesHelmValues vaut true, ce qui configure l’instance Prometheus avec un serviceMonitorSelector sur release: <nom de la release Helm>. Un ServiceMonitor sans ce label est parfaitement valide côté API, il n’apparaîtra simplement jamais dans les cibles. Aucun message d’erreur, aucun événement : juste rien.

Quand l’application n’a pas de Service — un batch, un DaemonSet interne — le PodMonitor fait le même travail en ciblant les pods directement. Attention au nom du champ, qui n’est pas endpoints mais podMetricsEndpoints :

apiVersion: monitoring.coreos.com/v1
kind: PodMonitor
metadata:
  name: mon-batch
  labels:
    release: prometheus
spec:
  selector:
    matchLabels:
      app: mon-batch
  podMetricsEndpoints:
    - port: metrics

🔔 Déclarer une alerte avec PrometheusRule

Même logique pour les alertes : un objet Kubernetes, versionné avec l’application, au lieu d’un fichier de rules monté dans le pod Prometheus.

apiVersion: monitoring.coreos.com/v1
kind: PrometheusRule
metadata:
  name: mon-api-rules
  labels:
    release: prometheus
spec:
  groups:
    - name: mon-api
      rules:
        - alert: MonApiTauxErreurEleve
          expr: |
            sum(rate(http_requests_total{job="mon-api", code=~"5.."}[5m]))
              / sum(rate(http_requests_total{job="mon-api"}[5m])) > 0.05
          for: 10m
          labels:
            severity: warning
          annotations:
            summary: 'Plus de 5 % de 5xx sur mon-api'
            runbook_url: 'https://runbooks.example.com/mon-api-5xx'

Le for: 10m est ce qui sépare une alerte utile d’un générateur de bruit : la condition doit tenir dix minutes d’affilée avant de déclencher, ce qui absorbe les pics passagers. Et le runbook_url finit dans la notification Alertmanager — c’est peu coûteux à écrire et ça évite à l’astreinte de chercher quoi faire à 3 h du matin. Ces mêmes expressions sont d’ailleurs le socle de vos SLO et error budgets : sans collecte fiable, pas de SLI mesurable.

Bonne nouvelle : la stack livre déjà plus de 130 règles d’alerte pour le cluster lui-même (KubePodCrashLooping, KubeNodeNotReady, KubeDeploymentReplicasMismatch…). Vos PrometheusRule n’ont à couvrir que vos applications.

🔍 kube-state-metrics ou cAdvisor : laquelle utiliser ?

Les deux sont installés et il est facile de les confondre. kube-state-metrics expose l’état déclaré des objets Kubernetes, tel que l’API le voit : nombre de replicas voulus et disponibles, phase des pods, compteur de redémarrages. cAdvisor, intégré au kubelet, expose la consommation réelle des conteneurs : CPU, mémoire, réseau, disque.

Autrement dit : « combien de pods devraient tourner ? » relève de kube-state-metrics, « combien de CPU consomment-ils ? » relève de cAdvisor.

# Deployments qui n'ont pas tous leurs replicas (kube-state-metrics)
kube_deployment_status_replicas_available
  < kube_deployment_status_replicas
 
# CPU consommé par pod, en cœurs (cAdvisor)
sum(rate(container_cpu_usage_seconds_total{container!="", image!=""}[5m])) by (pod)
 
# Mémoire par namespace (cAdvisor)
sum(container_memory_working_set_bytes{container!="", image!=""}) by (namespace)

Le image!="" n’est pas décoratif : sans lui, on ramasse aussi les séries agrégées au niveau du pod, qui font double emploi avec la somme de ses conteneurs. C’est le filtre que les recording rules livrées par la stack appliquent elles-mêmes.

Le réflexe à garder : les métriques cAdvisor suffixées _total sont des compteurs monotones, elles ne se lisent qu’à travers rate(). Les jauges de kube-state-metrics se lisent directement.

🎯 Passer en production

Les valeurs par défaut du chart conviennent à une découverte, pas à un cluster de production. Quatre réglages à revoir dans un values.yaml :

prometheus:
  prometheusSpec:
    retention: 30d
    retentionSize: '45GiB'
    replicas: 2
    storageSpec:
      volumeClaimTemplate:
        spec:
          accessModes: ['ReadWriteOnce']
          resources:
            requests:
              storage: 50Gi
    # scruter les objets de tous les namespaces, sans filtre sur le label release
    serviceMonitorSelectorNilUsesHelmValues: false
    podMonitorSelectorNilUsesHelmValues: false
    ruleSelectorNilUsesHelmValues: false
 
alertmanager:
  alertmanagerSpec:
    replicas: 3

storageSpec est le plus important : sans PVC, Prometheus écrit dans un emptyDir et perd tout son historique au premier redémarrage du pod. La rétention par défaut est de 10d ; retentionSize doit rester en dessous de la taille du volume, la purge se déclenchant à la première limite atteinte. Passer les trois booléens ...NilUsesHelmValues à false supprime le filtre sur le label release — pratique en multi-équipes, au prix de la garde-fou contre les selectors trop larges.

Une fois ce values.yaml écrit, il a sa place dans le dépôt Git géré par Argo CD comme n’importe quel autre manifeste — à l’exception des secrets, qui ne doivent jamais y figurer en clair.

⚠️ Quelques précautions

  • Le label release est la première cause de « ma cible n’apparaît pas ». Avant de fouiller les logs, vérifiez le serviceMonitorSelector de l’objet Prometheus avec kubectl -n monitoring get prometheus -o yaml.
  • Un namespaceSelector: {any: true} couplé à un selector large fait exploser la cardinalité. Chaque combinaison de labels crée une série ; sur un gros cluster, ça se paie immédiatement en RAM.
  • Les CRD ne sont pas supprimées avec helm uninstall. C’est volontaire, mais ça surprend lors d’une réinstallation : une mise à jour majeure du chart demande souvent d’appliquer les nouvelles CRD à la main.
  • alertmanager.yml contient des secrets (webhooks Slack, identifiants SMTP, clés PagerDuty). Passez par un Secret Kubernetes référencé depuis les values, jamais par des valeurs en clair versionnées.
  • Ne laissez pas Grafana avec ses identifiants par défaut. Le chart crée l’utilisateur admin (grafana.adminUser) ; définissez explicitement grafana.adminPassword, ou mieux, pointez grafana.admin.existingSecret vers un Secret que vous gérez.
  • Pour déboguer, lisez la configuration réellement chargée, pas celle que vous croyez avoir écrite : la page Status → Configuration de l’UI Prometheus l’affiche telle quelle, et kubectl -n monitoring logs deploy/prometheus-kube-prometheus-operator révèle les erreurs de réconciliation quand un objet est rejeté.

🎉 Conclusion

L’intérêt du Prometheus Operator n’est pas technique, il est organisationnel. Prometheus fait exactement le même travail qu’avant — il scrape, stocke, évalue des règles — mais la configuration cesse d’être un fichier que tout le monde se dispute pour devenir un ensemble d’objets déclarés à côté des applications qu’ils surveillent. C’est ce qui rend le monitoring d’un cluster multi-équipes tenable.

Le prix à payer, c’est une couche d’indirection de plus : quand une cible manque, il faut savoir remonter du ServiceMonitor jusqu’au selector de l’instance Prometheus. Une fois ce réflexe acquis — et le label release en tête —, la stack se tient toute seule. Pour l’expérimenter sans risque, un cluster k3s en local suffit largement.

🔗 Liens utiles

/faq

Questions fréquentes

Qu'est-ce que kube-prometheus-stack ?

+

C'est un chart Helm de la communauté Prometheus qui déploie d'un coup le Prometheus Operator, Prometheus, Alertmanager, Grafana, node-exporter et kube-state-metrics, avec des dashboards et plus de 130 règles d'alerte Kubernetes déjà écrites.

À quoi sert un ServiceMonitor ?

+

Un ServiceMonitor est un objet Kubernetes qui déclare quel Service scraper et sur quel port. L'Operator le détecte, régénère la configuration de Prometheus et recharge l'instance : on ajoute une cible sans jamais éditer le prometheus.yml central.

Pourquoi mon ServiceMonitor n'apparaît-il pas dans les cibles Prometheus ?

+

Dans neuf cas sur dix, il manque le label correspondant au nom de la release Helm. Par défaut le chart configure Prometheus pour ne sélectionner que les ServiceMonitors portant le label release égal au nom de la release, par exemple release: prometheus.

Faut-il kube-prometheus-stack sur un petit cluster ?

+

Sur un cluster de dev ou un homelab, la stack complète est lourde mais reste installable en une commande et donne immédiatement des dashboards utilisables. Pour un simple serveur sans Kubernetes, un Prometheus autonome avec ses scrape_configs suffit largement.