## Introduction Lorsqu'un cluster Kubernetes manque de ressources, le planificateur doit décider quels pods expulser pour faire de la place aux charges de travail de priorité supérieure. La priorité et la préemption des pods sont le mécanisme qui régit ces décisions, mais il opère souvent en silence jusqu'à ce qu'un incident de production survienne. Sans surveillance et alertes appropriées, vous risquez de découvrir que des pods critiques ont été expulsés seulement après que les utilisateurs signalent une interruption. Ce guide propose une approche pratique pour surveiller la priorité et la préemption des pods Kubernetes. Il est conçu pour les développeurs, les consultants DevOps et les équipes techniques de startups qui doivent passer de l'observation d'un problème potentiel à la vérification d'une solution. Nous relions les alertes, les métriques, les tableaux de bord et la réponse aux incidents à des commandes concrètes, des sorties attendues, des signaux d'échec et des décisions de récupération. Notre objectif est la sécurité opérationnelle : observer avant de modifier, limiter le rayon d'impact, utiliser des espaces réservés au lieu de secrets, vérifier les résultats et documenter les chemins de récupération avant qu'un incident ne force une décision. Chaque section comprend un inventaire de version et d'environnement, un chemin de configuration sûr, la vérification et les diagnostics, les modes de défaillance et la récupération, ainsi qu'une liste de contrôle opérationnelle. ## Inventaire de version et d'environnement Avant de plonger dans la surveillance, établissez un inventaire clair de votre environnement. Cela inclut la version de Kubernetes, la configuration du planificateur, les PriorityClasses existantes et la façon dont les pods sont assignés aux priorités. ### Identifier la version de Kubernetes et le planificateur Tout d'abord, vérifiez la version de votre cluster : ```bash kubectl version --short ``` Sortie attendue (exemple) : ``` Client Version: v1.27.3 Kustomize Version: v5.0.1 Server Version: v1.27.3 ``` La priorité et la préemption des pods sont activées par défaut dans Kubernetes 1.14 et versions ultérieures. Si vous utilisez une version plus ancienne, vous devez activer la porte de fonctionnalité `PodPriority`. Pour les versions prises en charge (1.14+), aucune configuration supplémentaire n'est nécessaire. Vérifiez que le pod kube-scheduler est en cours d'exécution : ```bash kubectl get pods -n kube-system -l component=kube-scheduler ``` Sortie attendue : ``` NAME READY STATUS RESTARTS AGE kube-scheduler-... 1/1 Running 0 5d ``` ### Lister les PriorityClasses existantes Les PriorityClasses sont des ressources à l'échelle du cluster qui définissent les valeurs de priorité et les politiques de préemption. Listez-les : ```bash kubectl get priorityclasses ``` Exemple de sortie : ``` NAME VALUE GLOBAL-DEFAULT AGE system-cluster-critical 2000000000 false 30d system-node-critical 2000001000 false 30d high-priority 1000000 false 10d default 0 true 30d ``` Notez la colonne `GLOBAL-DEFAULT` : une seule PriorityClass peut être la valeur par défaut globale. Les pods sans `priorityClassName` utilisent la valeur par défaut globale (généralement la valeur 0). Si aucune valeur par défaut globale n'est définie, les pods sans classe de priorité ont une priorité de 0 mais aucune politique de préemption. Inspectez une PriorityClass spécifique : ```bash kubectl describe priorityclass high-priority ``` Sortie attendue : ``` Name: high-priority Value: 1000000 GlobalDefault: false PreemptionPolicy: PreemptLowerPriority Description: For critical applications Annotations: Events: ``` Le champ `PreemptionPolicy` peut être `PreemptLowerPriority` (par défaut) ou `Never`. Si `Never`, les pods avec cette classe ne préempteront pas d'autres pods, mais ils peuvent toujours être préemptés par des pods de priorité supérieure. ### Vérifier la priorité d'un pod Vérifiez la priorité d'un pod en cours d'exécution : ```bash kubectl get pod -o jsonpath='{.spec.priority}{"\n"}' ``` Exemple : ```bash kubectl get pod nginx-deployment-7d5b6d4c9-abcde -o jsonpath='{.spec.priority}{"\n"}' ``` Sortie : ``` 1000000 ``` Si aucune priorité n'est définie, elle est par défaut à 0 (ou la valeur par défaut globale). Vous pouvez également voir le nom de la classe de priorité : ```bash kubectl get pod -o jsonpath='{.spec.priorityClassName}{"\n"}' ``` ### Vérifier la configuration du planificateur pour la préemption Le kube-scheduler possède des drapeaux qui affectent le comportement de préemption. Affichez la commande du pod du planificateur : ```bash kubectl get pod -n kube-system -l component=kube-scheduler -o jsonpath='{.items[0].spec.containers[0].command}' | jq . ``` Recherchez des drapeaux tels que `--preemption=true` (par défaut) ou `--preemption=false`. Si la préemption est désactivée, aucun pod ne sera préempté quelle que soit la priorité. ### Vérifications pratiques en lecture seule Avant d'effectuer toute modification, exécutez ces commandes en lecture seule pour capturer l'état actuel : ```bash kubectl get pods -A -o wide | grep -v Running ``` Cela montre tous les pods qui ne sont pas en état Running, ce qui peut indiquer des problèmes de préemption ou d'éviction. ```bash kubectl get events -A --sort-by='.lastTimestamp' | tail -50 ``` Recherchez des événements avec des raisons telles que `Preempted`, `FailedScheduling` ou `Evicted`. C'est votre premier signal d'activité de préemption. ## Chemin de configuration sûr La configuration correcte de la priorité et de la préemption implique de créer des PriorityClasses, de les assigner aux pods et de tester le comportement de manière contrôlée. Testez toujours les modifications dans un namespace ou un cluster hors production d'abord. ### 1. Créer une PriorityClass Définissez une PriorityClass avec un nom et une valeur clairs. Des valeurs plus élevées signifient une priorité plus élevée. Les valeurs doivent être inférieures ou égales à 1 000 000 000 (1 milliard). Exemple `priority-class.yaml` : ```yaml apiVersion: scheduling.k8s.io/v1 kind: PriorityClass metadata: name: high-priority value: 1000000 preemptionPolicy: PreemptLowerPriority globalDefault: false description: "For critical applications" ``` Appliquez-le : ```bash kubectl apply -f priority-class.yaml ``` Vérifiez : ```bash kubectl get priorityclass high-priority ``` ### 2. Assigner une PriorityClass à une charge de travail Modifiez un Deployment pour utiliser la PriorityClass. Exemple `nginx-deployment.yaml` : ```yaml apiVersion: apps/v1 kind: Deployment metadata: name: nginx-deployment spec: replicas: 2 selector: matchLabels: app: nginx template: metadata: labels: app: nginx spec: priorityClassName: high-priority containers: - name: nginx image: nginx:1.21 resources: requests: cpu: "100m" memory: "128Mi" ``` Appliquez et vérifiez la priorité : ```bash kubectl apply -f nginx-deployment.yaml kubectl get pods -l app=nginx -o wide ``` Vérifiez la priorité des nouveaux pods : ```bash kubectl get pods -l app=nginx -o jsonpath='{.items[0].spec.priority}{"\n"}' ``` Sortie attendue : `1000000`. ### 3. Tester le comportement de préemption en toute sécurité Pour tester la préemption sans affecter la production, créez un namespace dédié et simulez une pression sur les ressources. Utilisez le drapeau `--dry-run=server` pour valider les manifestes avant de les appliquer. Tout d'abord, créez un namespace de test : ```bash kubectl create namespace preemption-test ``` Déployez un pod de faible priorité qui demande une ressource spécifique, par exemple, tout le CPU d'un nœud. Assurez-vous que votre cluster possède au moins un nœud avec une capacité limitée. Vous pouvez utiliser un pod avec une grande demande de CPU pour remplir le nœud : ```yaml apiVersion: v1 kind: Pod metadata: name: low-priority-pod namespace: preemption-test spec: priorityClassName: default # ou laisser vide pour la priorité 0 containers: - name: stress image: polinux/stress resources: requests: cpu: "1" memory: "1Gi" command: ["stress"] args: ["--cpu", "1"] ``` Appliquez-le et attendez qu'il soit en cours d'exécution. Ensuite, déployez un pod de haute priorité qui nécessite les mêmes ressources sans politique de préemption définie sur `Never` : ```yaml apiVersion: v1 kind: Pod metadata: name: high-priority-pod namespace: preemption-test spec: priorityClassName: high-priority containers: - name: nginx image: nginx:1.21 resources: requests: cpu: "1" memory: "1Gi" ``` Appliquez le pod de haute priorité et surveillez les événements : ```bash kubectl apply -f high-priority-pod.yaml kubectl get events -n preemption-test --watch ``` Événements attendus : ``` 0s Normal Scheduled pod/high-priority-pod Successfully assigned preemption-test/high-priority-pod to node-1 0s Normal Preempted pod/low-priority-pod Preempted by preemption-test/high-priority-pod on node node-1 ``` Le pod de faible priorité sera expulsé et replanifié ailleurs si des ressources sont disponibles. Cela confirme que la préemption fonctionne. ### 4. Retour en arrière et nettoyage Après le test, supprimez le namespace de test pour nettoyer : ```bash kubectl delete namespace preemption-test ``` Si un changement de configuration cause des problèmes, vous pouvez revenir en appliquant le manifeste précédent ou en supprimant la PriorityClass après vous être assuré qu'aucun pod ne l'utilise. ## Vérification et diagnostics Une fois la priorité et la préemption configurées, vous devez vérifier qu'elles fonctionnent comme prévu et diagnostiquer tout problème. Cette section fournit des commandes et des métriques pour surveiller le comportement. ### Vérifier les événements de préemption Utilisez `kubectl get events` filtré par raison : ```bash kubectl get events -A --field-selector reason=Preempted ``` Exemple de sortie : ``` NAMESPACE LAST SEEN TYPE REASON OBJECT MESSAGE preemption-test 3m Normal Preempted pod/low-priority-pod Preempted by preemption-test/high-priority-pod on node node-1 ``` Si vous ne voyez aucun événement, la préemption peut ne pas se produire. Vérifiez que le pod de haute priorité a une valeur de priorité supérieure à celle du pod de faible priorité et que la politique de préemption du pod de haute priorité n'est pas `Never`. ### Vérifier les journaux du planificateur Les journaux du planificateur peuvent révéler les décisions de préemption. Accédez aux journaux du pod du planificateur : ```bash kubectl logs -n kube-system -l component=kube-scheduler --tail=100 ``` Recherchez des lignes contenant "preemption" ou "Preempting" : ```bash kubectl logs -n kube-system -l component=kube-scheduler --tail=100 | grep -i preempt ``` Exemple de ligne de journal : ``` I0615 10:23:45.123456 1 preemption.go:276] Preempting 1 pod(s) to make room for high-priority-pod ``` Si les journaux ne sont pas disponibles en raison de la rotation des journaux, envisagez d'activer les métriques du planificateur (ci-dessous). ### Surveiller les métriques du planificateur Activez les métriques du planificateur en ajoutant `--authorization-always-allow-paths=/metrics` et `--bind-address=0.0.0.0` à la commande kube-scheduler. Ensuite, interrogez l'endpoint des métriques : ```bash kubectl get --raw /api/v1/namespaces/kube-system/pods/kube-scheduler-:10251/proxy/metrics | grep scheduler_preemption ``` Remplacez `` par le nœud de votre pod de planificateur. Métriques attendues (exemple) : ``` scheduler_preemption_attempts_total{result="success"} 2 scheduler_preemption_victims_total 3 ``` Métriques clés à surveiller : - `scheduler_preemption_attempts_total` : Nombre de tentatives de préemption, étiqueté par résultat (success, failure). - `scheduler_preemption_victims_total` : Nombre de pods préemptés. - `scheduler_schedule_attempts_total` : Nombre total de tentatives de planification. Ces métriques vous aident à construire des tableaux de bord et des alertes (voir les sections suivantes). ### Diagnostiquer les échecs de planification Si un pod de haute priorité est en attente, décrivez-le : ```bash kubectl describe pod -n ``` Recherchez des événements tels que : ``` Events: Type Reason Age From Message ---- ------ ---- ---- ------- Warning FailedScheduling 2m default-scheduler 0/3 nodes are available: 3 Insufficient cpu, 3 Insufficient memory. ``` Cela indique que même avec la préemption, il y a des ressources insuffisantes. Vérifiez si la préemption a été tentée en recherchant des événements `Preempted` ou les journaux du planificateur. Autre commande utile : ```bash kubectl get events -n --sort-by='.lastTimestamp' ``` ## Modes de défaillance et récupération La préemption peut entraîner divers modes de défaillance. Comprendre ces modes et avoir un plan de récupération est crucial pour maintenir la disponibilité des applications. ### Modes de défaillance courants 1. **Préemption non intentionnelle** : Les pods de faible priorité sont préemptés plus souvent que prévu, provoquant une interruption de service. Cela arrive souvent lorsque les priorités sont mal configurées (par exemple, un job batch obtient une priorité élevée). - **Récupération** : Examinez les affectations de PriorityClass et ajustez les valeurs. Utilisez `kubectl get pods -A -o jsonpath='{range .items[*]}{.metadata.name}{"\t"}{.spec.priorityClassName}{"\n"}{end}'` pour auditer. 2. **La préemption ne se déclenche pas** : Les pods de haute priorité restent en attente parce que la préemption est désactivée ou que les classes de priorité sont incorrectes. - **Récupération** : Vérifiez les drapeaux du planificateur (`--preemption=true`), vérifiez les valeurs de PriorityClass et assurez-vous que la politique de préemption n'est pas `Never`. 3. **Boucle d'éviction de pod** : Un pod est préempté et replanifié sur un autre nœud, pour être à nouveau préempté, provoquant une boucle de crash. - **Récupération** : Ajustez les demandes de ressources pour correspondre aux nœuds disponibles, ou augmentez la capacité du cluster. Utilisez les budgets de perturbation de pods pour limiter les perturbations volontaires des pods critiques. 4. **Inflation des priorités** : Au fil du temps, de nombreux pods obtiennent des valeurs de priorité élevées, réduisant l'efficacité de la préemption. - **Récupération** : Établissez un document de hiérarchie des priorités et appliquez-le par politique (par exemple, OPA ou Kyverno). ### Récupération étape par étape pour un pod critique préempté Si un pod critique a été préempté involontairement, suivez ces étapes : 1. Identifiez le pod préempté : ```bash kubectl get events -A --field-selector reason=Preempted | grep ``` 2. Vérifiez son état actuel : ```bash kubectl get pod -n -o wide ``` 3. S'il est en attente ou en boucle de crash, décrivez-le : ```bash kubectl describe pod -n ``` 4. Augmentez sa priorité en attribuant une PriorityClass supérieure (si justifié). Créez ou mettez à jour une PriorityClass, puis patchez le propriétaire du pod (par exemple, un Deployment) : ```bash kubectl patch deployment -n -p '{"spec":{"template":{"spec":{"priorityClassName":"system-cluster-critical"}}}}' ``` 5. Alternativement, réduisez les charges de travail de faible priorité pour libérer des ressources. 6. Surveillez le pod pour vous assurer qu'il devient Ready : ```bash kubectl rollout status deployment/ -n ``` ### Prévenir les incidents futurs - Définissez des PodDisruptionBudgets pour les charges de travail critiques : ```yaml apiVersion: policy/v1 kind: PodDisruptionBudget metadata: name: nginx-pdb spec: minAvailable: 1 selector: matchLabels: app: nginx ``` - Utilisez ResourceQuota et LimitRange pour empêcher l'accaparement des ressources. - Auditez régulièrement les PriorityClasses : ```bash kubectl get priorityclasses -o custom-columns=NAME:.metadata.name,VALUE:.value,GLOBAL:.globalDefault,PREEMPTION:.preemptionPolicy ``` ## Liste de contrôle opérationnelle Utilisez cette liste de contrôle pour vous assurer d'avoir une surveillance et des alertes complètes pour la priorité et la préemption des pods. ### Configuration de la surveillance - [ ] Confirmez que la porte de fonctionnalité PodPriority est activée (par défaut dans K8s 1.14+). Exécutez : `kubectl version --short` et vérifiez la version. - [ ] Listez et documentez toutes les PriorityClasses : `kubectl get priorityclasses`. - [ ] Activez les métriques du planificateur en vous assurant que `--bind-address=0.0.0.0` et `--authorization-always-allow-paths=/metrics` sont définis. Vérifiez en exécutant : `kubectl get --raw /api/v1/namespaces/kube-system/pods/kube-scheduler-:10251/proxy/metrics | grep scheduler_preemption`. - [ ] Configurez un collecteur de métriques (Prometheus) pour collecter les métriques du planificateur. ### Règles d'alerte Créez des alertes basées sur les métriques du planificateur. Exemple de règles d'alerte Prometheus : ```yaml groups: - name: preemption-alerts rules: - alert: HighPreemptionRate expr: rate(scheduler_preemption_attempts_total{result="success"}[5m]) > 0.1 for: 10m labels: severity: warning annotations: summary: "Taux de préemption élevé dans le cluster" description: "La préemption se produit fréquemment ({{ $value }} par seconde), pouvant causer de l'instabilité." - alert: PreemptionFailures expr: increase(scheduler_preemption_attempts_total{result="failure"}[15m]) > 0 labels: severity: critical annotations: summary: "Échecs de préemption détectés" description: "Le planificateur n'a pas réussi à préempter des pods pour la planification." ``` - [ ] Configurez les notifications d'alerte (par exemple, Slack, PagerDuty). ### Panneaux de tableau de bord Dans Grafana (ou similaire), créez des panneaux pour : - Taux de tentatives de préemption : `rate(scheduler_preemption_attempts_total[5m])` - Nombre de victimes de préemption : `scheduler_preemption_victims_total` - Pods en attente avec haute priorité : `kube_pod_status_phase{phase="Pending"} * on(pod) group_left(priority_class) kube_pod_info` (nécessite kube-state-metrics). - Pression sur les ressources des nœuds : `sum by (node) (kube_node_status_capacity{resource="cpu"}) - sum by (node) (kube_pod_container_resource_requests{resource="cpu"})` ### Manuel de réponse aux incidents 1. En cas d'alerte, vérifiez les événements de préemption : `kubectl get events -A --field-selector reason=Preempted --sort-by='.lastTimestamp'`. 2. Identifiez les pods préemptés et leurs propriétaires. 3. Vérifiez l'utilisation des ressources du cluster : `kubectl top nodes`. 4. Si des pods critiques sont affectés, ajustez les priorités ou faites évoluer les ressources. 5. Documentez l'incident et examinez les classes de priorité. ## Conclusion La surveillance de la priorité et de la préemption des pods Kubernetes est essentielle pour maintenir la stabilité des applications. En comprenant comment le planificateur prend ses décisions de préemption et en observant les métriques clés, vous pouvez prévenir les évictions inattendues et répondre rapidement aux incidents. Nous avons couvert le cycle de vie complet : de l'inventaire de votre environnement et de la configuration sûre des priorités, à la vérification du comportement de préemption, au diagnostic des échecs et à l'établissement de listes de contrôle opérationnelles avec alertes et tableaux de bord. Comme prochaine étape, choisissez une vérification à faible risque dans ce guide. Par exemple, listez vos PriorityClasses et auditez quelques déploiements critiques pour vous assurer qu'ils ont des priorités appropriées. Ensuite, activez les métriques du planificateur et créez une alerte simple pour les tentatives de préemption. Cela vous donnera une visibilité immédiate sur le comportement du planificateur. N'oubliez pas que la préemption est une fonctionnalité puissante mais potentiellement perturbatrice. Testez toujours les changements dans un environnement contrôlé, documentez votre hiérarchie de priorités et ayez des procédures de récupération prêtes. Avec la bonne surveillance et les bonnes alertes, vous pouvez exploiter la priorité et la préemption des pods pour améliorer l'utilisation des ressources sans sacrifier la fiabilité.