Introduction
Les définitions de ressources personnalisées (CustomResourceDefinitions, CRD) étendent l'API Kubernetes avec vos propres types de ressources. À mesure que votre application évolue, vous introduisez de nouvelles versions de ces ressources. Chaque version ajoute une surcharge en termes de stockage, de conversion et de serveur d'API. Sans une planification minutieuse de la capacité, les CRD versionnées peuvent silencieusement dégrader les performances du cluster, épuiser le stockage etcd ou provoquer des échecs de requêtes API.
Ce guide explique comment planifier la capacité pour le versionnage des CRD. Vous apprendrez à inventorier votre utilisation actuelle des CRD, à estimer le coût de chaque version, à définir des limites de ressources sûres, à surveiller les signaux de mise à l'échelle et à mettre en œuvre des procédures de récupération. Nous utilisons des exemples concrets basés sur Kubernetes 1.27+ et supposons une familiarité avec les concepts de base des CRD.
À la fin, vous serez en mesure de répondre :
- Quelle quantité de stockage etcd supplémentaire une nouvelle version de CRD consommera-t-elle ?
- Quelle est la latence de requête acceptable pour les webhooks de conversion ?
- Comment détecter que le serveur d'API est sous pression à cause des conversions de CRD ?
- Quelles étapes de retour en arrière sont sûres lorsqu'une conversion échoue ?
Inventaire des versions et de l'environnement
Avant de planifier la capacité, vous devez connaître votre état actuel. Rassemblez les informations suivantes pour chaque CRD qui aura plusieurs versions.
1. Lister les CRD et leurs versions
Exécutez :
kubectl get crd -o custom-columns=NAME:.metadata.name,STORAGE:.spec.versions[*].name,SERVED:.spec.versions[*].served,STORAGE_VERSION:.spec.versions[?(@.storage==true)].name
Exemple de sortie :
NAME STORAGE SERVED STORAGE_VERSION
widgets.example.com v1, v2 true, true [v1]
Cela montre que widgets.example.com a deux versions : v1 et v2. Les deux sont servies, mais v1 est la version de stockage actuelle. Tout nouvel objet v2 est converti en v1 pour le stockage à moins que vous ne changiez la version de stockage.
2. Compter les objets par version
Vous devez savoir combien d'objets existent pour chaque version afin d'estimer la croissance du stockage. Utilisez un sélecteur d'étiquettes si vos versions de CRD sont représentées par des étiquettes différentes, mais généralement vous devez interroger chaque version séparément.
# Obtenir le nombre d'objets v1
kubectl get widgets.v1.example.com --all-namespaces -o json | jq '.items | length'
# Obtenir le nombre d'objets v2
kubectl get widgets.v2.example.com --all-namespaces -o json | jq '.items | length'
Supposons que vous ayez 10 000 objets v1 et 2 000 objets v2. Les objets v2 sont déjà stockés en tant que v1 après conversion, donc ils n'ajoutent pas de stockage supplémentaire au-delà de leur représentation v1. Mais gardez ces chiffres pour planifier les futurs changements de version de stockage.
3. Estimer la taille des objets
Déterminez la taille sérialisée moyenne d'un objet de type CR. Utilisez kubectl get avec -o json et mesurez la taille d'un échantillon.
kubectl get widget.v1.example.com my-widget -n default -o json | wc -c
Si l'objet moyen fait 2 Ko, alors 10 000 objets consomment environ 20 Mo dans etcd (avant réplication et compression). Lorsque vous introduisez une nouvelle version, le stockage supplémentaire dépend de si vous stockez les deux versions ou convertissez à l'écriture.
4. Identifier la stratégie de conversion
Vérifiez si la CRD utilise un webhook de conversion ou None.
kubectl get crd widgets.example.com -o jsonpath='{.spec.conversion}'
Sorties possibles :
{"strategy":"None"}: Aucune conversion entre les versions. Chaque version est stockée séparément.{"strategy":"Webhook","webhook":{...}}: La conversion est gérée par un webhook externe. Cela ajoute une surcharge de requête au serveur d'API et une latence du webhook.
Si la stratégie est None, le serveur d'API ne sert que la version exacte stockée ; aucune conversion n'a lieu. C'est plus simple mais limite votre capacité à lire les anciennes versions après un changement de version de stockage.
5. Capacité du cluster et d'etcd
Connaissez vos limites de stockage etcd et votre utilisation actuelle. Si vous utilisez Kubernetes géré, vérifiez les quotas du fournisseur. Pour un cluster autogéré, interrogez les métriques etcd.
# Exemple : taille de la base de données etcd en octets
ETCDCTL_API=3 etcdctl --endpoints=https://127.0.0.1:2379 --cacert=/etc/kubernetes/pki/etcd/ca.crt --cert=/etc/kubernetes/pki/etcd/server.crt --key=/etc/kubernetes/pki/etcd/server.key endpoint status --write-out=table
Cherchez DB SIZE. Si vous êtes proche du quota par défaut de 2 Go (ou de votre quota configuré), prévoyez une compaction et éventuellement l'ajout de stockage.
Prérequis de l'environnement
- Kubernetes 1.16+ pour les fonctionnalités de versionnage des CRD.
- etcd v3.4+ recommandé pour une meilleure efficacité de stockage.
- Accès à metrics-server ou Prometheus pour les métriques du serveur d'API et d'etcd.
- Un cluster de test qui reflète les tailles de production pour les tests de charge des webhooks de conversion.
Chemin de configuration sûr
Maintenant que vous avez un inventaire, vous pouvez concevoir un plan de capacité. L'objectif est d'introduire le versionnage avec un risque minimal et une utilisation prévisible des ressources.
1. Définir des limites de ressources pour les webhooks de conversion
Si vous utilisez un webhook de conversion, il s'exécute en tant que service dans votre cluster. Il doit gérer les requêtes de conversion pour chaque incompatibilité de version d'objet. Définissez des demandes et limites de ressources appropriées.
Exemple de déploiement pour un webhook de conversion :
apiVersion: apps/v1
kind: Deployment
metadata:
name: crd-conversion-webhook
namespace: conversion
spec:
replicas: 3
template:
spec:
containers:
- name: webhook
image: example/conversion-webhook:1.2.3
resources:
requests:
cpu: "500m"
memory: "512Mi"
limits:
cpu: "1"
memory: "1Gi"
ports:
- containerPort: 443
Estimez le CPU et la mémoire en fonction du taux de conversion attendu et de la taille des objets. Par exemple, si chaque conversion prend 5 ms de CPU et que vous attendez 100 conversions par seconde, vous avez besoin d'au moins 500m CPU pour une réplique. Ajoutez une marge (par exemple, 2x) pour les pics.
2. Configurer les délais d'expiration et la politique d'échec du webhook
Le serveur d'API Kubernetes attend une réponse du webhook de conversion. Le délai d'expiration par défaut est de 10 secondes, configurable dans la configuration client du webhook de la CRD.
apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
name: widgets.example.com
spec:
conversion:
strategy: Webhook
webhook:
clientConfig:
url: "https://conversion.example.com/v1/convert"
caBundle: <base64-encoded-ca>
timeoutSeconds: 2
conversionReviewVersions: ["v1"]
versions:
- name: v1
served: true
storage: true
- name: v2
served: true
storage: false
Définissez timeoutSeconds sous clientConfig si nécessaire. Un délai plus court (par exemple, 2 secondes) empêche l'épuisement des threads du serveur d'API mais peut provoquer plus d'échecs de conversion sous charge. Équilibrez en fonction de la latence P99 de votre webhook.
Définissez failurePolicy sur Fail (par défaut) pour la cohérence, mais sachez que si le webhook est indisponible, toutes les requêtes pour la nouvelle version échoueront. Pour le déploiement initial, envisagez Ignore pendant les tests, mais passez à Fail pour la production afin d'éviter des incohérences de données silencieuses.
3. Planifier la migration de version de stockage
Lorsque vous introduisez une nouvelle version et la définissez comme stockage, les objets existants ne sont pas automatiquement convertis. Kubernetes effectue une migration paresseuse : les objets sont convertis vers la nouvelle version de stockage lorsqu'ils sont mis à jour ou accédés. Pour forcer la migration, vous pouvez déclencher des mises à jour ou utiliser un outil de migration.
Pour estimer l'impact sur la capacité, calculez la différence de taille de stockage entre les versions. Si les objets v2 sont plus petits (par exemple, en raison de l'élagage de champs), la migration réduit le stockage. S'ils sont plus grands, vous devez vous assurer qu'etcd peut gérer la croissance.
Utilisez un essai à sec dans un cluster de test pour voir le temps de conversion par objet. Par exemple :
# Simuler la conversion d'un objet v1 en v2
kubectl get widget.v1.example.com my-widget -n default -o json | jq '.apiVersion = "example.com/v2"' | kubectl convert -f -
4. Définir des limites de taux et des quotas
Le serveur d'API Kubernetes a une limitation de débit intégrée via --max-requests-inflight et --max-mutating-requests-inflight. Les valeurs par défaut sont respectivement 400 et 200. Si de nombreuses conversions ont lieu, les requêtes de mutation peuvent augmenter. Surveillez et ajustez ces indicateurs si nécessaire, mais comprenez l'impact sur les autres charges de travail.
Envisagez également ResourceQuota dans les espaces de noms pour limiter le nombre d'objets de type CR, empêchant une croissance incontrôlée.
apiVersion: v1
kind: ResourceQuota
metadata:
name: crd-quota
namespace: default
spec:
hard:
count/widgets.example.com: "10000"
5. Déploiement progressif
Ne servez pas une nouvelle version à tous les utilisateurs en même temps. Utilisez une approche par étapes :
- Ajoutez la nouvelle version avec
served: falseetstorage: falseà la CRD. Cela définit le schéma mais ne le sert pas. - Déployez le webhook de conversion et assurez-vous qu'il peut gérer les conversions entre les versions.
- Définissez
served: truepour la nouvelle version tout en gardant l'ancien stockage. Maintenant, les clients peuvent créer des objets avec la nouvelle version, mais ils sont stockés en tant qu'ancienne version après conversion. - Après surveillance, changez
storage: truepour la nouvelle version et définissezstorage: falsepour l'ancienne version. Déclenchez la migration progressivement. - Éventuellement, définissez
served: falsesur l'ancienne version après que tous les clients ont migré.
Cette approche vous permet de valider les performances de conversion à chaque étape.
Vérification et diagnostics
La planification de la capacité n'est pas une activité ponctuelle. Vous devez continuellement vérifier que votre système fonctionne dans les limites.
1. Surveiller les métriques du serveur d'API
Exposez les métriques du serveur d'API (par défaut sur :6443/metrics). Métriques clés à surveiller :
apiserver_request_duration_seconds_bucketavecresource=customresourcedefinitionsou votre groupe de ressources. Une latence élevée indique une surcharge de conversion ou une pression etcd.apiserver_current_inflight_requests- doit rester en dessous de la limite.apiserver_request_totalavec verb=POST/PUT et votre ressource.etcd_db_total_size_in_bytesetetcd_object_countspour la croissance du stockage.
Exemple de requête Prometheus pour la latence du webhook de conversion :
histogram_quantile(0.99, sum(rate(apiserver_request_duration_seconds_bucket{resource="widgets", verb="POST"}[5m])) by (le))
Si le P99 dépasse 1 seconde, examinez les performances du webhook.
2. Tester le webhook de conversion sous charge
Utilisez un générateur de charge pour simuler les conversions. Par exemple, créez de nombreux objets avec la nouvelle version et mesurez le débit du serveur d'API et l'utilisation des ressources du webhook. Une simple boucle shell :
for i in $(seq 1 1000); do
kubectl apply -f - <<EOF
apiVersion: example.com/v2
kind: Widget
metadata:
name: widget-$i
namespace: loadtest
spec:
size: 10
EOF
done
Surveillez le CPU/la mémoire du pod webhook avec kubectl top pods -n conversion. Si le CPU est proche des limites, augmentez les répliques ou les ressources.
3. Valider la progression de la migration de version de stockage
Confirmez la version de stockage actuelle après l'avoir changée :
kubectl get crd widgets.example.com -o jsonpath='{.spec.versions[?(@.storage==true)].name}'
Si vous avez changé le stockage de v1 à v2, cette commande devrait sortir v2. Le champ status.storedVersions devrait éventuellement montrer seulement ["v2"] après que tous les objets sont migrés. Vous pouvez forcer la migration en mettant à jour les objets ou en utilisant kubectl get --raw pour déclencher la conversion.
4. Vérifier la taille et la fragmentation d'etcd
Après la migration, surveillez la taille de la base de données etcd. Si elle augmente de manière inattendue, vérifiez le stockage en double des deux versions (possible si la conversion est None et que vous avez les deux versions stockées). Utilisez la compaction pour récupérer de l'espace :
ETCDCTL_API=3 etcdctl --endpoints=... compact <latest-revision>
ETCDCTL_API=3 etcdctl --endpoints=... defrag
5. Journaux et alertes
Configurez des alertes pour :
- Les requêtes de mutation du serveur d'API dépassant 80 % de la limite.
- La taille de la base de données etcd au-dessus de 80 % du quota.
- Le taux d'erreur du webhook de conversion au-dessus de 1 %.
- Les versions stockées contenant des noms de version inattendus.
Pièges courants et comment les éviter
Même avec une planification minutieuse, les équipes rencontrent souvent les mêmes problèmes. Voici les pièges les plus fréquents et comment les éviter ou s'en remettre.
1. Sous-estimer la surcharge de conversion
Piège : Supposer que la conversion est gratuite ou triviale, donc vous n'allouez pas assez de CPU au webhook. Pourquoi cela arrive : Les opérations CRUD normales ne montrent pas le coût de la conversion ; il n'apparaît que lorsque le webhook est invoqué. Comment l'éviter : Testez en charge les conversions avec des tailles d'objets et des taux représentatifs. Définissez les demandes de ressources en fonction de la latence P99 mesurée, pas de suppositions. Récupération : Augmentez les répliques du webhook et les limites de CPU, puis retestez.
2. Ignorer la croissance d'etcd due aux nouveaux champs
Piège : Ajouter une nouvelle version avec des champs supplémentaires augmente la taille des objets, mais vous supposez que le stockage ne croîtra pas beaucoup. Pourquoi cela arrive : Vous comparez le nombre d'objets mais pas la taille moyenne des objets. Comment l'éviter : Mesurez la taille sérialisée d'objets d'échantillon dans les deux versions et projetez l'utilisation totale d'etcd avant le déploiement. Récupération : Définissez un ResourceQuota pour plafonner le nombre d'objets ou compactez etcd plus fréquemment.
3. Laisser failurePolicy: Ignore en production
Piège : Vous définissez failurePolicy: Ignore pendant les tests et oubliez de le changer en Fail. Pourquoi cela arrive : Il est facile de négliger une spécification de CRD après les tests. Comment l'éviter : Utilisez GitOps ou une liste de contrôle pour garantir que toutes les CRD ont failurePolicy: Fail avant la production. Mettez en place une politique d'audit pour détecter Ignore. Récupération : Changez la politique en Fail et validez que le webhook est sain.
4. Ne pas surveiller storedVersions après la migration
Piège : Vous changez la version de stockage mais ne vérifiez jamais que tous les objets sont migrés. Pourquoi cela arrive : La migration paresseuse signifie que les objets peuvent rester dans l'ancien stockage pendant des mois. Comment l'éviter : Après le changement de stockage, configurez une tâche pour vérifier périodiquement storedVersions et alerter si plus d'une version est présente plus longtemps que prévu. Récupération : Forcez la migration en mettant à jour tous les objets ou en utilisant un outil comme kubectl get --raw pour déclencher la conversion, puis surveillez jusqu'à ce que storedVersions ne montre que la nouvelle version.
5. Ne pas tester les procédures de retour en arrière
Piège : Vous ne testez que le chemin aller, pas le retour en arrière. Pourquoi cela arrive : Le retour en arrière est considéré comme un cas limite. Comment l'éviter : Pratiquez le retour en arrière en staging : simulez une migration de stockage échouée et revenez à l'ancienne version, en mesurant le temps et l'impact. Récupération : Si vous vous retrouvez dans un état d'échec, suivez les étapes de retour en arrière dans la section sur les modes de défaillance ci-dessous, puis améliorez votre manuel de retour en arrière.
Modes de défaillance et récupération
Même avec une planification minutieuse, des échecs surviennent. Sachez comment récupérer.
1. Webhook de conversion indisponible
Si le service webhook est en panne et failurePolicy: Fail, toutes les requêtes pour la nouvelle version seront rejetées avec des erreurs comme :
Error from server (InternalError): error when creating "STDIN": conversion webhook for example.com/v2, Kind=Widget failed: Post "https://conversion.example.com/v1/convert": dial tcp 10.0.0.5:443: connect: connection refused
Récupération :
- Restaurez le déploiement du webhook.
- Si la récupération est lente, définissez temporairement
failurePolicy: Ignoresur la CRD pour permettre les requêtes sans conversion. Cela peut entraîner une perte de données si les champs ne sont pas compatibles, donc utilisez avec prudence. - Alternativement, retirez la nouvelle version de la liste servie pour arrêter de la servir.
2. Latence de conversion élevée provoquant une surcharge du serveur d'API
Si les conversions sont lentes, les threads du serveur d'API peuvent se bloquer, augmentant les requêtes en vol et provoquant le ralentissement ou l'expiration des nouvelles requêtes.
Récupération :
- Augmentez les répliques du webhook de conversion.
- Optimisez le code du webhook (par exemple, mise en cache, conversion parallèle).
- Augmentez temporairement
--max-mutating-requests-inflightdu serveur d'API, mais surveillez la mémoire. - Réduisez la charge en désactivant le service de la nouvelle version jusqu'à ce que la latence s'améliore.
3. Migration de stockage incomplète ou bloquée
Parfois, une CRD reste avec plusieurs versions stockées parce que certains objets ne sont jamais mis à jour. Cela peut provoquer une amplification de lecture et une taille etcd accrue.
Récupération :
- Identifiez les objets encore dans l'ancienne version de stockage en utilisant
kubectl get --rawet en vérifiant la version stockée. - Forcez la mise à jour de ces objets (par exemple, en ajoutant une annotation) pour déclencher la conversion.
- Si de nombreux objets, utilisez un script pour les lister et les mettre à jour par lots.
4. Quota etcd dépassé
Si etcd manque d'espace, le serveur d'API peut devenir en lecture seule, provoquant des pannes généralisées.
Récupération :
- Compactez et défragmentez immédiatement etcd.
- Si le quota est dur, augmentez le quota etcd ou ajoutez du stockage.
- Réduisez le nombre d'objets en supprimant les CR obsolètes ou les espaces de noms.
- Révisez votre plan de capacité pour la croissance future.
5. Retour en arrière des changements de versionnage
Si une nouvelle version pose des problèmes, vous pouvez revenir en arrière partiellement ou totalement.
- Définissez
served: falsepour la nouvelle version pour arrêter de la servir. Les objets stockés existants restent, mais aucun nouvel objet ne peut être créé avec cette version. - Si vous avez changé la version de stockage et que la migration est incomplète, vous pouvez revenir au stockage d'origine. Cependant, les objets déjà migrés vers la nouvelle version de stockage seront reconvertis lors de l'accès. Cela peut provoquer une charge temporaire.
- Ayez toujours une sauvegarde d'etcd avant les changements majeurs. Restaurez à partir de la sauvegarde si nécessaire.
Liste de contrôle des opérations et propriété des décisions
Utilisez cette liste de contrôle pour les opérations continues et la révision de la capacité. Chaque élément a un propriétaire clair et une cadence de révision.
| Domaine | Ce qu'il faut vérifier | Propriétaire | Fréquence |
|---|---|---|---|
| Versions de CRD | Nombre de versions, version de stockage, drapeaux servis | Responsable de plateforme (par exemple, Priya Shah, responsable ingénierie) | Mensuellement ou lors d'un changement |
| Nombre d'objets | Nombre total d'objets par version et taux de croissance | Propriétaire d'application (par exemple, Tom Chen, équipe API produit) | Mensuellement |
| Stockage etcd | Taille de la base de données, tendance de croissance, état de compaction | Ingénieur infrastructure (par exemple, Maria Garcia, SRE) | Hebdomadairement |
| Webhook de conversion | CPU, mémoire, taux d'erreur, latence | Mainteneur du webhook (par exemple, Alex Kim, équipe service de conversion) | Surveillance continue |
| Serveur d'API | Requêtes en vol, durée des requêtes, taux d'erreur | SRE (par exemple, Jordan Lee, administrateur Kubernetes) | Surveillance continue |
| Migration de stockage | storedVersions ne doit contenir que les versions prévues | Responsable de plateforme (par exemple, Priya Shah, responsable ingénierie) | Après le changement de stockage, puis hebdomadairement jusqu'à stabilisation |
| Alertes | Réviser les seuils d'alerte et ajuster | SRE de garde (par exemple, rôle tournant) | Trimestriellement ou après des incidents |
Propriété des décisions : La décision finale de changer la version de stockage ou de servir une nouvelle version revient au responsable de plateforme. La décision doit être révisée au moins tous les six mois ou lorsqu'une nouvelle version est proposée.
Étapes de révision
- Inventaire : Mettez à jour votre liste de versions de CRD et le nombre d'objets.
- Mesurer : Collectez la taille d'etcd et les métriques du serveur d'API.
- Analyser : Comparez l'utilisation actuelle aux limites de capacité. Cherchez des tendances.
- Planifier : Si vous approchez des limites (par exemple, etcd plein à 70 %, requêtes en vol du serveur d'API > 70 % du maximum), planifiez des actions de mise à l'échelle telles que :
- Augmenter le stockage etcd ou compacter plus fréquemment.
- Mettre à l'échelle le webhook de conversion.
- Augmenter les ressources du serveur d'API ou ajuster les limites de débit.
- Élaguer les anciens objets CR.
- Tester : Avant d'appliquer les changements, testez dans un cluster de staging avec une charge réaliste.
- Documenter : Mettez à jour les manuels avec les nouveaux seuils et procédures.
Conclusion
La planification de la capacité pour le versionnage des CRD Kubernetes est essentielle pour maintenir les performances et la stabilité du cluster. En inventoriant votre état actuel, en estimant les besoins en ressources, en configurant des limites sûres et en surveillant les métriques clés, vous pouvez introduire de nouvelles versions sans surprises.
Rappelez-vous les points clés :
- Les CRD versionnées ajoutent une surcharge de stockage et de CPU, surtout avec les webhooks de conversion.
- Utilisez un déploiement progressif pour exposer progressivement les nouvelles versions et surveiller l'impact.
- Définissez des limites de ressources et des alertes basées sur la charge mesurée.
- Ayez des procédures de récupération pour les défaillances courantes telles que l'indisponibilité du webhook et la pression etcd.
- Révisez régulièrement les métriques de capacité et ajustez à mesure que votre application se développe.
Commencez par un projet pilote étroit : choisissez une CRD avec une utilisation modérée, implémentez le versionnage en suivant ce guide, et observez les métriques avant de déployer sur toutes les CRD. Cette approche pratique réduit les risques et renforce la confiance.
Pour en savoir plus, consultez la documentation Kubernetes sur les CustomResourceDefinitions et les métriques du serveur d'API. Expérimentez avec les commandes fournies dans un environnement de test pour les adapter à votre configuration.