## 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-lease` pour les battements de cœur des nœuds, `kube-system` pour 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 : ```bash 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 : ```bash 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 `_`. 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 à : ```yaml 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é : ```yaml 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 `-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=client` pour vérifier la syntaxe. - **Diff avec l'état en direct** : Utilisez `kubectl diff` pour 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. ```bash 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 : ```bash 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. ```bash 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 : ```diff --- 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. ```bash kubectl apply -f leases/myapp-leader.yaml --namespace staging ``` Vérifiez ensuite que le bail existe et possède les champs attendus : ```bash 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 : ```yaml 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 : ```bash #!/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 : ```bash kubectl get lease myapp-leader -n dev -o yaml ``` Extrait de sortie attendu : ```yaml 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 : ```bash #!/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 : ```bash ./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 : ```bash 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 -n --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. 1. Identifiez le manifeste précédent correct dans l'historique Git. Par exemple, utilisez `git log` pour trouver le commit avant la modification : ```bash git log --oneline -- leases/myapp-leader.yaml ``` 2. Revenez à la version précédente du fichier : ```bash git checkout -- leases/myapp-leader.yaml ``` Cela ne valide pas la modification ; cela la met en scène. Appliquez ensuite l'ancien manifeste : ```bash kubectl apply -f leases/myapp-leader.yaml ``` 3. Vérifiez que le bail est sain avec le script de vérification. 4. Observez l'application pendant quelques cycles de bail (par exemple, 3 fois `leaseDurationSeconds`) pour assurer la stabilité. 5. Validez la restauration dans Git avec un message clair. 6. 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.