Supervision et Kubernetes
Introduction
Les années précédentes, nous avons vu les différents types de supervision des infrastructures.
Pour rappel, on distingue trois grands axes de supervision:
Réseau
Ici, on va principalement surveiller l'état du réseau et de ses différents composants:
- routeurs
- commutateurs
- interfaces réseau
- états de liens
- tables de routage
- bande passante
- pertes
- latence
- statut de la QoS
Le protocole de choix reste ici SNMP, avec des solutions spécialisées comme Observium ou LibreNMS. Des visualisations de type WeatherMap permettent en un coup d'oeil de vérifier l'état du réseau et d'identifier les comportements anormaux.
Système
La supervision Système se concentre, elle, sur la collecte et l'aggrégation de métriques purement système:
- Utilisation CPU
- Utilisation Mémoire
- Espace disque
- Entrées/Sorties disque et réseau
- etc
Applicative
La supervision Applicative elle se focalise sur le bon fonctionnement des applicatifs de l'infrastructure. On peut citer pêle-mêle:
- Bon fonctionnement des applicatifs eux-mêmes (est-ce que postgresql fonctionne et répond?)
- temps de réponse des applicatifs (Nginx me sert-il la page d'accueil du site de l'entreprise en 50 ms ou en 5 secondes?)
- Le temps CPU utilisé par une fonction de notre code, ou les erreurs générées par le code de notre applicatif durant son exécution (statsd, Sentry, etc)
Ici, on utilisera généralement conjointement des solutions de supervision classiques ainsi que de l'aggrégation de logs, et potentiellement des outils comme Sentry, New Relic (propriétaire mais puissant), ou statsd qui nous remonteront, notamment, les erreurs applicatives ainsi que leur origine dans le code source de notre applicatif, et les problèmes de performance liés au code même de l'applicatif.
Il existe pléthore de solutions de supervision, de Nagios à Zabbix en passant par Prometheus/Grafana, couplée à des solutions d'aggrégation de logs telles qu'ELK, Graylog ou bien Loki (très prometteur)
On utilise en général conjointement plusieurs solutions de supervision afin d'obtenir la meilleure visibilité possible sur notre infrastructure.
Kubernetes: outils de supervision
Bien évidemment, on va vouloir superviser et monitorer nos clusters Kubernetes de la même manière que nous monitorons le reste de notre infrastucture.
Heureusement, nous disposons de tout un panel de solutions.
Tout d'abord, la majorité des "distributions" Kubernetes intègrent un petit serveur de métriques de base, metrics-server. Son rôle est principalement de remonter l'utilisation RAM et CPU de nos pods afin que Kubernetes puisse déterminer s'il peut provisionner tel ou tel nouveau pod sur un noeud de notre cluster ou pas. Couplé au système de requêtes et de limites, cela nous permet de nous assurer que nos charges de travail disposent toujours de suffisamment de ressources pour s'exécuter correctement.
Cependant, si nous voulons obtenir plus d'informations sur l'état de notre cluster, par exemple le nombre de requêtes par seconde reçues par le serveur API de Kubernetes, ou l'état de la base etcd, par exemple, nous allons devoir utiliser des outils externes dédiés, par exemple:
- Prometheus via kube-prometheus-stack, dashboards via Grafana, la référence bien connue dans le domaine
- Victoria Metrics (avec Mimir), un concurrent avec des avantages certains, notamment la légèreté. Ne l'ayant pas encore testé en profondeur, je ne me permettrais pas d'avis autre que de dire que cela semble prometteur
Dans la suite de ce module, nous utiliserons kube-prometheus-stack du fait de sa maturité.
Installons kube-prometheus-stack sur notre cluster K3S
Là, c'est très simple, il va nous suffire d'utiliser Helm pour ajouter le dépôt de chartes correspondant, et d'installer la charte en question:
helm repo add prometheus https://prometheus-community.github.io/helm-charts
helm repo update
helm fetch prometheus/kube-prometheus-stack
tar xvzf kube-prometheus-stack.tar.gz
cd kube-prometheus-stack
vim kube-prometheus-stack/values.yaml
# on va modifier les paramètres ingress
# pensez à ajouter un enregistrement host sur votre laptop !
helm install -n prom --create-namespace prometheus prometheus/kube-prometheus-stack -f values.yaml
Et on va attendre quelques minutes. Lorsque tout sera terminé, un:
kubectl get ingress,svc,pods -n prom
devrait nous renvoyer:
lidstah@marfak:~$ kubectl get ingress,svc,pods -n prom
NAME CLASS HOSTS ADDRESS PORTS AGE
ingress.networking.k8s.io/prom-stack-grafana traefik promtest.lidstah.info 192.168.1.11,192.168.1.49 80, 443 8d
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
service/alertmanager-operated ClusterIP None <none> 9093/TCP,9094/TCP,9094/UDP 8d
service/prom-stack-grafana ClusterIP 10.43.176.117 <none> 80/TCP 8d
service/prom-stack-kube-prometheus-alertmanager ClusterIP 10.43.8.159 <none> 9093/TCP,8080/TCP 8d
service/prom-stack-kube-prometheus-operator ClusterIP 10.43.146.14 <none> 443/TCP 8d
service/prom-stack-kube-prometheus-prometheus ClusterIP 10.43.234.76 <none> 9090/TCP,8080/TCP 8d
service/prom-stack-kube-state-metrics ClusterIP 10.43.181.176 <none> 8080/TCP 8d
service/prom-stack-prometheus-node-exporter ClusterIP 10.43.100.13 <none> 9100/TCP 8d
service/prometheus-operated ClusterIP None <none> 9090/TCP 8d
NAME READY STATUS RESTARTS AGE
pod/alertmanager-prom-stack-kube-prometheus-alertmanager-0 2/2 Running 0 14m
pod/prom-stack-grafana-fbcb88649-6p2sh 3/3 Running 0 14m
pod/prom-stack-kube-prometheus-operator-59498fb75b-mdwsd 1/1 Running 0 14m
pod/prom-stack-kube-state-metrics-5b44dc4467-x99pz 1/1 Running 0 14m
pod/prom-stack-prometheus-node-exporter-flfql 1/1 Running 0 7d10h
pod/prom-stack-prometheus-node-exporter-pkj4h 1/1 Running 1 (4d20h ago) 7d10h
pod/prometheus-prom-stack-kube-prometheus-prometheus-0 2/2 Running 2 (4d20h ago) 7d10h
Une fois terminé, on va pouvoir récupérer le mot de passe admin généré:
kubectl get secret -n prom prom-stack-grafana -o yaml
Il ne nous reste plus qu'à décoder login et mot de passe obtenus dans le yaml du secret via la commande base64. Normalement, vous savez faire
En se connectant à l'adresse définie lorsque nous avons configuré l'ingress, et en s'identifiant avec les informations obtenues à l'instant, on devrait obtenir ceci:

La stack a la bonne idée d'intégrer tout un ensemble de dashboards déjà fonctionnels, qui vont nous remonter les informations obtenues des métriques collectées sur le cluster, notamment concernant le serveur API, chaque pod, les stats etcd, etc.
Exemple:

On va explorer plusieurs de ces dashboards, mais on va ensuite attaquer la partie la plus intéressante: celle qui nous permet de définir de nouvelles sondes qui vont aller interroger nos applicatifs fournissant un endpoint de métriques!
"Monitorons" nos applicatifs!
Alors, ça tombe bien, il existe une petite application de démo toute prête pour que nous puissions tester:
git clone https://github.com/brancz/prometheus-example-app
Elle est très simple: une requête sur / produit une 200 qui incrémente un compteur, une requête sur /err produit une 404, qui implémente un autre compteur, et ces statistiques sont accessibles sur l'endpoint /metrics de l'"applicatif".
On a aussi un déploiement, et un PodMonitor déjà préconfigurés, que nous allons adapter un peu:
apiVersion: apps/v1
kind: Deployment
metadata:
labels:
app.kubernetes.io/name: prometheus-example-app
name: prometheus-example-app
namespace: apptest
spec:
replicas: 1
selector:
matchLabels:
app.kubernetes.io/name: prometheus-example-app
template:
metadata:
labels:
app.kubernetes.io/name: prometheus-example-app
spec:
containers:
- name: prometheus-example-app
image: quay.io/brancz/prometheus-example-app:v0.3.0
ports:
- name: web
containerPort: 8080
PodMonitor Prometheus
Le PodMonitor va nous servir à indiquer à Prometheus d'aller chercher les informations de monitoring de "l'app" directement sur l'endpoint du pod de notre "applicatif" d'exemple (on préfèrera utiliser un ServiceMonitor, mais on va y revenir par la suite):
apiVersion: monitoring.coreos.com/v1
kind: PodMonitor
metadata:
labels:
app.kubernetes.io/name: prometheus-example-app
release: prom-stack
name: prometheus-example-app
namespace: apptest
spec:
selector:
matchLabels:
app.kubernetes.io/name: prometheus-example-app
podMetricsEndpoints:
- port: web
Alors attention, ici, notez bien le label "release: prom-stack" qui est relatif à mon installation de prometheus dans cet exemple. Pour bien vérifier quel label vous devez ajouter à vos PodMonitors et ServiceMonitors:
lidstah@marfak:~$ kubectl get prometheus -n prom -o yaml | grep -A5 serviceMonitorSelector
serviceMonitorSelector:
matchLabels:
release: prom-stack
shards: 1
tsdb:
outOfOrderTimeWindow: 0s
En effet, par défaut, Prometheus ne "scrapera" (ne récupèrera) les métriques de ses différents moniteurs que s'ils portent bien le bon label release!. Sinon, vous risquez de chercher très, très longtemps (comme votre humble serviteur à l'époque) pourquoi vos métriques ne remontent pas.
On va déployer ces deux manifestes dans notre cluster, et vérifier leur bon fonctionnement.
On crée d'abord notre namespace:
kubectl create ns apptest
kubectl apply -f deployment.yaml -f pod-monitor.yaml
kubectl get podmonitors,deploy,pods -n apptest
# et on vérifie que tout est OK
lidstah@marfak:~$ kubectl get podmonitors,deploy,pods -n apptest
NAME AGE
podmonitor.monitoring.coreos.com/prometheus-example-app 118s
NAME READY UP-TO-DATE AVAILABLE AGE
deployment.apps/prometheus-example-app 1/1 1 1 2m3s
NAME READY STATUS RESTARTS AGE
pod/prometheus-example-app-76c48dff8-l944k 1/1 Running 0 2m3s
On va peut être lui ajouter un petit service pour exposer ça à l'extérieur, non?
Je vous laisse faire? Normalement, ça ne devrait pas poser trop de problème
Bien, maintenant, on va interroger les URLs /, /err et /metrics de notre applicatif d'exemple, afin de vérifier son bon fonctionnement (et de "préchauffer" quelques stats).
Il ne nous reste plus qu'à créer un petit dashboard Grafana pour tester tout ça:

ServiceMonitor Prometheus
Alors, un PodMonitor, c'est sympa, mais il est plus intéressant de créer un ServiceMonitor, qui, comme son nom l'indique, ira interroger nos endpoints non pas directement en "tapant" sur le(s) Pod(s) de notre applicatif, mais sur un port exposé par un Service, qui nous permettra de bénéficier en rime de la répartition de charge offerte par les Services.
NOTE: Ici, notre applicatif d'exemple expose ses métriques sur le même port que l'"application". En production, on voudra séparer trafic applicatif et trafic métriques en les séparant sur des ports différents. En effet, on expose nos métriques en même temps que notre applicatif dans notre exemple, ce qui veut dire que n'importe qui sur "Internet" (ici: le réseau du labo) peut avoir accès à nos métriques. Et on a pas envie du tout de ça
On va se créer un service "prometheus-example-app-metrics-svc" de type ClusterIP cette fois ci. Normalement, ça, vous savez faire . Le port exposé par ce service devra être nommé "metrics" - vous pouvez le nommer différemment mais il faudra en tenir compte dans la création de notre ServiceMonitor. C'est ce service que nous utiliserons pour aller "scrapper" nos métriques.
Notre ServiceMonitor devrait ressembler à ça:
apiVersion: monitoring.coreos.com/v1
kind: ServiceMonitor
metadata:
name: prometheus-example-app-servicemonitor
namespace: apptest
labels:
release: prom-stack
spec:
selector:
matchLabels:
app.kubernetes.io/name: prometheus-example-app
endpoints:
- port: metrics
interval: 15s
path: /metrics
Comme précédemment: faites très attention au label release!
Avant de le déployer, nous allons supprimer notre podmonitor:
kubectl delete podmonitors -n apptest prometheus-example-app
De même, il faudra un peu de temps à Grafana et Prometheus pour se rendre compte de la modification PodMonitor vers ServiceMonitor, ce qui au bout de quelques minutes devrait donner ceci:

Notez le "saut" entre le PodMonitor et le ServiceMonitor.
TP: Ajoutez un endpoint de métriques à votre applicatif
But
Ajoutez un endpoint /metrics à votre applicatif développé/déployé dans kube pendant le module de Benjamin.
Le but est d'exposer plusieurs métriques de votre applicatif (nombre de requêtes par seconde, temps d'exécution d'une fonction de votre code, nombre d'utilisateurs, taux de création d'utilisateurs par unité de temps, etc) pour pouvoir les "donner à manger" à Prometheus et obtenir de la visibilité sur ces métriques via un dashboard grafana.
Contrairement à l'exemple précédent, utilisez un port différent pour exposer les métriques de votre applicatif. N'oubliez pas qu'un Service peut exposer plusieurs ports. Idéalement, utilisez un Ingress pour exposer votre applicatif (donc, en renvoyant vers le port "web" de votre Service, le port "metrics" lui n'étant exposé qu'à l'intérieur du cluster).
Rendu
Oral lors du dernier cours du module, où vous devrez:
- Montrer le dashboard Grafana affichant les différentes métriques exposées par votre applicatif
- Expliquer les différentes métriques remontées
- Expliquer la configuration de chaque panel de votre Dashboard
- Expliquer la configuration de votre ServiceMonitor
- Expliquer la configuration de votre Service
- Petite démo de votre applicatif?