Introduction
Les baux Kubernetes sont des primitives de coordination qui permettent aux composants distribués d'élire un leader en toute sécurité, de revendiquer un accès exclusif ou de signaler leur vivacité par battement de cœur. Le gestionnaire de contrôleurs et le planificateur Kubernetes utilisent des baux en interne pour l'élection de leader, et de nombreux contrôleurs personnalisés s'appuient sur les baux à des fins similaires. La gestion manuelle des objets de bail est sujette aux erreurs : les noms, les espaces de noms, les identités de détenteur et les durées doivent correspondre exactement, et une modification incorrecte peut perturber l'élection de leader ou provoquer un comportement de split-brain. L'automatisation de la gestion des baux via des pipelines CI/CD apporte répétabilité, traçabilité et sécurité à ces objets sensibles.
Ce guide présente une mise en œuvre pratique : inventorier votre environnement, créer une configuration ciblée, vérifier les déploiements, gérer les échecs et établir une liste de contrôle opérationnelle. À la fin, vous serez en mesure de déployer des modifications de baux via un pipeline en toute confiance.
Comprendre les baux et leur cycle de vie
Avant d'automatiser, prenez le temps de comprendre ce que contient un objet de bail et comment il se comporte. Un bail comporte deux sections principales : spec et status. La section spec définit les paramètres de coordination souhaités :
holderIdentity: le détenteur actuel du bail, généralement un nom de pod ou un nom d'hôte suivi d'un UUID. Lorsqu'il est vide, le bail est disponible.leaseDurationSeconds: la durée pendant laquelle un détenteur peut conserver le bail sans le renouveler. Les valeurs courantes sont 15, 30 ou 60 secondes.acquireTime: le moment où le détenteur actuel a acquis le bail (défini par le système).renewTime: le moment où le détenteur actuel a renouvelé le bail pour la dernière fois (mis à jour par le détenteur).leaseTransitions: le nombre de fois que le bail a changé de mains.
Le champ status est utilisé par le système pour suivre l'état observé, mais pour la plupart des automatisations, vous ne gérez que spec. Lorsqu'un composant souhaite devenir leader, il tente de créer ou de mettre à jour un bail avec son propre holderIdentity et un renewTime. Si le détenteur actuel ne renouvelle pas avant l'expiration de leaseDurationSeconds, un autre composant peut acquérir le bail. Ce mécanisme empêche deux composants d'agir simultanément en tant que leader.
Pour la CI/CD, vous gérez généralement l'existence et l'état initial des baux, et non leurs mises à jour en direct. Votre pipeline appliquera un manifeste souhaité qui crée un bail sans détenteur (laissant le runtime le réclamer) ou met à jour un champ non critique comme leaseDurationSeconds. Modifier manuellement holderIdentity est risqué et ne doit être fait que si vous comprenez le comportement du composant.
Inventaire des versions et de l'environnement
Avant d'automatiser les baux, établissez précisément avec quoi vous travaillez. Un environnement typique comprend :
- Version du cluster Kubernetes (par exemple, v1.28.3)
- Version du client kubectl (doit être dans une version mineure du serveur)
- Espace de noms où résident les baux (par exemple,
kube-node-leasepour les battements de cœur des nœuds,kube-systempour l'élection de leader du contrôleur) - Objets de bail existants et leurs identités de détenteur attendues
- Identifiants d'accès et autorisations RBAC pour votre compte de service CI/CD
Collectez les versions du cluster et du client avec :
kubectl version --short
Exemple de sortie :
Client Version: v1.28.3
Kustomize Version: v5.0.4-0.20230601165947-6ce0bf390ce3
Server Version: v1.28.3
Listez les baux existants dans tous les espaces de noms pour avoir une vue d'ensemble :
kubectl get leases --all-namespaces
Exemple de sortie :
NAMESPACE NAME HOLDER AGE
kube-node-lease ip-10-0-1-23 ip-10-0-1-23 5d
kube-system kube-controller-manager ip-10-0-0-10_1c9b8e2e-2f7a-4ad9-a3b1-9b1f0e6d6a6f 7d
kube-system kube-scheduler ip-10-0-0-10_2b4a1a5e-6d8e-4c4e-a5d4-6f9c8e4f1c2e 7d
Notez le format de l'identité du détenteur : pour l'élection de leader, il s'agit généralement de <hostname>_<uuid>. Assurez-vous que votre compte de service CI/CD dispose des autorisations get, list, create, update et patch sur les baux dans les espaces de noms concernés. Un ClusterRole minimal pourrait ressembler à :
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: lease-manager
rules:
- apiGroups: ["coordination.k8s.io"]
resources: ["leases"]
verbs: ["get", "list", "create", "update", "patch"]
N'accordez pas delete à moins que votre flux de travail ne nécessite spécifiquement la suppression de baux, ce qui peut forcer une élection de leader immédiate.
En outre, documentez le responsable de l'automatisation des baux. Cette personne examine et approuve les modifications des manifestes de bail. Par exemple, attribuez à Priya Shah, responsable de l'ingénierie, le rôle de propriétaire. Elle examine les modifications chaque mois ou lorsqu'un nouveau bail est ajouté.
Concevoir une configuration ciblée et sûre
Une automatisation sûre des baux commence par un projet pilote étroit et mesurable. Choisissez un seul bail qui n'est pas sur le chemin critique, comme un bail de test dans un espace de noms de développement. Définissez le bail dans un dépôt Git en tant que manifeste, puis appliquez-le via votre pipeline.
Voici un exemple de manifeste pour un bail d'élection de leader personnalisé :
apiVersion: coordination.k8s.io/v1
kind: Lease
metadata:
name: myapp-leader
namespace: dev
spec:
holderIdentity: ""
leaseDurationSeconds: 15
acquireTime: null
renewTime: null
leaseTransitions: 0
À la création, laissez holderIdentity vide ; le runtime de l'application le réclamera. Pour un bail statique qui pré-attribue un détenteur, vous pouvez définir holderIdentity, mais soyez prudent : si ce détenteur n'est pas réellement en cours d'exécution, le bail sera obsolète.
Stockez tous les manifestes de bail dans un répertoire dédié de votre dépôt Git, tel que leases/. Cela facilite l'examen des modifications et leur application sélective.
Utilisez les espaces de noms Kubernetes pour isoler les baux. Si vous gérez plusieurs applications, envisagez d'utiliser une convention de nommage comme <app-name>-leader pour éviter toute confusion.
Construire le pipeline CI/CD pour les déploiements de baux
Mettez en œuvre le pipeline par étapes :
- Lint et validation : Utilisez
kubectl apply --dry-run=clientpour vérifier la syntaxe. - Diff avec l'état en direct : Utilisez
kubectl diffpour voir ce qui changerait. - Appliquer dans un espace de noms de staging : Si possible, testez d'abord dans un cluster non productif.
- Appliquer en production avec approbation : Ajoutez une porte d'approbation manuelle avant l'application en production.
- Vérification post-application : Exécutez des contrôles pour confirmer que le bail est correct.
Voici des exemples concrets pour chaque étape utilisant des outils CI courants.
Étape 1 : Validation par essai à blanc
Validez toujours le manifeste avant de tenter une modification. Cela permet de détecter les erreurs de syntaxe et les violations de schéma.
kubectl apply -f leases/myapp-leader.yaml --dry-run=client
Si la commande se termine avec un statut non nul, le manifeste est invalide. Dans un pipeline, utilisez :
if ! kubectl apply -f leases/myapp-leader.yaml --dry-run=client; then
echo "Validation failed for leases/myapp-leader.yaml"
exit 1
fi
Sortie attendue en cas de succès :
lease.coordination.k8s.io/myapp-leader created (dry run)
Étape 2 : Diff avec l'état en direct
Si le bail existe déjà, utilisez kubectl diff pour voir les modifications exactes. C'est un contrôle de sécurité critique.
kubectl diff -f leases/myapp-leader.yaml || true
Le || true empêche le pipeline d'échouer s'il y a des différences (car kubectl diff renvoie 1 lorsque des différences sont trouvées). Examinez la sortie du diff.
Exemple de sortie lors de la mise à jour de leaseDurationSeconds de 15 à 30 :
--- live
+++ merged
@@ -7,7 +7,7 @@
holderIdentity: myapp-instance-1
- leaseDurationSeconds: 15
+ leaseDurationSeconds: 30
acquireTime: "2025-03-01T12:00:00Z"
Examinez attentivement le diff ; la modification de leaseDurationSeconds affecte la rapidité avec laquelle un nouveau leader est élu après une défaillance.
Étape 3 : Appliquer en staging
Si vous disposez d'un cluster ou d'un espace de noms de staging, appliquez-y le manifeste d'abord et exécutez des contrôles de vérification. Cela peut être automatisé mais doit inclure certaines assertions.
kubectl apply -f leases/myapp-leader.yaml --namespace staging
Vérifiez ensuite que le bail existe et possède les champs attendus :
kubectl get lease myapp-leader -n staging -o yaml
Étape 4 : Porte d'approbation manuelle pour la production
Avant d'appliquer en production, exigez qu'un humain approuve la modification. Dans GitHub Actions, vous pouvez utiliser un environnement avec des réviseurs requis. Dans GitLab CI, utilisez une tâche when: manual.
Exemple de configuration GitLab CI :
deploy_production:
stage: deploy
script:
- kubectl apply -f leases/myapp-leader.yaml --namespace production
environment:
name: production
when: manual
only:
- main
Étape 5 : Vérification post-application
Après l'application en production, exécutez le script de vérification décrit dans la section suivante. S'il échoue, le pipeline doit effectuer une restauration ou alerter.
Exemple de script de pipeline combiné pour une tâche CI simple basée sur shell :
#!/bin/bash
set -e
# Validation par essai à blanc
kubectl apply -f leases/myapp-leader.yaml --dry-run=client
# Afficher le diff (uniquement si l'objet existe déjà)
kubectl diff -f leases/myapp-leader.yaml || true
# Appliquer
kubectl apply -f leases/myapp-leader.yaml
# Vérifier
./verify-lease.sh "myapp-leader" "dev" "myapp-instance-1"
Dans le script ci-dessus, le propriétaire (Priya Shah) reçoit un e-mail ou une notification Slack lorsqu'une application en production est terminée.
Vérification et diagnostics
Après avoir appliqué un bail, vérifiez ses champs et son statut. Utilisez kubectl get avec un formatage de sortie :
kubectl get lease myapp-leader -n dev -o yaml
Extrait de sortie attendu :
apiVersion: coordination.k8s.io/v1
kind: Lease
metadata:
name: myapp-leader
namespace: dev
spec:
holderIdentity: myapp-instance-1
leaseDurationSeconds: 30
acquireTime: "2025-03-01T12:30:00Z"
renewTime: "2025-03-01T12:31:00Z"
leaseTransitions: 1
Vérifiez que holderIdentity est conforme aux attentes et que renewTime est récent (dans leaseDurationSeconds). Si le détenteur ne renouvelle pas, une nouvelle élection a lieu.
Pour une vérification automatisée, écrivez un script qui interroge le bail et fait échouer le pipeline si les champs sont hors limites. Exemple :
#!/bin/bash
# verify-lease.sh
LEASE_NAME="$1"
NAMESPACE="$2"
EXPECTED_HOLDER="$3"
HOLDER=$(kubectl get lease "$LEASE_NAME" -n "$NAMESPACE" -o jsonpath='{.spec.holderIdentity}')
if [ "$HOLDER" != "$EXPECTED_HOLDER" ]; then
echo "Unexpected holder: $HOLDER (expected $EXPECTED_HOLDER)"
exit 1
fi
RENEW_TIME=$(kubectl get lease "$LEASE_NAME" -n "$NAMESPACE" -o jsonpath='{.spec.renewTime}')
if [ -z "$RENEW_TIME" ] || [ "$RENEW_TIME" == "null" ]; then
echo "Renew time is null; lease may not be held"
exit 1
fi
# Comparer les horodatages avec une tolérance de 2 * leaseDurationSeconds (simplifié en utilisant l'époque).
LEASE_DURATION=$(kubectl get lease "$LEASE_NAME" -n "$NAMESPACE" -o jsonpath='{.spec.leaseDurationSeconds}')
TOLERANCE=$((LEASE_DURATION * 2))
CURRENT_EPOCH=$(date -u +%s)
RENEW_EPOCH=$(date -u -d "$RENEW_TIME" +%s)
DIFF=$((CURRENT_EPOCH - RENEW_EPOCH))
if [ "$DIFF" -gt "$TOLERANCE" ]; then
echo "Renew time is too old: $RENEW_TIME (current: $(date -u +%Y-%m-%dT%H:%M:%SZ))"
exit 1
fi
echo "Lease $LEASE_NAME in $NAMESPACE is healthy: holder=$HOLDER, renewTime=$RENEW_TIME"
Exécutez-le comme :
./verify-lease.sh myapp-leader dev myapp-instance-1
Sortie attendue :
Lease myapp-leader in dev is healthy: holder=myapp-instance-1, renewTime=2025-03-01T12:31:00Z
En outre, surveillez les événements dans l'espace de noms pour les problèmes liés aux baux :
kubectl get events -n dev --field-selector involvedObject.name=myapp-leader
Exemple de sortie :
LAST SEEN TYPE REASON OBJECT MESSAGE
2m Normal LeaderElection lease/myapp-leader myapp-instance-1 became leader
L'absence d'événements peut aussi être un bon signe ; cela signifie que le bail est stable.
Configurez des alertes pour les baux critiques. Par exemple, utilisez un système de surveillance comme Prometheus pour récupérer le renewTime et alerter s'il est plus ancien que leaseDurationSeconds pendant plus de deux intervalles consécutifs.
Pièges courants et comment les éviter
L'automatisation des baux introduit des risques spécifiques. Voici les pièges courants et comment les gérer.
Piège 1 : Modifications accidentelles de holderIdentity
Il est tentant de définir manuellement holderIdentity pour forcer un leader particulier. Cependant, si le détenteur spécifié n'est pas en cours d'exécution ou est défaillant, le cluster peut entrer dans un état où aucun composant ne peut acquérir le bail car le champ détenteur est non vide mais le détenteur ne renouvelle jamais. Cela peut bloquer l'élection de leader.
Pourquoi cela arrive : Un développeur peut modifier le manifeste pour définir un détenteur pendant le débogage ou pour « pré-attribuer » un leader.
Évitement : Gardez holderIdentity vide dans les manifestes versionnés, sauf si vous avez un cas d'utilisation statique spécifique. Le runtime de l'application doit être responsable de la définition et du renouvellement du détenteur.
Récupération : Si un détenteur obsolète est défini, exécutez kubectl patch lease <name> -n <ns> --type merge -p '{"spec":{"holderIdentity":""}}' pour l'effacer, permettant une nouvelle élection.
Piège 2 : leaseDurationSeconds mal configuré
Une valeur trop courte peut entraîner des changements de leader fréquents sous charge normale, car le détenteur ne peut pas renouveler à temps. Une valeur trop longue retarde le basculement lorsque le leader meurt, provoquant des temps d'arrêt.
Pourquoi cela arrive : Copier un manifeste d'une autre application sans ajuster la durée pour correspondre à l'intervalle de renouvellement de l'application.
Évitement : Comprenez l'intervalle de renouvellement de l'application. En règle générale, le détenteur renouvelle au moins toutes les leaseDurationSeconds / 2. Définissez la durée sur au moins deux fois l'intervalle de renouvellement, plus une marge. Par exemple, si l'application renouvelle toutes les 5 secondes, définissez leaseDurationSeconds sur 15 ou 20.
Récupération : Mettez à jour le manifeste avec la valeur correcte et appliquez. La modification prend effet lors de la prochaine acquisition.
Piège 3 : Application dans le mauvais espace de noms
Un manifeste de bail peut avoir un champ metadata.namespace, mais les systèmes CI le remplacent parfois par un contexte kubeconfig par défaut ou mal configuré. Cela peut créer un bail dans un espace de noms non intentionnel, entraînant de la confusion et éventuellement un second leader.
Pourquoi cela arrive : Se fier à l'espace de noms par défaut dans le kubeconfig au lieu de définir explicitement l'espace de noms dans le manifeste ou la commande.
Évitement : Spécifiez toujours namespace dans le manifeste. Dans les commandes du pipeline, ajoutez --namespace explicitement. Utilisez des contextes kubeconfig distincts par environnement.
Récupération : Supprimez le bail créé incorrectement (si sûr) et appliquez dans le bon espace de noms. Examinez la configuration du pipeline.
Piège 4 : Suppression accidentelle d'un bail
Un pipeline peut inclure une étape kubectl delete pour le nettoyage, mais sans garde-fous, il pourrait supprimer un bail actif, forçant une élection immédiate et une perturbation potentielle.
Pourquoi cela arrive : Une tâche de nettoyage générique ou un manifeste mal configuré qui définit metadata.deletionTimestamp.
Évitement : N'accordez pas la permission delete au compte de service CI/CD. Si la suppression est nécessaire, ajoutez une confirmation explicite et une vérification que le bail n'est pas actuellement détenu (holderIdentity est vide ou ancien).
Récupération : Recréez le bail à partir du manifeste ; l'application réélira un leader. Surveillez les erreurs.
Piège 5 : Ne pas vérifier après l'application
Sans contrôle post-application, un manifeste mal appliqué peut passer inaperçu jusqu'à ce qu'une défaillance survienne.
Pourquoi cela arrive : Supposer qu'une application kubectl apply réussie signifie que la modification est correcte.
Évitement : Incluez toujours un script de vérification comme étape du pipeline. Le script doit vérifier l'identité du détenteur, la fraîcheur du temps de renouvellement et éventuellement la durée du bail.
Récupération : Exécutez immédiatement le script de vérification manuellement et restaurez si nécessaire.
Procédures de restauration et de récupération
Même avec une planification minutieuse, une modification de bail peut causer des problèmes. Voici une procédure de restauration concrète.
- Identifiez le manifeste précédent correct dans l'historique Git. Par exemple, utilisez
git logpour trouver le commit avant la modification :
git log --oneline -- leases/myapp-leader.yaml
- Revenez à la version précédente du fichier :
git checkout <previous-commit> -- leases/myapp-leader.yaml
Cela ne valide pas la modification ; cela la met en scène. Appliquez ensuite l'ancien manifeste :
kubectl apply -f leases/myapp-leader.yaml
- Vérifiez que le bail est sain avec le script de vérification.
- Observez l'application pendant quelques cycles de bail (par exemple, 3 fois
leaseDurationSeconds) pour assurer la stabilité.
- Validez la restauration dans Git avec un message clair.
- Le propriétaire responsable (par exemple, Priya Shah) examine la restauration et met à jour le runbook avec les détails de l'incident.
Si le problème a été causé par un facteur externe (par exemple, l'application se comporte mal), traitez-le séparément.
Liste de contrôle opérationnelle
Utilisez cette liste de contrôle pour chaque exécution d'automatisation de bail et pour l'examen périodique. Stockez cette liste de contrôle dans le dépôt à côté des manifestes de bail. Le propriétaire de cette liste de contrôle est le responsable de l'ingénierie, examinée mensuellement.
| Étape | Action | Résultat attendu |
|---|---|---|
| 1 | Vérifier les versions du cluster et du client | Serveur et client dans une version mineure |
| 2 | Vérifier les autorisations RBAC pour le compte de service CI/CD | Peut obtenir, lister, créer, mettre à jour, patcher les baux dans les espaces de noms cibles |
| 3 | Examiner les modifications du manifeste via diff | Seuls les champs prévus ont changé |
| 4 | Appliquer en essai à blanc | Pas d'erreurs de syntaxe |
| 5 | Appliquer en staging (si disponible) | Bail créé ou mis à jour comme prévu |
| 6 | Approbation manuelle avant la production | Approbation humaine enregistrée |
| 7 | Appliquer en production | Message d'application réussi |
| 8 | Vérifier l'identité du détenteur et le temps de renouvellement | Le détenteur correspond aux attentes ; le temps de renouvellement est récent |
| 9 | Vérifier les événements pour l'élection de leader | Événements d'élection de leader normaux ou aucun |
| 10 | Documenter toute anomalie | Entrée de journal dans le runbook |
Exécutez cette liste de contrôle pour chaque modification de bail. Pour les baux critiques (par exemple, kube-controller-manager), envisagez une revue par les pairs du manifeste avant de l'appliquer.
Conclusion
L'automatisation des workflows de baux Kubernetes avec CI/CD est un moyen pratique de gérer les primitives de coordination en toute sécurité. En inventoriant votre environnement, en mettant en œuvre un pipeline ciblé avec validation et diff, en vérifiant les déploiements et en vous préparant aux échecs, vous réduisez les erreurs manuelles et gagnez en répétabilité. Commencez par un projet pilote étroit, comme recommandé pour toute nouvelle automatisation, puis étendez à plus de baux à mesure que la confiance grandit. N'oubliez pas de toujours examiner les diffs, d'éviter les suppressions accidentelles et d'avoir un plan de restauration. Avec ces pratiques, votre gestion des baux devient un processus contrôlé et traçable.
Comme prochaines étapes, envisagez d'intégrer vos contrôles de vérification dans un système de surveillance et d'étendre l'automatisation à d'autres ressources de coordination comme les ConfigMaps utilisées pour l'élection de leader. Réexaminez régulièrement l'attribution du propriétaire et la liste de contrôle pour vous adapter aux changements.