Superviser Kubernetes avec Prometheus

11 Octobre 2022 • 9 min

Cet article est le premier d’une série d’articles consacrée à l’utilisation de Prometheus.

Surveiller ses applications et son infrastructure est essentiel quand on opère une plateforme : pour être informé lorsqu’un problème survient ou mieux encore, pour l’anticiper. Kubernetes ne fait pas exception à la règle et nous allons voir ici en quoi Prometheus est le compagnon idéal pour superviser Kubernetes.

Les métriques dans Kubernetes

Dans un cluster Kubernetes, beaucoup d’éléments peuvent remonter des métriques utiles pour la supervision, faisons un tour d’horizon.

Les pods

C’est souvent les métriques qui intéressent le plus les utilisateurs, celles exposées directement par notre application (via une page dédiée ou un exporter en sidecar par exemple).

Mais il y a aussi les ressources utilisées par les conteneurs, principalement l’utilisation CPU et RAM, qui sont remontées par le Kubelet.

Les ressources Kubernetes

Les deployments, statefulset, job, secret, … On retrouve des métriques pour une bonne partie des ressources Kubernetes (via l’exporter kube-state-metrics). Il y a des métriques très génériques communes aux différentes ressources, mais aussi des métriques plus précises en rapport avec la ressource en question. Par exemple, on peut avoir le nombre de restart d’un container au sein d’un pod, l’état des replicas d’un deployment ou encore les status d’un job.

Le control-plane

L'API Server, controller-manager, le scheduler ou encore l'etcd. Ces composants du control-plane Kubernetes remontent chacun de nombreuses métriques très utiles pour détecter des problèmes sur notre cluster.

Les nodes

On a évidemment le classique node-exporter qui va remonter l’état de la machine, mais il faut également ajouter les métriques de Kubelet, ainsi que les certificats utilisés par les composants de Kubernetes.

Les composants système

Et l’on n’oublie pas les métriques des composants de base comme celui qui vous fournit le réseau (CNI) ou le stockage persistant (CSI), l’ingress-controller, celui de Gitops, … et bien évidemment Prometheus lui-même.

Prometheus à la sauce Kubernetes

On vient de le voir, il y a un certain nombre de choses à récupérer par notre Prometheus. Heureusement, il existe des projets pour nous aider dans notre périlleuse mission.

prometheus-operator

Le prometheus-operator a pour rôle de gérer Prometheus (spawner les pods, maintenir la redondance, …) ainsi que de générer ses différentes configurations (targets, alertes, …) depuis des Custom Resources comme par les ServiceMonitor ou les PodMonitor par exemple.

Cet opérateur est devenu un indispensable dans n’importe quel cluster Kubernetes et de très nombreuses applications fournissent un ServiceMonitor (une des CustomResources qui décrit comment récupérer les métriques depuis un Service) dans leur chart Helm.

kube-prometheus

Le projet kube-prometheus fournit un ensemble de manifests, et autres éléments nécessaires pour monitorer un cluster Kubernetes :

  • le prometheus-operator vu plus haut, avec une instance de la CustomResource Prometheus incluse
  • l’exporter node-exporter sur chaque node du cluster
  • l’exporter kube-state-metrics
  • tout un ensemble de ServiceMonitor pour chacun des exporter à monitorer

Mais il fournit également de quoi exploiter ces métriques par défaut avec :

  • un Grafana, pour afficher toutes les métriques récupérées sur des dashboards
  • un Alertmanager, pour envoyer les alertes générées à partir des métriques
  • kubernetes-mixin (cf ci-dessous)

kubernetes-mixin

Ce dernier, justement, est un projet qui regroupe de nombreux dashboards pour Grafana et alertes pour Prometheus.

Les dashboards fournis sont clairement une base très appréciable pour voir ce qui se passe dans son cluster.

kube-prometheus-stack: Un chart Helm pour les gouverner tous

Un chart Helm tout trouvé.
Un chart Helm pour les déployer tous et dans les ténèbres nous éclairer.

Prometheus supervisant Kubernetes

kube-prometheus-stack est notre meilleur allié pour sortir du noir et voir ce qui se passe sur notre cluster Kubernetes.

Il se base sur le projet kube-prometheus que nous avons vu plus haut et rend le tout facilement déployable et extrêmement configurable part un chart Helm via de très nombreuses values.

C’est LA bonne méthode pour déployer Prometheus dans Kubernetes, superviser tous les composants et avoir les outils de base pour exploiter les métriques.

Exemple sur un cluster déployé avec kubeadm

Installation

Place à la pratique, on va voir concrètement la mise en place sur un cluster déployé avec kubeadm.

On installe le chart avec :

helm repo add prometheus-community https://prometheus-community.github.io/helm-charts
helm repo update
kubectl create ns monitoring
helm -n monitoring install kube-prometheus-stack prometheus-community/kube-prometheus-stack

On se retrouve avec plusieurs pods dans notre namespace monitoring :

$ kubectl -n monitoring get pod -o name
pod/alertmanager-kube-prometheus-stack-alertmanager-0
pod/kube-prometheus-stack-grafana-649d6dcddc-j9dsj
pod/kube-prometheus-stack-kube-state-metrics-9d456cddd-gm94f
pod/kube-prometheus-stack-operator-66bfdc5dc8-589s2
pod/kube-prometheus-stack-prometheus-node-exporter-6zdgx
pod/kube-prometheus-stack-prometheus-node-exporter-9czwc
pod/kube-prometheus-stack-prometheus-node-exporter-9zz8f
pod/kube-prometheus-stack-prometheus-node-exporter-p5ctr
pod/kube-prometheus-stack-prometheus-node-exporter-pdpql
pod/prometheus-kube-prometheus-stack-prometheus-0

Il y a bien les composants que l’on a mentionnés plus tôt, le prometheus-operator, une instance de Prometheus, Alertmanager et Grafana, le kube-state-metrics et un node-exporter par nœud du cluster via un DaemonSet.

Et ce n’est évidemment pas tout, il y a plusieurs Services, ConfigMap et autres ressources liées au RBAC qui ont été créées. On retrouve bien sûr les ressources ServiceMonitor du prometheus-operator :

$ kubectl get -A servicemonitors -l 'release=kube-prometheus-stack' -o name
servicemonitor.monitoring.coreos.com/kube-prometheus-stack-alertmanager
servicemonitor.monitoring.coreos.com/kube-prometheus-stack-apiserver
servicemonitor.monitoring.coreos.com/kube-prometheus-stack-coredns
servicemonitor.monitoring.coreos.com/kube-prometheus-stack-kube-controller-manager
servicemonitor.monitoring.coreos.com/kube-prometheus-stack-kube-etcd
servicemonitor.monitoring.coreos.com/kube-prometheus-stack-kube-proxy
servicemonitor.monitoring.coreos.com/kube-prometheus-stack-kube-scheduler
servicemonitor.monitoring.coreos.com/kube-prometheus-stack-kube-state-metrics
servicemonitor.monitoring.coreos.com/kube-prometheus-stack-kubelet
servicemonitor.monitoring.coreos.com/kube-prometheus-stack-operator
servicemonitor.monitoring.coreos.com/kube-prometheus-stack-prometheus
servicemonitor.monitoring.coreos.com/kube-prometheus-stack-prometheus-node-exporter

C’est ce qui va générer les ‘targets’ côté Prometheus. Et justement vérifions cela, on va faire un port-forward sur le Prometheus pour voir ce qu’il récupère :

kubectl -n monitoring port-forward prometheus-kube-prometheus-stack-prometheus-0  9090:9090

On ouvre un navigateur sur http://localhost:9090 et on va dans Status/Targets :

Targets Prometheus

On y retrouve bien les ressources vues plus haut. En revanche, les éléments du control-plane ne semblent à priori pas accessibles. image-10.png

Si on déplie l’une des cibles on obtient : Target Prom down details

Le cas kubeadm

La connexion est refusée, Prometheus n’arrive donc pas à récupérer les métriques. Dans ce cas, c’est parce que kubeadm a fait le choix de laisser les composants du control-plane uniquement en écoute sur localhost, ce qui ne permet pas à Prometheus de récupérer les métriques.

Une des solutions possibles est de modifier ça pour écouter plus largement, comme suggéré dans la documentation de kube-prometheus.

On va donc éditer la configuration kubeadm et reconfigurer les composants du control-plane pour prendre en compte ces modifications. On édite la configuration avec kubectl -n kube-system edit cm kubeadm-config et on ajoute l’option bind-address pour le controller-manager et le scheduler et listen-metrics-urls pour etcd :

controllerManager:
  extraArgs:
    bind-address: "0.0.0.0"
scheduler:
  extraArgs:
    bind-address: "0.0.0.0"
etcd:
  local:
    extraArgs:
        listen-metrics-urls: http://0.0.0.0:2381

Puis sur chaque nœud du control-plane, on va faire :

kubeadm upgrade node phase control-plane

On attend bien sûr que la modification soit complètement appliquée à un nœud avant de passer au suivant, pour ne pas faire d’interruption.

Maintenant si on regarde côté Prometheus : Target control-plane up

Le controller-manager, le scheduler et etcd sont maintenant up, mais il reste kube-proxy qui est dans la même situation avec un “connection refused”.

Le problème est similaire, par défaut il n’écoute que sur localhost, donc on va modifier ça dans la configuration avec kubectl -n kube-system edit cm kube-proxy et rajouter dans la clé config.conf :

metricsBindAddress: 0.0.0.0

Et re-deployer les pods avec:

kubectl -n kube-system rollout restart daemonset kube-proxy

On vérifie dans Prometheus et tout est up : All targets up

On peut également regarder dans le menu “Alerts” de Prometheus et on y retrouve tout un jeu d’alertes préconfigurées qui nous préviendront en cas de problème sur notre cluster.

L’accès au Grafana

Et si on regardait les dashboards que nous a installé le chart ? On va, comme pour Prometheus, faire un port-forward :

kubectl -n monitoring port-forward deployment/kube-prometheus-stack-grafana 3000:3000

Et se connecter sur http://localhost:3000
Il nous faut le mot de passe de l’utilisateur admin, par défaut le chart met prom-operator.

Une fois loggué, on peut parcourir les dashboards et voir qu’il y en a une certaine quantité qui viennent entre autres du projet kubernetes-mixin, mais pas que.

image-4.png image-5.png

On peut regarder par exemple le dashboard “Kubernetes / Compute Resources / Pod” et voir la consommation de notre Prometheus : image-6.pngimage-7.png

Recevoir les alertes

On a les métriques, des alertes et des dashboards. Par contre, il faut configurer un minimum Alertmanager pour lui dire où envoyer les alertes, sinon elles vont être moins utiles.

Par exemple, pour déclencher un webhook dès qu’une alerte est envoyée à Alertmanager, on rajoute dans les values du chart et on upgrade :

alertmanager:
  config:
    route:
      receiver: 'trigger_ops'
    receivers:
    - name: 'null'
    - name: 'trigger_ops'
      webhook_configs:
      - send_resolved: true
        url: 'https://my-webhook-endpoint.example.com'

On ajoute un receiver par défaut nommé “trigger_ops” et on le configure avec un webhook. Ici on laisse le receiver “null” qui vient de la configuration par défaut du chart.

Bien entendu, il faut adapter en fonction des besoins en se basant sur la documentation de configuration d’Alertmanager.

Il n’y a plus qu’à tester ça, par exemple en éteignant l’une des machines du control-plane et en attendant que les alertes se déclenchent.

Au bout d’un moment, on a bien des alertes qui sont en “Firing” côté Prometheus et on les retrouve également sur l’Alertmanager: image-8.pngimage-9.png

Les petits trucs à connaître

Récupérer les métriques de tous les ServiceMonitor / PodMonitor du cluster

Par défaut la Custom Resource Prometheus déployée par le chart kube-prometheus-stack est configurée pour ne récupérer que les ServiceMonitor et PodMonitor qui ont été déployés par ce dernier. Il est possible de définir une autre ressource Prometheus qui ciblera les ServiceMonitor / PodMonitor de notre applicatif, mais on peut souvent se contenter d’un seul Prometheus qui récupère l’ensemble des métriques.

Pour cibler tous les ServiceMonitor/PodMonitor du cluster, il suffit de déployer le chart avec les values suivantes :

prometheus:
  prometheusSpec:
    serviceMonitorSelectorNilUsesHelmValues: false
    podMonitorSelectorNilUsesHelmValues: false

Si les values pour serviceMonitorSelector et podMonitorSelector n’ont pas été modifiées, ce sera cette valeur par défaut ({}) qui sera utilisée pour sélectionner toutes les ressources.

Exposer Grafana, Alertmanager et Prometheus via un ingress

Accéder aux WebUI via un port-forward peut bien nous dépanner, mais pour une utilisation au jour le jour, configurer un ingress est plus pratique.

Pour ce faire, il faut utiliser :

grafana:
  ingress:
    enabled: true
    ingressClassName: nginx
    pathType: Prefix
    hosts:
      - grafana.domain.com
alertmanager:
  ingress:
    enabled: true
    ingressClassName: nginx
    pathType: Prefix
    hosts:
      - alertmanager.domain.com
prometheus:
  ingress:
    enabled: true
    ingressClassName: nginx
    pathType: Prefix
    hosts:
      - prometheus.domain.com

Le cas des cloud-provider

Quand on utilise un Kubernetes managé chez un cloud-provider, généralement on n’a pas le control-plane. Pour éviter d’avoir des éléments déployés inutilement et surtout les alertes qui indiquent que certains éléments du control-plane ne tournent pas, il suffit de les désactiver explicitement :

kubeEtcd:
  enabled: false

kubeControllerManager:
  enabled: false

kubeScheduler:
  enabled: false

kubeProxy:
  enabled: false

À noter que l’on désactive également kube-proxy, car souvent le CNI (Container Network Interface) par défaut du cloud-provider le remplace. On peut par contre conserver l’API Server qui reste accessible et qui peut fournir des métriques intéressantes (mais volumineuses). Si on souhaite le désactiver lui aussi :

kubeApiServer:
  enabled: false

Volume persistant

Si l’on veut conserver les métriques entre chaque redémarrage du pod, il faut lui configurer un volume persistant, même chose pour Alertmanager si on veut conserver les silences ou Grafana pour conserver les modifications :

prometheus:
  prometheusSpec:
    storageSpec:
      volumeClaimTemplate:
        spec:
          storageClassName: my-storage-class
          accessModes: ["ReadWriteOnce"]
          resources:
            requests:
              storage: 20Gi
alertmanager:
  alertmanagerSpec:
    storage:
      volumeClaimTemplate:
        spec:
          storageClassName: my-storage-class
          accessModes: ["ReadWriteOnce"]
          resources:
            requests:
              storage: 5Gi
grafana:
  persistence:
    enabled: true
    type: statefulset
    storageClassName: my-storage-class

Conclusion

Ça y est ! Nous avons maintenant une stack complète de supervision et d’alerting, le tout à la sauce K8S, c’est-à-dire par et pour le cluster Kube. C’était une bonne petite montagne à gravir, mais heureusement le chart kube-prometheus-stack est un peu notre Sam Gamegie qui nous porte sur ses épaules !

De la même manière que l’univers de Tolkien semble être un puit sans fond pour Hollywood, il reste encore beaucoup de sujets à aborder autour de Prometheus : le stockage de métriques sur le long terme, la haute disponibilité ou une utilisation plus avancée du chart kube-prometheus-stack. A suivre bientôt dans notre prochain article de la série !


Ne ratez pas nos prochains articles DevOps et Cloud Native! Suivez Enix sur Twitter!