Introduction
Les définitions de ressources personnalisées (Custom Resource Definitions, CRD) Kubernetes vous permettent d'étendre l'API Kubernetes avec vos propres types de ressources. Cet article présente une configuration complète de laboratoire local pour les CRD, destinée aux développeurs, aux consultants DevOps et aux équipes techniques de startups. Il relie la configuration, les tests, les exemples et le développement des CRD Kubernetes à des commandes spécifiques, des sorties attendues, des signaux d'échec et des décisions de récupération.
L'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 le résultat et documenter comment récupérer si l'état attendu n'est pas atteint. À la fin, vous disposerez d'un environnement local reproductible, d'exemples de CRD fonctionnels et de modèles de dépannage que vous pourrez appliquer à vos propres opérateurs.
Inventaire des versions et de l'environnement
Avant d'appliquer toute CRD, vous devez connaître votre environnement. Cette section couvre les plages de versions prises en charge, les prérequis, l'observation en lecture seule, le plus petit changement justifié et les commandes de vérification.
Prérequis et vérifications de version
Pour le développement local de CRD, vous avez besoin d'un cluster Kubernetes. Le moyen le plus simple est d'utiliser kind (Kubernetes in Docker), minikube ou le Kubernetes de Docker Desktop. Nous utiliserons kind dans cet article car il est léger et scriptable. Assurez-vous d'avoir installé :
kubectl(v1.25 ou version ultérieure recommandée)kind(v0.17.0 ou version ultérieure)- Docker (pour les nœuds
kind) jq(facultatif, pour filtrer les sorties JSON)
Vérifiez les versions :
kubectl version --client
# Exemple de sortie : Client Version: v1.27.3
kind version
# Exemple de sortie : kind v0.20.0 go1.20.4 linux/amd64
docker version --format '{{.Server.Version}}'
# Exemple de sortie : 24.0.2
Observation en lecture seule
Avant d'apporter des modifications, observez l'état actuel du cluster. Si vous n'avez pas encore de cluster, créez-en un, mais vérifiez d'abord si un cluster est déjà configuré :
kubectl config current-context
# Exemple de sortie : kind-kind (s'il existe déjà) ou une erreur s'il n'y en a pas
Si vous voyez un contexte, inspectez ses nœuds et la version du serveur API :
kubectl get nodes
# Exemple de sortie : NAME STATUS ROLES AGE VERSION
# kind-control-plane Ready control-plane 10m v1.27.3
kubectl version --short
# Exemple de sortie : Client Version: v1.27.3
# Server Version: v1.27.3
S'il n'existe pas de cluster, créez-en un pour ce laboratoire :
kind create cluster --name crd-lab --image kindest/node:v1.27.3
# Exemple de sortie :
# Creating cluster "crd-lab" ...
# ✓ Ensuring node image (kindest/node:v1.27.3) 🖼
# ✓ Preparing nodes 📦
# ✓ Writing configuration 📜
# ✓ Starting control-plane 🕹️
# ✓ Installing CNI 🔌
# ✓ Installing StorageClass 💾
# Set kubectl context to "kind-crd-lab"
Maintenant, définissez le contexte sur le nouveau cluster :
kubectl config use-context kind-crd-lab
# Exemple de sortie : Switched to context "kind-crd-lab".
Plus petit changement justifié
Maintenant que nous avons un cluster, nous allons ajouter une CustomResourceDefinition. Une CRD définit un nouveau type et son schéma. Nous commencerons par une CRD minimale pour une ressource fictive Widget.
Créez un fichier widget-crd.yaml :
apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
name: widgets.example.com
spec:
group: example.com
names:
kind: Widget
listKind: WidgetList
plural: widgets
singular: widget
scope: Namespaced
versions:
- name: v1
served: true
storage: true
schema:
openAPIV3Schema:
type: object
properties:
spec:
type: object
properties:
size:
type: integer
minimum: 1
maximum: 10
color:
type: string
enum: [red, green, blue]
required: [size, color]
Cette CRD définit une ressource à espace de noms widgets dans le groupe example.com, avec un schéma qui exige size (entier entre 1 et 10) et color (un parmi red, green, blue). Appliquez-la :
kubectl apply -f widget-crd.yaml
# Exemple de sortie : customresourcedefinition.apiextensions.k8s.io/widgets.example.com created
Vérifiez que la CRD est établie :
kubectl get crd widgets.example.com
# Exemple de sortie :
# NAME CREATED AT
# widgets.example.com 2023-07-01T12:00:00Z
Vérifiez ses conditions :
kubectl get crd widgets.example.com -o jsonpath='{.status.conditions[?(@.type=="Established")].status}'
# Exemple de sortie : True
Désormais, le cluster dispose d'un nouveau point de terminaison API. Vous pouvez lister les ressources personnalisées (actuellement aucune) :
kubectl get widgets
# Exemple de sortie : No resources found in default namespace.
Chemin de configuration sûr
Dans cette section, nous étendons la CRD précédente à un scénario plus réaliste : une ressource personnalisée à espace de noms avec des valeurs par défaut de validation, des colonnes d'impression supplémentaires et un modèle de contrôleur simple simulé via un script shell. Nous couvrons également comment apporter des modifications sûres et revenir en arrière si nécessaire.
CRD à espace de noms avec sous-ressource de statut
Ajoutez une sous-ressource de statut et des colonnes d'impression pour rendre kubectl get plus informatif. Modifiez widget-crd.yaml (ou créez un nouveau fichier widget-crd-v2.yaml) :
apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
name: widgets.example.com
spec:
group: example.com
names:
kind: Widget
listKind: WidgetList
plural: widgets
singular: widget
shortNames:
- wd
scope: Namespaced
versions:
- name: v1
served: true
storage: true
schema:
openAPIV3Schema:
type: object
properties:
spec:
type: object
properties:
size:
type: integer
minimum: 1
maximum: 10
color:
type: string
enum: [red, green, blue]
required: [size, color]
status:
type: object
properties:
phase:
type: string
enum: [Pending, Running, Failed]
message:
type: string
subresources:
status: {}
additionalPrinterColumns:
- name: Size
type: integer
jsonPath: .spec.size
- name: Color
type: string
jsonPath: .spec.color
- name: Phase
type: string
jsonPath: .status.phase
Appliquez la CRD mise à jour :
kubectl apply -f widget-crd-v2.yaml
# Exemple de sortie : customresourcedefinition.apiextensions.k8s.io/widgets.example.com configured
Remarque : la mise à jour d'une CRD est un changement sûr si vous ajoutez uniquement des champs, des sous-ressources ou des colonnes d'impression. La suppression de champs ou la modification de la validation peut affecter les ressources personnalisées existantes.
Créez maintenant un exemple de Widget :
cat <<EOF | kubectl apply -f -
apiVersion: example.com/v1
kind: Widget
metadata:
name: my-widget
namespace: default
spec:
size: 5
color: blue
EOF
# Exemple de sortie : widget.example.com/my-widget created
Vérifiez-le avec les colonnes personnalisées :
kubectl get widgets
# Exemple de sortie :
# NAME SIZE COLOR PHASE
# my-widget 5 blue
Simulation d'un contrôleur avec une boucle de surveillance Bash
Un vrai contrôleur surveille les changements et met à jour le statut. Pour la sécurité, nous pouvons simuler un contrôleur minimal à l'aide d'une boucle shell qui liste tous les widgets et définit un statut si aucun n'existe. Ce n'est pas de qualité production, mais cela démontre le modèle.
Tout d'abord, créez un petit script simple-controller.sh :
#!/usr/bin/env bash
set -euo pipefail
while true; do
for widget in $(kubectl get widgets -o jsonpath='{.items[*].metadata.name}'); do
phase=$(kubectl get widget "$widget" -o jsonpath='{.status.phase}')
if [ -z "$phase" ]; then
echo "Setting status for $widget to Pending"
kubectl patch widget "$widget" --type merge -p '{"status":{"phase":"Pending","message":"Processing"}}'
fi
done
sleep 10
done
Rendez-le exécutable et lancez-le dans un terminal séparé (ou en arrière-plan) :
chmod +x simple-controller.sh
./simple-controller.sh
Après quelques secondes, vérifiez le statut du widget :
kubectl get widget my-widget -o yaml
# Extrait de sortie attendu :
# status:
# message: Processing
# phase: Pending
Cela démontre comment un contrôleur peut gérer les mises à jour de statut en toute sécurité à l'aide de kubectl patch avec stratégie de fusion.
Retour en arrière et récupération
Si vous appliquez une modification de CRD qui casse les ressources existantes, vous pouvez parfois revenir en arrière en appliquant un manifeste précédent. Conservez les manifestes versionnés dans un référentiel Git. Par exemple, pour revenir à la v1 :
kubectl apply -f widget-crd.yaml
# Exemple de sortie : customresourcedefinition.apiextensions.k8s.io/widgets.example.com configured
Si une mise à jour de CRD invalide des ressources personnalisées existantes, vous devrez peut-être modifier ou supprimer ces ressources d'abord. Testez toujours dans un laboratoire local avant de toucher à la production.
Vérification et diagnostics
La vérification ne se limite pas à kubectl apply ; il s'agit de confirmer que la ressource se comporte comme prévu. Cette section fournit des contrôles concrets, des signaux d'échec courants et des commandes de diagnostic.
Vérification fonctionnelle du schéma de la CRD
Le schéma que nous avons défini applique des contraintes. Essayez de créer un widget avec une couleur invalide :
cat <<EOF | kubectl apply -f -
apiVersion: example.com/v1
kind: Widget
metadata:
name: bad-widget
spec:
size: 7
color: purple
EOF
# Erreur attendue :
# The Widget "bad-widget" is invalid: spec.color: Unsupported value: "purple": supported values: "red", "green", "blue"
Le serveur API rejette la demande avant de la stocker. Cela confirme que la validation du schéma fonctionne.
Testez les limites de taille :
cat <<EOF | kubectl apply -f -
apiVersion: example.com/v1
kind: Widget
metadata:
name: big-widget
spec:
size: 11
color: red
EOF
# Erreur attendue :
# The Widget "big-widget" is invalid: spec.size: Invalid value: 11: spec.size in body should be less than or equal to 10
Commandes de diagnostic pour les problèmes de CRD
Si votre CRD ne s'établit pas, vérifiez ses conditions de statut et ses événements.
kubectl describe crd widgets.example.com
# Recherchez les événements en bas, par exemple :
# Events:
# Type Reason Age From Message
# Warning NonStructuralSchema 2m customresourcedefinition-api spec.validation.openAPIV3Schema.type: Required value: must not be empty at the root
Signaux d'échec courants :
NonStructuralSchema: Le schéma n'est pas structurel (par exemple, il manquetype: object).NoServedVersions: La listeversionsest vide ou toutes les versions ontserved: false.Terminating: La CRD est bloquée en suppression en raison de finaliseurs.
Si vous voyez Terminating, inspectez les finaliseurs :
kubectl get crd widgets.example.com -o jsonpath='{.metadata.finalizers}'
# Exemple de sortie : ["customresourcecleanup.apiextensions.k8s.io"]
Si vous devez forcer la suppression (uniquement en laboratoire, pas en production), supprimez le finaliseur :
kubectl patch crd widgets.example.com -p '{"metadata":{"finalizers":[]}}' --type=merge
# Exemple de sortie : customresourcedefinition.apiextensions.k8s.io/widgets.example.com patched
Vérifiez ensuite s'il disparaît.
Vérification du comportement du contrôleur
Si votre contrôleur personnalisé ne détecte pas les changements, vérifiez ses journaux. Comme notre contrôleur simple s'exécute dans un shell local, sa sortie va au terminal. Dans un vrai pod, vous utiliseriez :
kubectl logs <controller-pod-name>
Problèmes courants de contrôleur :
- Mauvaise configuration RBAC : Le contrôleur n'a pas la permission de lister/surveiller les widgets. Vérifiez avec
kubectl auth can-i list widgets --as=system:serviceaccount:default:controller-sa - CRD non servie :
kubectl get widgetsrenvoie l'erreur "the server could not find the requested resource". Assurez-vous que la CRD est appliquée et queserved: true. - Dépréciation de l'API : Si vous utilisez une ancienne version d'API, assurez-vous d'utiliser
apiextensions.k8s.io/v1(pas v1beta1).
Modes de défaillance et récupération
Les opérateurs doivent prévoir les défaillances. Cette section détaille les modes de défaillance courants pour les CRD et les ressources personnalisées, ainsi que les étapes de récupération.
Mode de défaillance : la suppression de la CRD laisse des ressources personnalisées orphelines
Lorsque vous supprimez une CRD, toutes les ressources personnalisées de ce type sont supprimées immédiatement (récupérées par le ramasse-miettes). Si vous supprimez accidentellement une CRD avec des données importantes, vous risquez de perdre des ressources. La récupération nécessite des sauvegardes.
Avant de supprimer une CRD, sauvegardez toutes les ressources personnalisées :
kubectl get widgets -o yaml > widgets-backup.yaml
Si vous supprimez la CRD et devez la restaurer, recréez d'abord la CRD, puis réappliquez la sauvegarde :
kubectl delete crd widgets.example.com
# Exemple de sortie : customresourcedefinition.apiextensions.k8s.io "widgets.example.com" deleted
# Maintenant, recréez la CRD (en supposant que votre manifeste est sauvegardé)
kubectl apply -f widget-crd.yaml
# Puis restaurez les ressources
kubectl apply -f widgets-backup.yaml
Mais notez : si les ressources personnalisées avaient des champs de statut, ils peuvent ne pas être conservés car les sauvegardes capturées avant la suppression peuvent manquer le statut. Meilleure pratique : utilisez un outil de sauvegarde comme Velero.
Mode de défaillance : un schéma trop restrictif bloque les mises à jour
Si un schéma empêche des mises à jour légitimes en raison d'un nouveau champ, vous pouvez assouplir le schéma en ajoutant le champ comme facultatif. Exemple : ajoutez un champ description au schéma :
spec:
description:
type: string
Appliquez la CRD mise à jour, et les ressources existantes ne sont pas affectées car le champ est facultatif.
Pour éviter les changements cassants, suivez ces règles :
- Ne supprimez jamais un champ du schéma.
- Ne modifiez jamais le type d'un champ.
- Ajoutez uniquement de nouveaux champs facultatifs.
Pour les changements majeurs, introduisez une nouvelle version d'API (par exemple, v2) et laissez les anciennes ressources migrer progressivement.
Mode de défaillance : mauvaise configuration du webhook de conversion
Si vous utilisez des webhooks de conversion pour plusieurs versions, un webhook mal configuré peut casser tous les appels API pour la CRD. Pour récupérer, patchez la CRD pour supprimer la stratégie de conversion ou réparer le service webhook.
Exemple : vérifiez les paramètres de conversion :
kubectl get crd widgets.example.com -o jsonpath='{.spec.conversion}'
# Exemple de sortie : {"strategy":"Webhook","webhook":{"clientConfig":{"service":{"name":"conversion-webhook","namespace":"default","path":"/convert","port":443}},"conversionReviewVersions":["v1"]}}
Si le webhook est en panne, les demandes d'API peuvent échouer avec conversion webhook not found. Changez temporairement la stratégie en None (si une seule version est servie) pour rétablir l'accès :
kubectl patch crd widgets.example.com --type merge -p '{"spec":{"conversion":{"strategy":"None"}}}'
Remarque : cela exige que toutes les versions soient servies par le serveur API sans conversion, donc assurez-vous de n'avoir qu'une seule version ou que toutes les versions sont structurellement identiques.
Vérification de la récupération
Après toute action de récupération, vérifiez en listant et en décrivant les ressources, en vérifiant les conditions de la CRD et en vous assurant que les contrôleurs sont en cours d'exécution.
kubectl get crd widgets.example.com -o jsonpath='{.status.conditions}' | jq .
Liste de contrôle opérationnelle
Utilisez cette liste de contrôle avant et après tout changement de CRD dans votre laboratoire local (ou comme modèle pour la production). Chaque élément inclut un exemple concret.
| # | Élément de la liste | Exemple de commande / action | Résultat attendu |
|---|---|---|---|
| 1 | Enregistrez les versions actuelles du cluster et de la CRD | kubectl version --short et kubectl get crd widgets.example.com -o yaml | grep -A1 'name: v' | Exemple : v1.27.3, version de CRD v1 uniquement |
| 2 | Sauvegardez les ressources personnalisées existantes | kubectl get widgets -o yaml > widgets-backup-$(date +%Y%m%d).yaml | Fichier enregistré (par exemple, widgets-backup-20230701.yaml) |
| 3 | Appliquez le nouveau manifeste de CRD | kubectl apply -f widget-crd-v2.yaml | Sortie : customresourcedefinition configured |
| 4 | Vérifiez que la CRD est établie | kubectl get crd widgets.example.com -o jsonpath='{.status.conditions[?(@.type=="Established")].status}' | True |
| 5 | Testez la validation du schéma avec un objet invalide | echo '{"apiVersion":"example.com/v1","kind":"Widget","metadata":{"name":"test"},"spec":{"size":5,"color":"purple"}}' | kubectl apply -f - | Erreur : Unsupported value: "purple" |
| 6 | Créez une ressource personnalisée valide | Appliquez un manifeste avec size=3, color=green | Sortie : widget.example.com/test created |
| 7 | Vérifiez les champs de la ressource personnalisée via kubectl get | kubectl get widgets test | Affiche SIZE=3, COLOR=green, PHASE vide |
| 8 | Simulez la mise à jour du statut par le contrôleur | Exécutez simple-controller.sh | Patche le widget avec phase Pending |
| 9 | Vérifiez la mise à jour du statut | kubectl get widget test -o jsonpath='{.status.phase}' | Pending |
| 10 | Testez le retour en arrière | kubectl apply -f widget-crd.yaml (ancienne CRD) | CRD configurée sans sous-ressource de statut |
| 11 | Nettoyez les ressources | kubectl delete crd widgets.example.com après sauvegarde | CRD supprimée, toutes les ressources personnalisées supprimées |
Conclusion
La configuration d'un laboratoire local de définitions de ressources personnalisées Kubernetes avec des exemples pratiques est essentielle pour le développement sûr d'opérateurs personnalisés. Cet article a fourni un guide étape par étape : inventaire de l'environnement, CRD minimale, configuration sûre avec sous-ressources et colonnes d'impression, commandes de vérification et scénarios de récupération en cas de défaillance.
Pour renforcer l'apprentissage, pratiquez ces tâches :
- Étendez la CRD Widget avec un nouveau champ facultatif et un modèle de validation.
- Implémentez une deuxième version d'API (v2) avec une stratégie de conversion (None ou Webhook) et testez la migration.
- Déployez un vrai contrôleur à l'aide d'un déploiement Kubernetes et d'un compte de service avec RBAC approprié.
- Utilisez
kubectl explain widgetspour explorer la documentation API générée.
Rappelez-vous les principes de sécurité opérationnelle : observer avant de modifier, limiter le rayon d'impact, utiliser des espaces réservés, vérifier et documenter la récupération. Avec un laboratoire local solide, vous pouvez itérer plus rapidement et réduire les risques en production.
Prochaine étape : choisissez une vérification à faible risque de la liste de contrôle (par exemple, la validation du schéma), enregistrez l'état actuel, exécutez la vérification, comparez avec la sortie attendue et examinez les dépendances telles que les rôles RBAC pour votre contrôleur.
Un flux de travail technique fiable rend les défaillances visibles, protège les valeurs sensibles, limite les modifications à la ressource prévue et définit la vérification de récupération avant qu'un incident ne force la décision.