Introduction
Les définitions de ressources personnalisées (Custom Resource Definitions, CRD) étendent Kubernetes avec des API spécifiques à un domaine, permettant aux équipes de gérer des applications et des infrastructures via des configurations déclaratives. Cependant, les CRD sont puissantes et complexes. Un seul champ mal configuré peut casser des ressources existantes, provoquer des échecs silencieux de l'API ou rendre les mises à niveau impossibles. Ce guide examine les erreurs fréquentes de configuration des CRD à travers des exemples pratiques, en montrant comment les détecter, les prévenir et s'en remettre dans des clusters de production. Vous apprendrez à valider les schémas, à éviter les pièges courants et à établir des flux de travail opérationnels fiables.
Inventaire des versions et de l'environnement
Avant de modifier une CRD, sachez exactement ce que vous exécutez. Capturez la version de Kubernetes, la version de l'API de la CRD et la version du contrôleur qui consomme la CRD. Certains comportements diffèrent entre les versions de Kubernetes, notamment autour des schémas structurels et de l'élagage.
Exécutez ces commandes en lecture seule pour enregistrer l'environnement :
kubectl version --short
kubectl get crd <crd-name> -o yaml
kubectl get deployment -n <namespace> <controller-name> -o yaml
La sortie attendue inclut la version de l'API (apiextensions.k8s.io/v1 ou v1beta1), les versions stockées de la CRD et la balise d'image du contrôleur. Par exemple :
API Version: apiextensions.k8s.io/v1
Kind: CustomResourceDefinition
Spec:
Group: example.com
Versions:
- Name: v1
Served: true
Storage: true
Si la CRD utilise apiextensions.k8s.io/v1beta1, planifiez une migration vers v1 car v1beta1 est supprimée dans Kubernetes 1.22 et versions ultérieures. Vérifiez que tous les champs requis par le schéma v1 sont présents, en particulier schema.openAPIV3Schema avec les exigences de schéma structurel.
Test pratique de l'environnement : appliquez une ressource personnalisée minimale et vérifiez qu'elle apparaît dans kubectl get <plural> sans erreurs. Par exemple :
apiVersion: example.com/v1
kind: MyResource
metadata:
name: test-instance
spec:
size: 1
Puis :
kubectl apply -f test-instance.yaml
kubectl get myresources
Si la ressource n'est pas listée, vérifiez les journaux du contrôleur et les conditions de la CRD.
Chemin de configuration sûr
Un déploiement sûr d'une CRD minimise le risque de casser des ressources personnalisées et des contrôleurs existants. Suivez ces étapes :
- Examinez la CRD actuelle :
kubectl get crd <crd-name> -o yaml > crd-backup.yaml - Testez les modifications de schéma localement : Utilisez
kubectl apply --dry-run=clientet--dry-run=serverpour valider sans persister. - Étalez la nouvelle CRD : Si vous ajoutez une nouvelle version, assurez-vous qu'elle est
served: falseinitialement, puis activez-la progressivement. - Mettez à jour le contrôleur d'abord : Le contrôleur doit comprendre le nouveau schéma avant que vous ne l'appliquiez au cluster.
- Appliquez la modification de la CRD :
kubectl apply -f crd-updated.yaml
Exemple : Ajout d'une nouvelle version v2 à une CRD existante. Commencez avec served: true, storage: false :
versions:
- name: v1
served: true
storage: true
- name: v2
served: true
storage: false
Après la migration des données, définissez progressivement storage: true sur v2 et storage: false sur v1.
Erreur courante : Changer la version de stockage sans chemin de migration. Cela peut entraîner une perte de données ou des erreurs d'API. Kubernetes ne convertit pas automatiquement les objets stockés ; vous devez fournir un webhook de conversion ou le gérer manuellement.
Vérification : Après l'application, vérifiez les conditions d'état de la CRD :
kubectl get crd <crd-name> -o jsonpath='{.status.conditions}' | jq .
Recherchez Established: True et NamesAccepted: True. Essayez également de créer une ressource avec les anciens et les nouveaux champs pour garantir la compatibilité.
Vérification et diagnostics
Un diagnostic efficace commence par l'observation de la réponse du système à votre configuration. Utilisez kubectl describe pour faire apparaître les événements et les détails d'état.
Validez l'exactitude du schéma en appliquant une ressource volontairement invalide :
apiVersion: example.com/v1
kind: MyResource
metadata:
name: bad-instance
spec:
size: "large" # entier attendu
Sortie attendue de kubectl apply :
The request is invalid: spec.size: Invalid value: "string": spec.size in body must be of type integer: "string"
Si vous ne voyez pas d'erreur de validation, le schéma peut manquer type: integer ou le champ n'est pas marqué comme requis. Examinez le schéma OpenAPI v3 sous spec.versions[].schema.openAPIV3Schema.
Diagnostiquez les problèmes du contrôleur : Récupérez les journaux du contrôleur :
kubectl logs -n <namespace> deployment/<controller-name> --tail=50
Recherchez les erreurs de réconciliation répétées, telles que unable to parse resource ou field not recognized. Elles indiquent une inadéquation entre le schéma de la CRD et les attentes du contrôleur.
Vérifiez les conditions d'état de la CRD :
kubectl get crd <crd-name> -o yaml | grep -A 10 conditions:
Conditions à surveiller :
NamesAccepted: vrai si les noms sont valides.Established: vrai lorsque le point de terminaison REST est actif.NonStructuralSchema: présent si le schéma n'est pas structurel.
Exemple : Une CRD avec un schéma non structurel déclenchera cet avertissement :
message: 'spec.validation.openAPIV3Schema.type: Required value: must not be empty at the root'
Corrigez en spécifiant type: object à la racine et en ajoutant properties pour chaque champ.
Modes de défaillance et récupération
Les erreurs de configuration des CRD peuvent provoquer des échecs partiels, des pertes de données silencieuses ou une indisponibilité complète de l'API. Préparez des procédures de récupération pour les scénarios les plus probables.
Scénario 1 : Un schéma invalide casse les ressources existantes
Symptôme : Après la mise à jour d'une CRD, les ressources existantes échouent à la validation lors de la mise à jour ou des opérations du contrôleur.
Cause : Le nouveau schéma a des exigences plus strictes (par exemple, de nouveaux champs requis) que les anciennes ressources ne satisfont pas.
Récupération :
- Revenez à la version précédente de la CRD en utilisant votre sauvegarde :
kubectl apply -f crd-backup.yaml
- Corrigez le schéma pour qu'il soit rétrocompatible, par exemple en n'ajoutant pas de champs requis sans valeurs par défaut.
- Réappliquez la CRD mise à jour.
Scénario 2 : Version de stockage modifiée sans conversion
Symptôme : kubectl get <ressource> renvoie des erreurs comme no kind is registered for the version "v2" ou des erreurs de conversion.
Cause : Vous avez changé la version de storage mais n'avez pas fourni de webhook de conversion, et les objets existants sont stockés dans l'ancienne version.
Récupération :
- Remettez la version de stockage à la précédente :
kubectl patch crd <crd-name> --type='json' -p='[{"op": "replace", "path": "/spec/versions/0/storage", "value": true},{"op": "replace", "path": "/spec/versions/1/storage", "value": false}]'
(Ajustez les indices pour correspondre à vos versions.)
- Implémentez un webhook de conversion ou migrez manuellement les objets en utilisant
kubectl get --rawetkubectl replace. - Répétez ensuite le changement de version de stockage.
Scénario 3 : Suppression d'une CRD orpheline des ressources personnalisées
Symptôme : Vous supprimez une CRD et toutes ses ressources personnalisées sont supprimées immédiatement, entraînant une perte de données.
Cause : La suppression en cascade est le comportement par défaut. Vous aviez l'intention de conserver les données pour une migration ultérieure.
Récupération : Restaurez à partir d'une sauvegarde etcd ou de snapshots. Pour éviter cela à l'avenir, définir spec.preserveUnknownFields: false ne suffit pas ; vous devez exporter les ressources avant la suppression :
kubectl get <plural> -o yaml > resources-backup.yaml
Après avoir recréé la CRD, réappliquez les ressources.
Scénario 4 : Contrôleur non mis à jour pour les nouveaux champs
Symptôme : Les nouveaux champs sont ignorés ou provoquent des plantages du contrôleur.
Cause : Les structures Go du contrôleur n'incluent pas les nouveaux champs, ou le contrôleur utilise du code non structuré avec un décodage strict.
Récupération : Mettez à jour le contrôleur pour correspondre au nouveau schéma, puis redémarrez-le. Pour éviter cela, utilisez un webhook de conversion pour conserver plusieurs versions d'API ou coordonnez les versions de la CRD et du contrôleur.
Erreurs courantes et comment les éviter
1. Omettre le schéma structurel
Pourquoi cela arrive : Les premières CRD utilisaient souvent preserveUnknownFields: true ou des schémas minimaux pour plus de flexibilité.
Risque : Kubernetes ne peut pas élaguer les champs inconnus, ce qui entraîne un gonflement de la mémoire du serveur d'API et une incompatibilité avec les webhooks d'admission et les outils de validation des CRD.
Évitement : Définissez toujours un openAPIV3Schema complet avec type: object et des propriétés pour tous les champs connus. Définissez preserveUnknownFields: false (par défaut en v1).
Exemple de schéma structurel minimal :
schema:
openAPIV3Schema:
type: object
properties:
spec:
type: object
properties:
size:
type: integer
minimum: 1
2. Oublier les valeurs par défaut pour les nouveaux champs
Pourquoi cela arrive : L'ajout de nouveaux champs sans default peut casser les objets existants qui n'ont pas ce champ défini.
Risque : Les contrôleurs peuvent devenir un pointeur nul vers un champ, provoquant des plantages. Ou les webhooks d'admission rejettent les anciens objets.
Évitement : Définissez une default pour chaque nouveau champ, ou faites en sorte que le contrôleur gère gracieusement les valeurs manquantes.
Exemple :
properties:
replicas:
type: integer
default: 1
3. Changer les types de champs de manière incompatible
Pourquoi cela arrive : Les développeurs supposent que changer un champ de chaîne à entier est facile.
Risque : Cela casse la compatibilité de l'API. Les clients existants et les objets stockés peuvent échouer.
Évitement : Ne changez jamais le type d'un champ dans la même version d'API. Introduisez une nouvelle version (v2) avec le type modifié et utilisez un webhook de conversion.
4. Ne pas tester avec kubectl explain
Pourquoi cela arrive : Les équipes se précipitent pour appliquer les CRD sans vérifier la génération de la documentation.
Risque : Les utilisateurs ne peuvent pas découvrir correctement les champs ; kubectl explain donne des informations incomplètes.
Évitement : Après avoir appliqué la CRD, exécutez kubectl explain <ressource>.<champ> pour vous assurer que les descriptions apparaissent. Ajoutez des champs description dans le schéma.
Exemple :
kubectl explain myresource.spec.size
La sortie attendue inclut la description et le type.
5. Ignorer les webhooks de conversion
Pourquoi cela arrive : Les équipes ne servent initialement qu'une seule version, puis ajoutent plus tard une nouvelle version sans webhook.
Risque : Lorsque plusieurs versions sont servies, le serveur d'API ne peut pas convertir entre elles, provoquant des erreurs pour les clients qui utilisent une version spécifique.
Évitement : Implémentez un webhook de conversion dès que vous avez plus d'une version. Utilisez des outils comme controller-runtime pour générer des webhooks.
Exemple de configuration de webhook dans la CRD :
conversion:
strategy: Webhook
webhook:
conversionReviewVersions: ["v1", "v1beta1"]
clientConfig:
service:
name: conversion-webhook
namespace: default
path: /convert
6. Élaguer accidentellement des champs personnalisés
Pourquoi cela arrive : Vous ajoutez un champ mais ne l'incluez pas dans le schéma, et le serveur d'API l'élague car les champs inconnus sont supprimés par défaut.
Risque : Les données disparaissent silencieusement lorsqu'un utilisateur soumet une ressource avec ce champ.
Évitement : Assurez-vous que le schéma inclut chaque champ attendu par votre contrôleur. Utilisez x-kubernetes-preserve-unknown-fields: true uniquement dans des contextes imbriqués spécifiques si nécessaire.
Exemple de préservation des champs inconnus imbriqués :
properties:
spec:
type: object
x-kubernetes-preserve-unknown-fields: true
Mais utilisez avec parcimonie ; il vaut mieux modéliser tous les champs.
Liste de contrôle des opérations
Maintenez une liste de contrôle des opérations CRD reproductible. Attribuez un responsable pour chaque étape majeure et révisez la liste trimestriellement ou après des incidents.
| Étape | Action | Responsable | Fréquence | Vérification |
|---|---|---|---|---|
| 1 | Sauvegarder le manifeste CRD actuel dans le contrôle de version | Responsable plateforme (ex. Priya Shah) | Avant tout changement | kubectl get crd <name> -o yaml diff avec le dépôt |
| 2 | Examiner les modifications de schéma pour la rétrocompatibilité | Ingénieur backend (ex. Tom Nguyen) | Chaque PR | kubectl apply --dry-run=server réussit |
| 3 | Tester avec des ressources d'exemple | Ingénieur QA (ex. Maria Lopez) | Chaque version | kubectl apply -f samples/ et vérifier l'état |
| 4 | Vérifier les journaux du contrôleur pour les erreurs | SRE de garde (ex. Chris Evans) | Après le déploiement | kubectl logs ne montre aucune erreur liée à la CRD |
| 5 | Valider la sortie de kubectl explain | Développeur (ex. Sam Chen) | Chaque changement de schéma | kubectl explain <ressource> affiche les champs |
| 6 | Examiner les métriques du webhook de conversion | Responsable plateforme | Mensuel | Latence du webhook < 100 ms, taux d'erreur < 0,1 % |
| 7 | Tester la procédure de retour en arrière | SRE (ex. Dana White) | Trimestriel | Appliquer la CRD précédente et confirmer que les ressources fonctionnent toujours |
Utilisez cette liste comme point de départ : adaptez les responsables et les fréquences à la taille de votre équipe et à votre rythme de publication.
Conclusion
Les erreurs de configuration des CRD peuvent avoir des conséquences considérables, allant de la perte de données silencieuse à des défaillances d'API à l'échelle du cluster. En comprenant les pièges courants, en validant rigoureusement les schémas et en établissant des manuels de récupération, vous pouvez exploiter les CRD en toute confiance. Commencez par un inventaire de vos CRD actuelles, implémentez des schémas structurels et testez chaque changement dans un environnement de préproduction. La prochaine étape consiste à choisir une CRD dans votre cluster, à exécuter les diagnostics décrits ici et à corriger les problèmes que vous trouvez. Prendre ces habitudes dès maintenant vous épargnera un débogage pénible plus tard.
Un flux de travail fiable pour les CRD ne consiste pas seulement à éviter les erreurs ; il s'agit de rendre votre API Kubernetes étendue prévisible, observable et sûre pour tous ceux qui l'utilisent.