Introduction
Le multi-tenant Kubernetes permet à plusieurs équipes ou applications de partager un même cluster, favorisant une utilisation efficace des ressources et des économies de coûts. Cependant, le partage d'infrastructure comporte des risques de performance : un voisin bruyant peut consommer une part disproportionnée de CPU ou de mémoire, le trafic réseau peut interférer entre les locataires et des quotas mal configurés peuvent ralentir des charges de travail légitimes ou provoquer l'éviction de pods. Il en résulte une latence imprévisible des applications, une diminution du débit et des utilisateurs mécontents.
Ce guide propose une approche pratique, étape par étape, pour optimiser les performances d'un cluster Kubernetes multi-tenant. Nous couvrons l'inventaire de l'environnement, la configuration sécurisée des contrôles de ressources, la vérification et le diagnostic, la reprise après échec et une liste de contrôle opérationnelle pour la maintenance à long terme. Tout au long du guide, nous utilisons des commandes concrètes, des exemples YAML et des sorties attendues afin que vous puissiez appliquer ces techniques directement à votre propre cluster.
Un réglage efficace des performances commence par la compréhension de votre état actuel et la définition d'objectifs clairs et mesurables. Plutôt que d'apporter des changements radicaux, adoptez une approche pilote : sélectionnez un espace de noms de locataire, appliquez les modifications, mesurez l'impact, puis étendez-les aux autres. Cela minimise les risques et offre des gains rapides. À la fin de ce guide, vous disposerez d'un processus reproductible pour garantir une allocation équitable des ressources, contrôler les voisins bruyants et maintenir les performances du cluster à mesure de sa croissance.
Inventaire de la version et de l'environnement
Avant toute modification, documentez la version de Kubernetes, la topologie des nœuds et les objets de gestion des ressources existants. Cet inventaire sert à deux fins : il garantit la compatibilité avec les fonctionnalités que vous prévoyez d'utiliser et il fournit un point de restauration en cas de problème.
Commencez par vérifier la version de Kubernetes :
kubectl version --short
Sortie attendue (tronquée pour plus de concision) :
Client Version: v1.28.2
Server Version: v1.28.2
Ensuite, listez les nœuds et leur capacité :
kubectl get nodes -o custom-columns=NAME:.metadata.name,CPU:.status.capacity.cpu,MEMORY:.status.capacity.memory
Exemple de sortie :
NAME CPU MEMORY
node-1 8 32Gi
node-2 8 32Gi
node-3 8 32Gi
Inspectez les espaces de noms existants :
kubectl get namespaces
Vérifiez les quotas de ressources et les plages de limites existants :
kubectl get resourcequota --all-namespaces
kubectl get limitrange --all-namespaces
Documentez les politiques réseau :
kubectl get networkpolicies --all-namespaces
Vérifiez la disponibilité du serveur de métriques :
kubectl top nodes
kubectl top pods --all-namespaces
Si le serveur de métriques n'est pas installé, vous verrez une erreur comme error: Metrics API not available. Installez-le sur un cluster de test via :
kubectl apply -f https://github.com/kubernetes-sigs/metrics-server/releases/latest/download/components.yaml
Attendez une minute, puis vérifiez :
kubectl get deployment metrics-server -n kube-system
Les prérequis pour le réglage incluent :
- Accès administrateur au cluster.
- Serveur de métriques ou observabilité équivalente (Prometheus, Datadog, etc.).
- Un environnement de test qui reflète la production.
- Des sauvegardes des configurations actuelles. Exportez les objets pertinents avec
kubectl get -o yamlet stockez-les dans un système de contrôle de version. Par exemple :
kubectl get resourcequota --all-namespaces -o yaml > resourcequotas-backup.yaml
kubectl get limitrange --all-namespaces -o yaml > limitranges-backup.yaml
Chemin de configuration sécurisé
Mettez en œuvre les modifications de réglage de manière incrémentale, en commençant par les quotas de ressources et les plages de limites pour empêcher l'accaparement des ressources. Ce sont des contrôles fondamentaux qui fixent des limites pour chaque locataire.
Étape 1 : Définir un quota de ressources par espace de noms de locataire
Un ResourceQuota plafonne le total des demandes et limites de ressources pour un espace de noms. Cela empêche un seul locataire de consommer toutes les ressources du cluster. Pour le locataire team-a, créez un fichier team-a-quota.yaml :
apiVersion: v1
kind: ResourceQuota
metadata:
name: team-a-quota
namespace: team-a
spec:
hard:
requests.cpu: "10"
requests.memory: 20Gi
limits.cpu: "20"
limits.memory: 40Gi
pods: "50"
Appliquez-le :
kubectl apply -f team-a-quota.yaml
Vérifiez :
kubectl describe resourcequota team-a-quota -n team-a
Extrait de sortie attendue :
Name: team-a-quota
Namespace: team-a
Resource Used Hard
-------- ---- ----
limits.cpu 0 20
limits.memory 0 40Gi
pods 0 50
requests.cpu 0 10
requests.memory 0 20Gi
Étape 2 : Définir une plage de limites pour imposer des valeurs par défaut et des bornes
LimitRange définit des demandes et limites par défaut pour les conteneurs qui ne les spécifient pas, et impose des valeurs minimales et maximales. Cela empêche un locataire de créer un pod sans limites (ce qui pourrait consommer des ressources illimitées) ou avec des demandes excessives (ce qui pourrait affamer les autres). Créez team-a-limits.yaml :
apiVersion: v1
kind: LimitRange
metadata:
name: team-a-limits
namespace: team-a
spec:
limits:
- default:
cpu: 500m
memory: 512Mi
defaultRequest:
cpu: 200m
memory: 256Mi
max:
cpu: 2
memory: 4Gi
min:
cpu: 100m
memory: 128Mi
type: Container
Appliquez et vérifiez :
kubectl apply -f team-a-limits.yaml
kubectl describe limitrange team-a-limits -n team-a
Extrait de sortie attendue :
Name: team-a-limits
Namespace: team-a
Type Resource Min Max Default Request Default Limit Max Limit/Request Ratio
---- -------- --- --- --------------- ------------- -----------------------
Container cpu 100m 2 200m 500m -
Container memory 128Mi 4Gi 256Mi 512Mi -
Étape 3 : Isoler le trafic des locataires avec des politiques réseau
Les politiques réseau contrôlent la communication pod à pod et réduisent la surcharge réseau entre locataires en empêchant le trafic inutile. Commencez par une politique de refus par défaut pour l'espace de noms du locataire team-a :
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: default-deny
namespace: team-a
spec:
podSelector: {}
policyTypes:
- Ingress
- Egress
Appliquez :
kubectl apply -f default-deny.yaml
Autorisez ensuite le trafic au sein du même espace de noms (les pods peuvent communiquer entre eux) :
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-same-namespace
namespace: team-a
spec:
podSelector: {}
ingress:
- from:
- podSelector: {}
egress:
- to:
- podSelector: {}
Appliquez :
kubectl apply -f allow-same-namespace.yaml
Testez la connectivité en tentant d'accéder à un service dans un autre espace de noms de locataire (par exemple, team-b) depuis un pod dans team-a. Cela devrait échouer. Au sein de team-a, cela devrait réussir.
Étape 4 : Piloter et mesurer l'impact
Sélectionnez un espace de noms de locataire comme pilote. Appliquez les configurations ci-dessus uniquement à cet espace de noms. Avant l'application, capturez les métriques de référence :
- Utilisation CPU et mémoire par pod :
kubectl top pods -n team-a - Latence des services clés : utilisez un outil de test de charge comme
heyouwrksur un point de terminaison. - Temps de démarrage des pods et taux d'erreur.
Après l'application, exécutez les mêmes mesures. Comparez les résultats pour quantifier l'impact de vos modifications. Par exemple, vous pourriez constater une diminution du throttling CPU et une latence plus stable.
Si le pilote est concluant, répliquez la configuration dans d'autres espaces de noms avec des valeurs ajustées en fonction des besoins de chaque locataire.
Vérification et diagnostic
Après avoir appliqué les modifications, vérifiez leur efficacité et détectez toute régression de performance. Une surveillance continue est essentielle.
Vérifier la consommation des quotas
Exécutez :
kubectl get resourcequota team-a-quota -n team-a --output=yaml
Regardez la section status pour voir l'utilisation actuelle par rapport aux limites strictes :
status:
hard:
limits.cpu: "20"
limits.memory: 40Gi
pods: "50"
requests.cpu: "10"
requests.memory: 20Gi
used:
limits.cpu: "5"
limits.memory: 10Gi
pods: "12"
requests.cpu: "3"
requests.memory: 6Gi
Surveiller l'utilisation des ressources
Obtenez l'utilisation actuelle du CPU et de la mémoire pour les pods dans l'espace de noms :
kubectl top pods -n team-a
Exemple de sortie :
NAME CPU(cores) MEMORY(bytes)
my-app-abc 300m 256Mi
my-app-def 150m 128Mi
Si l'utilisation est proche des limites, envisagez d'ajuster ou de faire évoluer.
Simuler une charge et mesurer la latence
Utilisez un outil de test de charge pour simuler du trafic vers un service de locataire. Pour un service HTTP, créez un pod générateur de charge :
kubectl run load-generator --image=busybox --restart=Never --rm -it -- sh -c "while true; do wget -q -O- http://my-service.team-a.svc.cluster.local; done"
Mieux : utilisez un outil dédié comme hey dans un pod. Par exemple, déployez un pod temporaire avec hey :
kubectl run hey-load --image=rakyll/hey --restart=Never --rm -it -- -n 10000 -c 50 http://my-service.team-a.svc.cluster.local
Cela envoie 10 000 requêtes avec 50 connexions simultanées. Observez la sortie pour les percentiles de latence et le taux d'erreur.
Vérifier le throttling CPU et OOMKilled
Si les limites CPU sont trop basses, les pods seront limités, ce qui augmentera la latence. Inspectez le statut et les événements du pod :
kubectl describe pod <pod-name> -n team-a
Recherchez des événements comme :
Warning Throttling 5m ago kubelet Container is being throttled
Pour les problèmes de mémoire, recherchez OOMKilled dans le statut du pod :
kubectl get pods -n team-a -o wide
NAME READY STATUS RESTARTS AGE
my-app-xyz 0/1 OOMKilled 1 2m
Si vous utilisez Prometheus, interrogez des métriques comme container_cpu_cfs_throttled_seconds_total pour voir le throttling au fil du temps.
Diagnostiquer la latence réseau
Pour tester la connectivité et la latence réseau entre les pods, utilisez kubectl exec et des outils standard :
kubectl exec -it <pod-in-team-a> -n team-a -- ping <pod-ip-in-team-b>
Ou utilisez curl pour mesurer la latence HTTP :
kubectl exec -it <pod-in-team-a> -n team-a -- curl -o /dev/null -s -w '%{time_total}\n' http://<service-in-team-b>.team-b.svc.cluster.local
Cela devrait échouer si les politiques réseau isolent correctement les locataires. Au sein du même espace de noms, cela devrait réussir avec une faible latence.
Vérifier la dédicace des nœuds (si utilisée)
Si vous avez utilisé des sélecteurs de nœuds ou des teintes/tolérances pour dédier des nœuds à des locataires spécifiques, vérifiez la planification :
kubectl get pods -n team-a -o wide
Vérifiez la colonne NODE pour confirmer que les pods sont sur les nœuds prévus.
Résultats attendus
Après le réglage, vous devriez observer :
- L'utilisation des ressources reste dans les quotas.
- Aucune éviction de pods en raison du quota ou de la pression mémoire.
- La latence reste dans des seuils acceptables (par exemple, p95 < 200 ms pour les API typiques).
- Aucune interférence entre locataires : un pic de charge chez un locataire ne dégrade pas les performances chez un autre.
Si ces conditions ne sont pas remplies, revoyez vos quotas et limites ou approfondissez l'enquête.
Modes de défaillance et récupération
Des quotas ou limites mal configurés peuvent provoquer des échecs de création de pods, des évictions ou du throttling. Soyez prêt à annuler rapidement les modifications pour minimiser l'impact.
Défaillance courante : Pods bloqués en attente en raison d'un quota dépassé
Symptôme :
kubectl get pods -n team-a
NAME READY STATUS RESTARTS AGE
my-app-xyz 0/1 Pending 0 10s
Vérifiez les événements :
kubectl describe pod my-app-xyz -n team-a
La sortie peut montrer :
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Warning FailedScheduling 10s default-scheduler 0/3 nodes are available: 3 Insufficient cpu, 3 Insufficient memory.
Ou, si le quota est en cause :
Warning FailedCreate 10s quota-controller Error creating: pods "my-app-xyz" is forbidden: exceeded quota: team-a-quota, requested: requests.cpu=2, used: requests.cpu=9, limited: requests.cpu=10
Récupération :
- Augmentez le quota dans
team-a-quota.yaml(par exemple, passezrequests.cpuà 15) et réappliquez. - Ou supprimez temporairement le quota pour permettre au pod de se planifier, puis réappliquez après ajustement :
kubectl delete resourcequota team-a-quota -n team-a
kubectl apply -f team-a-quota.yaml # après avoir mis à jour les valeurs
Défaillance courante : Pods OOMKilled
Si un pod est tué en raison d'une limite de mémoire, augmentez la limite de mémoire ou enquêtez sur les fuites de mémoire de l'application.
Symptôme :
kubectl get pods -n team-a
NAME READY STATUS RESTARTS AGE
my-app-abc 0/1 OOMKilled 1 5m
Vérifiez les journaux du conteneur précédent :
kubectl logs my-app-abc -n team-a --previous
Récupération :
- Modifiez le déploiement pour augmenter la limite de mémoire (par exemple, de 512 Mi à 1 Gi) et réappliquez.
- Ou, si l'application a une fuite de mémoire, corrigez la fuite plutôt que de simplement augmenter les limites.
Défaillance courante : Throttling CPU
Si les pods sont limités, augmentez les limites CPU si le nœud a de la capacité disponible.
Vérifiez les métriques de throttling dans Prometheus ou décrivez le pod pour les événements.
Récupération :
- Augmentez la limite CPU dans la spécification du pod, ou ajustez le
maxde la plage de limites pour l'espace de noms. - Si le nœud est entièrement utilisé, envisagez d'ajouter des nœuds ou de déplacer des charges de travail.
Défaillance courante : La politique réseau bloque tout le trafic
Une politique réseau mal configurée peut bloquer le trafic légitime, provoquant des interruptions de service.
Symptôme : Les pods ne peuvent pas se joindre entre eux ou aux services externes ; curl expire.
Récupération :
- Supprimez la politique réseau :
kubectl delete networkpolicy default-deny -n team-a
- Réappliquez une politique corrigée.
Conservez toujours des sauvegardes. Utilisez kubectl get -o yaml avant de modifier pour enregistrer les configurations d'origine. Par exemple :
kubectl get networkpolicy -n team-a -o yaml > networkpolicies-backup.yaml
Testez les modifications dans un cluster de préproduction d'abord. En cas de problème, revenez immédiatement en arrière à l'aide de vos sauvegardes.
Liste de contrôle opérationnelle
Maintenez les performances dans le temps avec ces étapes reproductibles. Attribuez un responsable unique pour chaque élément majeur et révisez à une fréquence définie.
Revue mensuelle (Responsable : Responsable de l'ingénierie de plateforme)
- Examinez les quotas de ressources par rapport à l'utilisation réelle. Ajustez si l'utilisation est constamment proche des limites.
kubectl describe resourcequota --all-namespaces
- Vérifiez régulièrement les pods en CrashLoopBackOff ou en attente.
kubectl get pods --all-namespaces | grep -E 'CrashLoop|Pending'
Revue trimestrielle (Responsable : Responsable SRE)
- Surveillez l'utilisation des nœuds et envisagez de faire évoluer si elle est soutenue à plus de 80 %.
kubectl top nodes
- Auditez les politiques réseau pour détecter les règles trop permissives. Recherchez toute politique avec
podSelector: {}qui autorise tout le trafic entrant/sortant ; resserrez si nécessaire. - Revoyez les valeurs par défaut et les valeurs min/max des plages de limites pour vous assurer qu'elles correspondent aux besoins actuels des applications.
- Validez l'isolation multi-tenant : tentez d'accéder aux services d'autres locataires depuis un pod (cela devrait être refusé). Cela peut être automatisé avec un script simple.
Tests de charge semestriels (Responsable : Ingénieur performance)
- Exécutez des tests de charge sur des services représentatifs pour vérifier que la latence et le débit respectent toujours les SLO. Utilisez un outil comme
heyouwrket enregistrez la latence p95/p99. - Comparez avec les métriques de référence. Si une dégradation est observée, enquêtez sur les changements récents et ajustez les contrôles de ressources.
Documentation et gestion des changements (Responsable : Rédacteur technique ou responsable de plateforme)
- Documentez tout changement de réglage et sa justification dans un runbook partagé.
- Tenez la liste de contrôle opérationnelle à jour.
- Utilisez la liste de contrôle lors de l'intégration de nouveaux locataires et après les mises à niveau du cluster.
Pour chaque élément de la liste de contrôle, définissez une métrique spécifique et une cible. Par exemple :
- Métrique : taux de throttling CPU. Cible : < 1 % du temps CPU limité par pod et par mois.
- Métrique : temps de démarrage des pods. Cible : < 30 secondes pour 95 % des pods.
- Métrique : tentatives d'accès réseau entre locataires. Cible : 0 connexion réussie.
Pièges courants et comment les éviter
1. Quotas trop stricts
Ce qui se passe : Des quotas trop bas empêchent les pods légitimes de se planifier, obligeant les développeurs à demander constamment des augmentations de quota, ou ils peuvent contourner en utilisant plusieurs espaces de noms.
Pourquoi : Les quotas initiaux sont souvent basés sur des suppositions sans données d'utilisation historiques.
Comment éviter : Commencez avec des quotas généreux, surveillez l'utilisation réelle pendant quelques semaines, puis resserrez progressivement. Utilisez kubectl top et Prometheus pour suivre l'utilisation.
Récupération : Augmentez temporairement les quotas pour débloquer, puis réévaluez.
2. Ignorer les plages de limites
Ce qui se passe : Sans plages de limites, les locataires peuvent créer des pods sans limites de ressources, ce qui peut consommer toutes les ressources du nœud et affamer les autres. Ou ils peuvent définir des demandes très élevées, réduisant la capacité planifiable.
Pourquoi : Les plages de limites sont souvent négligées car elles ne sont pas appliquées par défaut.
Comment éviter : Définissez toujours une plage de limites pour chaque espace de noms de locataire avec des valeurs par défaut et des max/min raisonnables. Utilisez les exemples de ce guide comme point de départ.
Récupération : Appliquez une plage de limites, puis auditez les pods existants ; ils ne seront pas modifiés rétroactivement, vous devrez peut-être les recréer.
3. Politiques réseau trop complexes
Ce qui se passe : Un maillage complexe de politiques réseau peut devenir ingérable, entraînant une isolation accidentelle ou des failles de sécurité.
Pourquoi : Les équipes peuvent créer de nombreuses politiques fines sans documenter l'intention.
Comment éviter : Commencez par des politiques simples : refus par défaut, puis autorisez un trafic spécifique. Utilisez des étiquettes pour regrouper logiquement les pods. Documentez l'objectif de chaque politique.
Récupération : Simplifiez en supprimant toutes les politiques et en réappliquant un ensemble minimal, puis testez la connectivité.
4. Ne pas surveiller le throttling
Ce qui se passe : Le throttling CPU peut dégrader silencieusement les performances, augmentant la latence sans erreurs évidentes.
Pourquoi : Les métriques de throttling ne sont pas toujours visibles dans les tableaux de bord de base ; les équipes se concentrent plutôt sur l'utilisation de la mémoire et du CPU.
Comment éviter : Surveillez container_cpu_cfs_throttled_seconds_total dans Prometheus et définissez des alertes. Utilisez la mise à l'échelle automatique horizontale des pods (HPA) pour évoluer sous charge.
Récupération : Augmentez les limites CPU ou ajustez les seuils HPA.
5. Ne pas planifier pour les voisins bruyants
Ce qui se passe : Un seul locataire exécutant un travail par lots ou une charge de travail gourmande en mémoire peut dégrader les performances de tous les locataires sur le même nœud.
Pourquoi : Les demandes et limites de ressources seules ne garantissent pas l'isolation ; elles contraignent uniquement les pods individuels, pas le comportement agrégé.
Comment éviter : Utilisez l'affinité de nœuds/teintes pour dédier des nœuds aux locataires à haut risque, ou utilisez la priorité et la préemption des pods pour protéger les charges de travail critiques. Envisagez d'utiliser LimitRange pour définir un maxLimitRequestRatio afin d'éviter le sur-engagement.
Récupération : Identifiez le voisin bruyant à l'aide de kubectl top pods --all-namespaces et cordonnez/drainez le nœud ou ajustez les quotas.
Conclusion
Le réglage des performances du multi-tenant Kubernetes est un processus continu de mesure, d'ajustement et de vérification. En établissant une base de référence, en mettant en œuvre des contrôles de ressources et en surveillant en continu, vous pouvez obtenir des performances prévisibles pour tous les locataires. Les étapes de ce guide offrent un chemin sûr et pratique pour optimiser votre cluster.
Commencez par un pilote étroit : choisissez un locataire, appliquez les quotas de ressources et les plages de limites, isolez le trafic réseau et mesurez l'impact. Validez les métriques de succès telles que la latence, le throttling et les taux d'éviction. Une fois prouvé, étendez aux autres locataires avec des configurations ajustées.
Gardez la liste de contrôle opérationnelle à portée de main pour maintenir les performances à mesure que votre cluster évolue. Attribuez des responsables clairs et des fréquences de revue pour garantir la responsabilité. Évitez les pièges courants en commençant de manière prudente, en surveillant activement et en simplifiant les politiques réseau.
Avec une planification minutieuse et des changements incrémentaux, vous pouvez profiter des économies de coûts du multi-tenant sans sacrifier les performances. Utilisez les exemples et les commandes de ce guide comme référence et adaptez-les à votre environnement spécifique. Vos locataires vous remercieront.