Ce guide couvre le déploiement de Prometheus sur Kubernetes avec kube-prometheus-stack. Vous apprendrez le pattern Operator, les ServiceMonitors, et comment faire remonter les métriques de vos applications.
Ce que vous allez apprendre
Section intitulée « Ce que vous allez apprendre »- Installer kube-prometheus-stack avec Helm, version épinglée
- Déclarer une cible de collecte avec un ServiceMonitor ou un PodMonitor
- Écrire une alerte sous forme de PrometheusRule
- Adapter les valeurs Helm pour un déploiement de production (stockage, HA, secrets)
- Diagnostiquer une cible absente des targets Prometheus
Pourquoi un Operator (et pas juste un Deployment) ?
Section intitulée « Pourquoi un Operator (et pas juste un Deployment) ? »Sur Kubernetes, on pourrait déployer Prometheus "classiquement" (Deployment + ConfigMap). Mais ça pose des problèmes :
| Problème | Avec ConfigMap | Avec Operator |
|---|---|---|
| Ajouter une cible | Éditer ConfigMap + reload | Créer un ServiceMonitor |
| Multi-équipe | Conflits sur le même fichier | Chaque équipe gère ses CRDs |
| Validation | Aucune (YAML brut) | Schéma Kubernetes |
| Découverte | Manuelle | Automatique (labels) |
Le pattern Operator : au lieu de modifier un fichier de config central, chaque équipe déclare un ServiceMonitor (CRD Kubernetes). L'Operator génère automatiquement la config Prometheus.
Ce qu'installe kube-prometheus-stack
Section intitulée « Ce qu'installe kube-prometheus-stack »Un helm install déploie six composants d'un coup, et c'est utile de savoir
lesquels vous pourriez désactiver. Les quatre premiers forment le coeur
fonctionnel. Les deux derniers sont des sources de métriques : Node Exporter
remonte l'état des machines, kube-state-metrics l'état des objets Kubernetes.
Si vous avez déjà l'un des deux dans le cluster, laissez-le et désactivez celui
de la stack, sinon vous collecterez les mêmes séries deux fois.
| Composant | Rôle |
|---|---|
| Prometheus Operator | Surveille les CRDs, génère la config |
| Prometheus | Collecte et stockage (TSDB) |
| Alertmanager | Routing des alertes |
| Grafana | Dashboards préconfigurés |
| Node Exporter | Métriques système des nodes |
| kube-state-metrics | État des objets K8s (pods, deployments…) |
Installation
Section intitulée « Installation »Les quatre étapes ci-dessous partent d'un cluster vierge et se terminent par une
vérification qui prouve que la collecte fonctionne, pas seulement que les pods
démarrent. C'est la distinction importante : un Prometheus Running qui ne
scrape rien reste un Prometheus inutile. La dernière étape compte les cibles
actives pour lever ce doute.
-
Ajouter le repository Helm
Fenêtre de terminal helm repo add prometheus-community \https://prometheus-community.github.io/helm-chartshelm repo update -
Installer la stack (version pinnée)
Fenêtre de terminal helm install prometheus prometheus-community/kube-prometheus-stack \--namespace monitoring \--create-namespace \--version 87.10.1 -
Vérifier le déploiement (tous les pods Running)
Fenêtre de terminal kubectl get pods -n monitoring -wAttendez ~2-3 minutes. Tous les pods doivent être
Running:NAME READY STATUSalertmanager-prometheus-kube-prometheus-alertmanager-0 2/2 Runningprometheus-grafana-xxx 3/3 Runningprometheus-kube-prometheus-operator-xxx 1/1 Runningprometheus-kube-state-metrics-xxx 1/1 Runningprometheus-prometheus-kube-prometheus-prometheus-0 2/2 Runningprometheus-prometheus-node-exporter-xxx 1/1 Running -
Vérification rapide : Prometheus fonctionne
Fenêtre de terminal kubectl port-forward -n monitoring svc/prometheus-kube-prometheus-prometheus 9090:9090 &curl -s localhost:9090/api/v1/targets | jq '.data.activeTargets | length'# Attendu : un nombre > 10 (les targets K8s)
Accéder aux interfaces
Section intitulée « Accéder aux interfaces »Aucune de ces interfaces n'est exposée hors du cluster par défaut, ce qui est
volontaire : Prometheus et Alertmanager n'ont pas d'authentification native et
Grafana démarre avec un mot de passe connu. kubectl port-forward ouvre un
tunnel local le temps de la consultation, sans rien publier. Notez le
3000:80 pour Grafana : le Service écoute sur le port 80, pas sur 3000.
# Prometheuskubectl port-forward -n monitoring svc/prometheus-kube-prometheus-prometheus 9090:9090
# Grafana (mot de passe par défaut: prom-operator)kubectl port-forward -n monitoring svc/prometheus-grafana 3000:80
# Alertmanagerkubectl port-forward -n monitoring svc/prometheus-kube-prometheus-alertmanager 9093:9093Les CRDs à connaître
Section intitulée « Les CRDs à connaître »L'Operator surveille cinq Custom Resource Definitions, et la colonne « Qui le crée » est celle qui compte pour organiser le travail. Les trois premières appartiennent aux équipes applicatives et vivent dans leurs dépôts, à côté du code. Les deux dernières décrivent les instances elles-mêmes et restent aux mains de l'équipe plateforme, parce qu'une erreur à ce niveau affecte toute la collecte.
| CRD | Rôle | Qui le crée |
|---|---|---|
| ServiceMonitor | "Scrappe ce Service" | Équipe app |
| PodMonitor | "Scrappe ces Pods" (sans Service) | Équipe app |
| PrometheusRule | Alertes et recording rules | Équipe app/SRE |
| Prometheus | Instance Prometheus | Ops/Platform |
| Alertmanager | Instance Alertmanager | Ops/Platform |
Workflow typique :
- L'équipe platform installe kube-prometheus-stack
- Chaque équipe app crée ses ServiceMonitors + PrometheusRules
- L'Operator génère la config automatiquement
Votre premier ServiceMonitor (5 minutes)
Section intitulée « Votre premier ServiceMonitor (5 minutes) »Créons un ServiceMonitor pour une application qui expose /metrics.
Prérequis : une app avec métriques
Section intitulée « Prérequis : une app avec métriques »Deux détails de ce manifeste conditionnent la suite. Le conteneur déclare un
port nommé metrics, et le Service reprend ce même nom : c'est par ce nom,
et non par le numéro, que le ServiceMonitor désignera la cible. Ensuite, le
Service porte le label app: my-app, celui que le ServiceMonitor ira chercher.
Sans ces deux éléments alignés, la collecte ne démarrera jamais.
apiVersion: apps/v1kind: Deploymentmetadata: name: my-app namespace: default labels: app: my-appspec: replicas: 2 selector: matchLabels: app: my-app template: metadata: labels: app: my-app spec: containers: - name: my-app image: my-app:1.4.2 # jamais :latest, sinon le rollback est impossible ports: - containerPort: 8080 name: http - containerPort: 9090 name: metrics---apiVersion: v1kind: Servicemetadata: name: my-app namespace: default labels: app: my-appspec: ports: - port: 8080 name: http - port: 9090 name: metrics selector: app: my-appServiceMonitor
Section intitulée « ServiceMonitor »Le ServiceMonitor fait le lien entre deux mondes de labels, et c'est là que se
trompent la plupart des débutants. Le spec.selector désigne le Service à
scraper, tandis que le label release: prometheus de metadata sert à
l'Operator pour décider si ce ServiceMonitor le concerne. Les deux sont
indispensables et n'ont rien à voir l'un avec l'autre.
apiVersion: monitoring.coreos.com/v1kind: ServiceMonitormetadata: name: my-app namespace: default # Peut être dans le namespace de l'app labels: release: prometheus # OBLIGATOIRE, voir l'encadré ci-dessousspec: selector: matchLabels: app: my-app # Matche le label du Service endpoints: - port: metrics # Nom du port dans le Service interval: 15s path: /metricsVérification
Section intitulée « Vérification »Vérifiez dans cet ordre. La première commande confirme seulement que l'objet
existe dans etcd, ce qui ne prouve rien sur la collecte. C'est la page
Status puis Targets de Prometheus qui fait foi : votre cible doit y
apparaître avec l'état UP. Une cible absente signifie que l'Operator a ignoré
le ServiceMonitor ; une cible présente mais DOWN signifie qu'il l'a bien prise
en compte mais que le point de collecte ne répond pas.
# ServiceMonitor créé ?kubectl get servicemonitor -A
# Visible dans Prometheus Targets ?kubectl port-forward -n monitoring svc/prometheus-kube-prometheus-prometheus 9090:9090# Puis : Status → Targets → cherchez "my-app"PodMonitor : quand il n'y a pas de Service
Section intitulée « PodMonitor : quand il n'y a pas de Service »Certains pods n'ont pas de Service devant eux : les Jobs et CronJobs
n'en ont pas besoin, et les pods pilotés par un contrôleur maison non plus. Le
PodMonitor sélectionne alors directement les pods par leurs labels. La
différence de structure avec le ServiceMonitor tient en un champ :
podMetricsEndpoints remplace endpoints. Attention avec namespaceSelector: any: true, qui étend la sélection à tout le cluster et peut faire exploser le
nombre de cibles si le label choisi est trop courant.
apiVersion: monitoring.coreos.com/v1kind: PodMonitormetadata: name: batch-jobs namespace: monitoring labels: release: prometheusspec: selector: matchLabels: type: batch-job namespaceSelector: any: true # Tous les namespaces podMetricsEndpoints: - port: metrics interval: 30sPrometheusRule : alertes Kubernetes-native
Section intitulée « PrometheusRule : alertes Kubernetes-native »Les alertes vivent elles aussi dans des objets Kubernetes, ce qui permet à
chaque équipe de livrer ses règles avec son application. Deux champs pilotent le
comportement : expr est la requête PromQL évaluée en continu, for est la
durée pendant laquelle elle doit rester vraie avant que l'alerte parte. Ce for
est votre principal filtre anti-bruit : sans lui, un pic de deux secondes
réveille l'astreinte. Le champ runbook_url n'a pas d'effet technique mais
change tout pour la personne réveillée à trois heures du matin.
apiVersion: monitoring.coreos.com/v1kind: PrometheusRulemetadata: name: my-app-alerts namespace: monitoring labels: release: prometheus # Même piège que le ServiceMonitorspec: groups: - name: my-app rules: - alert: MyAppDown expr: up{job="my-app"} == 0 for: 2m labels: severity: critical team: backend annotations: summary: "{{ $labels.instance }} is down" runbook_url: "https://wiki/runbooks/my-app-down"
- alert: MyAppHighErrorRate expr: | sum(rate(http_requests_total{job="my-app", status=~"5.."}[5m])) / sum(rate(http_requests_total{job="my-app"}[5m])) > 0.05 for: 3m labels: severity: warning team: backend annotations: summary: "Error rate > 5%"Configuration production (values.yaml)
Section intitulée « Configuration production (values.yaml) »Ce fichier de valeurs regroupe les réglages qui distinguent un déploiement de
test d'un déploiement durable. Trois blocs méritent votre attention : le
storageSpec, sans lequel toutes les métriques disparaissent au redémarrage du
pod ; les trois lignes ...NilUsesHelmValues: false, qui suppriment le filtre
sur le label release et font scraper tous les ServiceMonitors du cluster ; et
le couple retention / retentionSize, où c'est la première limite
atteinte qui déclenche la purge.
# Prometheusprometheus: prometheusSpec: retention: 15d retentionSize: 50GB replicas: 2 # HA storageSpec: volumeClaimTemplate: spec: storageClassName: gp3 accessModes: ["ReadWriteOnce"] resources: requests: storage: 100Gi
# IMPORTANT : scraper tous les ServiceMonitors (pas seulement release=prometheus) serviceMonitorSelectorNilUsesHelmValues: false podMonitorSelectorNilUsesHelmValues: false ruleSelectorNilUsesHelmValues: false
# Alertmanageralertmanager: alertmanagerSpec: replicas: 3 storage: volumeClaimTemplate: spec: storageClassName: gp3 accessModes: ["ReadWriteOnce"] resources: requests: storage: 10Gi config: global: resolve_timeout: 5m route: receiver: 'slack-default' group_by: ['alertname', 'namespace', 'service'] routes: - matchers: - severity="critical" receiver: 'pagerduty' receivers: - name: 'slack-default' slack_configs: - api_url: 'https://hooks.slack.com/services/...' channel: '#alerts' - name: 'pagerduty' pagerduty_configs: - routing_key: '$PAGERDUTY_KEY'
# Grafanagrafana: adminPassword: 'VotreMotDePasseSecurise' persistence: enabled: true size: 10Gi
# Node Exporter sur TOUS les nodes (y compris taints)prometheus-node-exporter: tolerations: - operator: Exists
# kube-state-metrics : labels custom sur les podskube-state-metrics: metricLabelsAllowlist: - pods=[app,version,team] - deployments=[app,version]helm upgrade prometheus prometheus-community/kube-prometheus-stack \ -n monitoring \ -f values.yaml \ --version 87.10.1Un dernier réglage mérite d'être isolé, car c'est celui qui débloque le plus
souvent une collecte incomplète. Par défaut, l'Operator n'accepte que les
ServiceMonitors portant le label release: prometheus. En passant
serviceMonitorSelectorNilUsesHelmValues à false dans
prometheus.prometheusSpec, ce filtre disparaît et tous les ServiceMonitors
du cluster sont pris en compte. Le confort a une contrepartie : n'importe quelle
équipe peut alors ajouter des cibles à votre Prometheus, et la cardinalité vous
échappe.
prometheus: prometheusSpec: serviceMonitorSelectorNilUsesHelmValues: falseMétriques Kubernetes automatiques
Section intitulée « Métriques Kubernetes automatiques »Sans que vous ayez rien à déclarer, la stack collecte déjà deux familles de
métriques. La distinction entre les deux est la première chose à comprendre
pour ne pas chercher au mauvais endroit : kube-state-metrics décrit l'état
souhaité et constaté des objets Kubernetes, cAdvisor mesure la consommation
réelle des conteneurs. Une question sur un Deployment se répond avec la
première source, une question sur la mémoire consommée avec la seconde.
kube-state-metrics (état des objets)
Section intitulée « kube-state-metrics (état des objets) »Ces métriques décrivent ce que l'API Kubernetes déclare, pas ce que les machines consomment. Elles répondent à des questions du type « combien de replicas sont disponibles » ou « ce pod redémarre-t-il en boucle ». C'est la source à interroger pour surveiller l'orchestration ; pour la consommation réelle, il faut les métriques du bloc suivant.
| Métrique | Description |
|---|---|
kube_pod_status_phase | Phase du pod (Pending, Running…) |
kube_pod_container_status_restarts_total | Restarts |
kube_deployment_status_replicas_available | Replicas ready |
kube_node_status_condition | État des nodes |
kubelet/cAdvisor (ressources conteneurs)
Section intitulée « kubelet/cAdvisor (ressources conteneurs) »Ici on mesure la consommation observée, relevée par cAdvisor intégré au
kubelet. Le suffixe _total des deux premières métriques signale un
compteur qui ne fait que croître : sa valeur brute n'a aucun intérêt, il
faut toujours lui appliquer rate() pour obtenir une consommation par seconde.
container_memory_usage_bytes est en revanche une jauge, lisible telle
quelle.
| Métrique | Description |
|---|---|
container_cpu_usage_seconds_total | CPU par container |
container_memory_usage_bytes | Mémoire par container |
container_network_receive_bytes_total | Réseau |
Requêtes PromQL Kubernetes
Section intitulée « Requêtes PromQL Kubernetes »Ces cinq requêtes couvrent les questions les plus fréquentes en exploitation.
Le filtre container!="" des requêtes de consommation n'est pas décoratif : il
écarte les séries agrégées au niveau du pod, sans lequel vous compteriez chaque
ressource deux fois. Les trois dernières requêtes renvoient un résultat
seulement quand quelque chose ne va pas, ce qui les rend directement
réutilisables comme expressions d'alerte.
# Pods en CrashLoopBackOffkube_pod_container_status_waiting_reason{reason="CrashLoopBackOff"} > 0
# CPU par pod (cores)sum(rate(container_cpu_usage_seconds_total{container!=""}[5m])) by (pod, namespace)
# Mémoire par namespace (GB)sum(container_memory_usage_bytes{container!=""}) by (namespace) / 1e9
# Deployments avec replicas manquantskube_deployment_status_replicas_available < kube_deployment_spec_replicas
# Nodes NotReadykube_node_status_condition{condition="Ready", status="true"} == 0Alertes incluses (centaines)
Section intitulée « Alertes incluses (centaines) »kube-prometheus-stack inclut des règles d'alerte préconfigurées :
| Alerte | Sévérité | Description |
|---|---|---|
KubeNodeNotReady | Warning | Node non prêt |
KubePodCrashLooping | Warning | Pod en crash loop |
KubeDeploymentReplicasMismatch | Warning | Replicas manquants |
KubePersistentVolumeFillingUp | Critical | PV bientôt plein |
PrometheusNotConnectedToAlertmanagers | Warning | Prometheus → Alertmanager cassé |
TargetDown | Warning | Target non scrapable |
# Lister toutes les règleskubectl get prometheusrules -n monitoring
# Voir le détailkubectl get prometheusrules -n monitoring \ prometheus-kube-prometheus-kubernetes-apps -o yamlScraper des services externes
Section intitulée « Scraper des services externes »Tout ne tourne pas dans le cluster : bases de données historiques, équipements
réseau, serveurs physiques. Pour ces cibles, il n'existe ni Service ni Pod à
sélectionner, donc ni ServiceMonitor ni PodMonitor. La solution passe par un
bloc de configuration Prometheus classique, injecté depuis un Secret que
l'Operator concatène à la configuration qu'il génère. Le format attendu à
l'intérieur du Secret est celui de la section scrape_configs de Prometheus.
apiVersion: v1kind: Secretmetadata: name: additional-scrape-configs namespace: monitoringstringData: additional-scrape-configs.yaml: | - job_name: 'external-nodes' static_configs: - targets: - 'external-db.example.com:9104' - 'legacy-server.example.com:9100'Référencez dans values.yaml :
prometheus: prometheusSpec: additionalScrapeConfigsSecret: enabled: true name: additional-scrape-configs key: additional-scrape-configs.yamlLes pièges classiques
Section intitulée « Les pièges classiques »Ces cinq situations ont un point commun qui les rend pénibles : rien n'échoue
visiblement. Aucun objet n'est rejeté, aucun pod ne redémarre, la cible est
simplement absente des targets. Le réflexe qui les couvre presque toutes est de
comparer, dans l'ordre, le label release, le namespaceSelector, puis le nom
du port dans le Service.
| Piège | Symptôme | Solution |
|---|---|---|
Label release manquant | ServiceMonitor ignoré | Ajouter release: prometheus |
| Namespace non surveillé | Target absent | Vérifier namespaceSelector |
| Port name incorrect | Scrape timeout | Matcher le nom du port Service |
| Selector trop large | Explosion targets | Être plus spécifique |
| Pas de PVC | Données perdues au restart | Configurer storageSpec |
Dépannage
Section intitulée « Dépannage »Quand une cible manque, l'information de référence n'est ni dans Prometheus ni dans vos manifestes, mais dans la configuration générée par l'Operator. La commande donnée plus bas la décompresse et l'affiche : si votre job n'y figure pas, le problème est en amont, au niveau de la sélection des ServiceMonitors. S'il y figure, le problème est réseau ou applicatif.
| Symptôme | Commande de debug |
|---|---|
| ServiceMonitor pas scrapé | kubectl get servicemonitor -A -o yaml | grep -A3 labels |
| Prometheus OOM | kubectl top pod -n monitoring + réduire cardinalité |
| Config générée | Voir ci-dessous |
| Alertes pas envoyées | kubectl logs -n monitoring alertmanager-... |
Voir la config Prometheus générée :
kubectl get secret -n monitoring \ prometheus-prometheus-kube-prometheus-prometheus \ -o jsonpath='{.data.prometheus\.yaml\.gz}' | base64 -d | gunzip | head -100Logs utiles :
# Operator (génère la config)kubectl logs -n monitoring deploy/prometheus-kube-prometheus-operator
# Prometheuskubectl logs -n monitoring prometheus-prometheus-kube-prometheus-prometheus-0 -c prometheus
# Alertmanagerkubectl logs -n monitoring alertmanager-prometheus-kube-prometheus-alertmanager-0À retenir
Section intitulée « À retenir »- Operator pattern : les CRDs remplacent les fichiers de config
- ServiceMonitor : déclare un Service à scraper
- Label
release: prometheus: obligatoire par défaut (le piège #1) - Dashboards inclus : observabilité K8s prête à l'emploi
- HA :
replicas: 2pour Prometheus,replicas: 3pour Alertmanager - Stockage : toujours configurer
storageSpecen production