Aller au contenu
English
Conteneurs & Orchestration medium

Manifests Kubernetes : produire, valider et corriger vite (CKAD)

70 min de lecture

logo kubernetes

Produire des manifests Kubernetes rapidement, c'est savoir générer un squelette avec kubectl, le compléter avec les bons champs, et valider avant d'appliquer. Ce guide vous donne les réflexes CKAD : kubectl explain, --dry-run=client -o yaml, correction rapide, et validation côté serveur.

L'épreuve n'évalue pas votre connaissance théorique de l'API, mais votre capacité à produire, corriger et valider un manifest rapidement. Tout ce qui suit est orienté par cette contrainte de temps.

  • Workflow examen : générer → compléter → valider → appliquer
  • Deux approches : génération assistée vs écriture manuelle
  • Namespace et contexte : éviter les erreurs de cible
  • Snippets fréquents : resources, probes, command, envFrom
  • Correction rapide : kubectl edit, kubectl patch
  • Validation complète : --dry-run=client, --dry-run=server, kubectl diff

En examen, chaque seconde compte. Voici le workflow le plus efficace :

  1. Configurer le namespace de travail

    Fenêtre de terminal
    kubectl create namespace ckad
    kubectl config set-context --current --namespace=ckad

    En changeant le namespace courant, vous évitez de répéter -n à chaque commande.

  2. Générer un squelette

    Fenêtre de terminal
    kubectl create deployment web --image=nginx:1.30@sha256:d5792f71a9496b833bc08ea834a758c46e2b6a6306c10f4be926f38a656cdc1c \
    --dry-run=client -o yaml > web.yaml
  3. Compléter le manifest

    Fenêtre de terminal
    vim web.yaml

    Ajoutez les champs manquants : resources, probes, labels...

  4. Valider avant application

    Fenêtre de terminal
    kubectl apply --dry-run=client -f web.yaml
    kubectl diff -f web.yaml # si la ressource existe déjà
  5. Appliquer et vérifier

    Fenêtre de terminal
    kubectl apply -f web.yaml
    kubectl get all
    kubectl describe deploy web

Il existe deux façons d'obtenir un fichier YAML valide : le faire générer par kubectl ou l'écrire à la main. Les deux mènent au même résultat, mais elles n'ont pas le même coût en temps ni la même valeur pédagogique. La génération évite les fautes d'indentation et les mauvaises apiVersion ; l'écriture manuelle vous apprend où se placent réellement les champs.

Utile pour : gagner du temps, éviter les erreurs de base, partir d'un squelette valide.

Fenêtre de terminal
kubectl create deployment web --image=nginx:1.30@sha256:d5792f71a9496b833bc08ea834a758c46e2b6a6306c10f4be926f38a656cdc1c \
--dry-run=client -o yaml > web.yaml

Le YAML généré est un squelette. Vous devez presque toujours l'ajuster : labels, ports, resources, probes...

Utile pour : comprendre la structure, corriger un YAML existant, maîtriser les champs importants.

Quand vous connaissez bien la structure, vous pouvez écrire directement. Mais en examen, la génération assistée reste plus rapide.

La méthode qui gagne combine donc les deux : générer le squelette avec --dry-run=client -o yaml, puis compléter à la main les champs que kubectl ne sait pas deviner. Vous ne perdez pas de temps sur l'apiVersion et l'indentation, et vous gardez la main là où l'examen vous attend.

Kubernetes utilise un langage déclaratif : vous décrivez l'état souhaité, et Kubernetes se charge de l'atteindre. Chaque manifest YAML suit une structure commune avec quatre champs obligatoires.

Ces quatre clés sont présentes dans tous les manifests, quel que soit le type de ressource ; seul le contenu de spec change d'un kind à l'autre.

apiVersion: v1
kind: Pod
metadata:
name: web
namespace: ckad
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx:1.30@sha256:d5792f71a9496b833bc08ea834a758c46e2b6a6306c10f4be926f38a656cdc1c
ports:
- containerPort: 80
ChampDescriptionExemples
apiVersionVersion de l'API Kubernetesv1, apps/v1, networking.k8s.io/v1
kindType de ressourcePod, Deployment, Service, ConfigMap
metadataIdentité de l'objetname, namespace, labels, annotations
specSpécification détailléeVarie selon le kind

Les labels sont des paires clé-valeur qui permettent de sélectionner des ressources. Un Service trouve ses Pods via un selector qui matche les labels. Utilisez la même convention partout (ex: app: nginx).

Un namespace est un cloisonnement logique du cluster : deux ressources peuvent porter le même nom dans deux namespaces différents sans entrer en conflit. kubectl travaille toujours dans un namespace, celui du contexte courant si vous ne passez pas -n. Fixer ce namespace une fois pour toutes est le geste qui évite le plus d'erreurs, en examen comme en production.

Trois commandes suffisent : créer le namespace, l'attacher au contexte courant, puis vérifier que le changement a bien été pris en compte.

Fenêtre de terminal
# Créer le namespace
kubectl create namespace monprojet
# Définir comme namespace courant
kubectl config set-context --current --namespace=monprojet
# Vérifier le contexte actuel
kubectl config get-contexts

Une ressource créée dans le mauvais namespace n'échoue pas : elle est acceptée, elle démarre, et elle reste invisible depuis l'endroit où vous la cherchez. Pire, une ConfigMap ou un Secret ne sont jamais lisibles depuis un autre namespace, donc le Pod qui les référence reste bloqué au démarrage.

ProblèmeConséquenceSolution
Oublier -nRessource créée dans defaultChanger le namespace courant
Mauvais namespaceRessource introuvablekubectl get pods -A pour chercher
ConfigMap/Secret ailleursnot found au démarrage du PodCréer dans le même namespace

Vous n'avez pas besoin de mémoriser la structure de chaque ressource : le cluster la décrit lui-même. Deux commandes suffisent pour retrouver n'importe quel champ, kubectl api-resources pour l'inventaire des types et kubectl explain pour le détail des champs. Cette documentation embarquée est autorisée en examen, contrairement à vos notes personnelles.

Cette liste donne aussi les abréviations (deploy, svc, cm) qui raccourcissent toutes les commandes suivantes.

Fenêtre de terminal
kubectl api-resources | head -15
Sortie
NAME SHORTNAMES APIVERSION NAMESPACED KIND
bindings v1 true Binding
componentstatuses cs v1 false ComponentStatus
configmaps cm v1 true ConfigMap
endpoints ep v1 true Endpoints
events ev v1 true Event
limitranges limits v1 true LimitRange
namespaces ns v1 false Namespace
nodes no v1 false Node
persistentvolumeclaims pvc v1 true PersistentVolumeClaim
persistentvolumes pv v1 false PersistentVolume
pods po v1 true Pod
podtemplates v1 true PodTemplate
replicationcontrollers rc v1 true ReplicationController
resourcequotas quota v1 true ResourceQuota

Cette commande liste tous les types de ressources avec leur abréviation, leur apiVersion et si elles sont namespacées. Le tri est alphabétique par groupe d'API : les ressources du groupe v1 sortent en premier, et deployments n'apparaît que bien plus bas, dans apps/v1. Pour aller droit au but, filtrez plutôt que de dérouler :

Fenêtre de terminal
kubectl api-resources | grep -i deployment

kubectl explain documente chaque champ directement depuis le cluster :

Fenêtre de terminal
kubectl explain pod.spec.containers
KIND: Pod
VERSION: v1
FIELD: containers <[]Container>
DESCRIPTION:
List of containers belonging to the pod.
FIELDS:
args <[]string>
command <[]string>
env <[]EnvVar>
image <string>
name <string> -required-
ports <[]ContainerPort>
resources <ResourceRequirements>
...

La sortie par défaut s'arrête au premier niveau. Quand vous cherchez placer un champ dont vous connaissez le nom sans savoir à quelle profondeur il vit, --recursive déplie tout l'arbre d'un coup, et un grep vous y amène directement :

Fenêtre de terminal
kubectl explain deployment.spec --recursive | grep -A5 strategy

C'est la commande à connaître le jour où vous cherchez terminationGracePeriodSeconds ou topologySpreadConstraints sans vouloir ouvrir un navigateur.

Au lieu d'écrire un YAML de zéro, générez-le avec --dry-run=client -o yaml :

Fenêtre de terminal
kubectl run web --image=nginx:1.30@sha256:d5792f71a9496b833bc08ea834a758c46e2b6a6306c10f4be926f38a656cdc1c \
--dry-run=client -o yaml
Sortie
apiVersion: v1
kind: Pod
metadata:
labels:
run: web
name: web
namespace: ckad
spec:
containers:
- image: nginx:1.30@sha256:d5792f71a9496b833bc08ea834a758c46e2b6a6306c10f4be926f38a656cdc1c
name: web
resources: {}
dnsPolicy: ClusterFirst
restartPolicy: Always
status: {}

Les champs vides resources: {} et status: {} ne sont pas du bruit : resources est précisément celui que l'examen vous demandera de remplir.

Le YAML généré par kubectl create service ajoute souvent un selector qui ne correspond pas à vos Pods. Préférez kubectl expose ou ajustez manuellement le selector après génération.

Ces champs reviennent constamment en CKAD. Gardez-les en mémoire.

command remplace l'ENTRYPOINT de l'image et args remplace sa CMD : déclarer l'un sans l'autre suffit rarement, car l'image perd alors la moitié de sa ligne de démarrage.

containers:
- name: app
image: busybox:1.36@sha256:73aaf090f3d85aa34ee199857f03fa3a95c8ede2ffd4cc2cdb5b94e566b11662
command: ["sh", "-c"]
args: ["echo hello && sleep 3600"]

Une valeur d'environnement est toujours une chaîne de caractères : les booléens et les nombres doivent être entre guillemets, sinon l'API server rejette le manifest.

env:
- name: APP_ENV
value: production
- name: APP_DEBUG
value: "false"

Chaque clé de la ConfigMap devient une variable du même nom, ce qui évite de les énumérer une par une. Les clés dont le nom n'est pas un identifiant d'environnement valide sont ignorées au démarrage, sans erreur.

envFrom:
- configMapRef:
name: app-config

Les requests servent au scheduler pour choisir un nœud capable d'accueillir le Pod ; les limits sont appliquées par le kubelet pendant l'exécution. Dépasser la limite CPU provoque un throttling, dépasser la limite mémoire provoque la mort du conteneur (OOMKilled).

resources:
requests:
cpu: "100m"
memory: "128Mi"
limits:
cpu: "500m"
memory: "256Mi"
  • CPU : 100m = 0,1 core, 1 = 1 core
  • Mémoire : 128Mi = 128 mebibytes, 1Gi = 1 gibibyte

Les deux sondes n'ont pas du tout les mêmes conséquences. Un échec de la readinessProbe retire le Pod des endpoints du Service, sans le redémarrer ; un échec de la livenessProbe fait redémarrer le conteneur. Une liveness trop agressive sur une application lente à démarrer déclenche des redémarrages en boucle.

readinessProbe:
httpGet:
path: /healthz
port: 80
initialDelaySeconds: 5
periodSeconds: 10
livenessProbe:
httpGet:
path: /healthz
port: 80
initialDelaySeconds: 15
periodSeconds: 20

Déclarer un containerPort est purement informatif : cela n'ouvre ni ne ferme rien. L'intérêt est ailleurs, le name donné ici peut être réutilisé comme targetPort dans un Service ou comme port dans une sonde.

ports:
- containerPort: 80
name: http
- containerPort: 443
name: https

Le montage se fait en deux temps et le lien entre les deux est le nom : volumes déclare la source au niveau du Pod, volumeMounts indique où la monter dans le conteneur. Une faute de frappe entre les deux bloque le Pod au démarrage.

containers:
- name: app
image: nginx:1.30@sha256:d5792f71a9496b833bc08ea834a758c46e2b6a6306c10f4be926f38a656cdc1c
volumeMounts:
- name: config-volume
mountPath: /etc/config
volumes:
- name: config-volume
configMap:
name: app-config

Un Pod est l'unité de base dans Kubernetes. Il exécute un ou plusieurs conteneurs.

Ce manifeste reprend les extraits précédents : identité, image, port, quotas de ressources et sonde de disponibilité. C'est le niveau de complétude attendu sur une question d'examen, pas seulement image et name.

apiVersion: v1
kind: Pod
metadata:
name: web
namespace: ckad
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx:1.30@sha256:d5792f71a9496b833bc08ea834a758c46e2b6a6306c10f4be926f38a656cdc1c
ports:
- containerPort: 80
resources:
requests:
cpu: "100m"
memory: "128Mi"
limits:
cpu: "500m"
memory: "256Mi"
readinessProbe:
httpGet:
path: /
port: 80
initialDelaySeconds: 5
periodSeconds: 10
Fenêtre de terminal
kubectl apply -f pod.yaml
kubectl get pods -l app=nginx --show-labels
NAME READY STATUS RESTARTS AGE LABELS
web 1/1 Running 0 19s app=nginx

Un Deployment gère un ensemble de Pods identiques et permet les mises à jour progressives.

Par rapport au Pod précédent, la définition du conteneur descend d'un cran : elle vit désormais dans spec.template, le modèle que le Deployment reproduit autant de fois que replicas l'indique.

apiVersion: apps/v1
kind: Deployment
metadata:
name: web
namespace: ckad
labels:
app: nginx
spec:
replicas: 3
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx:1.30@sha256:d5792f71a9496b833bc08ea834a758c46e2b6a6306c10f4be926f38a656cdc1c
ports:
- containerPort: 80
resources:
requests:
cpu: "100m"
memory: "128Mi"
Fenêtre de terminal
kubectl apply -f deployment.yaml
kubectl get deployment
NAME READY UP-TO-DATE AVAILABLE AGE
web 3/3 3 3 10s

Le selector.matchLabels doit matcher exactement les labels du template.metadata.labels. Sinon le Deployment ne trouvera pas ses Pods.

Un Service expose un ensemble de Pods sur le réseau.

Le champ type détermine jusqu'où le Service est joignable. En son absence, Kubernetes applique ClusterIP, donc une exposition strictement interne au cluster. Les deux autres types élargissent progressivement la portée, NodePort en ouvrant un port sur chaque nœud, LoadBalancer en réclamant une IP publique au fournisseur cloud.

TypeUsageAccessibilité
ClusterIPTrafic interneDans le cluster uniquement
NodePortTests, accès externe simple<NodeIP>:<nodePort>
LoadBalancerProduction cloudIP externe via load balancer

Deux champs décident de tout : le selector, qui doit correspondre aux labels des Pods, et le couple port (celui du Service) et targetPort (celui du conteneur). Un selector qui ne matche rien produit un Service sans endpoints, joignable mais muet.

apiVersion: v1
kind: Service
metadata:
name: web
namespace: ckad
spec:
type: ClusterIP
selector:
app: nginx
ports:
- protocol: TCP
port: 80
targetPort: 80

La méthode la plus rapide pour créer un Service cohérent :

Fenêtre de terminal
kubectl expose deployment web --port=80 --target-port=80

Le selector est automatiquement aligné avec les labels du Deployment.

Une ConfigMap stocke des configurations non sensibles.

Une ConfigMap accepte deux usages dans le même objet : des paires clé-valeur courtes, destinées aux variables d'environnement, et des fichiers de configuration entiers introduits par le bloc | de YAML, destinés à être montés en volume.

apiVersion: v1
kind: ConfigMap
metadata:
name: app-config
namespace: ckad
data:
APP_ENV: production
APP_DEBUG: "false"
config.json: |
{
"database": "postgres",
"port": 5432
}

Le Pod ne cite plus les clés une par une, il référence la ConfigMap entière. Attention, l'injection a lieu au démarrage du conteneur : modifier la ConfigMap ensuite n'a aucun effet tant que le Pod n'est pas recréé.

apiVersion: v1
kind: Pod
metadata:
name: app
namespace: ckad
spec:
containers:
- name: app
image: busybox:1.36@sha256:73aaf090f3d85aa34ee199857f03fa3a95c8ede2ffd4cc2cdb5b94e566b11662
command: ["sleep", "3600"]
envFrom:
- configMapRef:
name: app-config

Toutes les clés de la ConfigMap deviennent des variables d'environnement.

Un Secret stocke des données sensibles. Utilisez stringData pour écrire en clair dans le manifest.

Le champ stringData accepte des valeurs en clair et l'API server les encode lui-même en base64 dans le champ data. C'est un champ d'écriture seule : une relecture avec kubectl get -o yaml ne renverra que data.

apiVersion: v1
kind: Secret
metadata:
name: db-credentials
namespace: ckad
type: Opaque
stringData:
username: admin
password: S3cr3tP@ssw0rd!

secretKeyRef désigne une clé précise d'un Secret et suppose que celui-ci existe déjà dans le même namespace. Sinon le Pod reste en CreateContainerConfigError jusqu'à ce que le Secret apparaisse.

apiVersion: v1
kind: Pod
metadata:
name: app
namespace: ckad
spec:
containers:
- name: app
image: busybox:1.36@sha256:73aaf090f3d85aa34ee199857f03fa3a95c8ede2ffd4cc2cdb5b94e566b11662
command: ["sleep", "3600"]
env:
- name: DB_USER
valueFrom:
secretKeyRef:
name: db-credentials
key: username
- name: DB_PASS
valueFrom:
secretKeyRef:
name: db-credentials
key: password

Un Secret exposé en variable d'environnement est pratique mais plus exposé : il est lisible dans /proc et remonte facilement dans les journaux. Le montage en volume reste préférable pour les données très sensibles.

Regroupez plusieurs ressources liées dans un seul fichier avec --- :

apiVersion: v1
kind: ConfigMap
metadata:
name: app-config
namespace: ckad
data:
APP_ENV: production
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: app
namespace: ckad
spec:
replicas: 2
selector:
matchLabels:
app: myapp
template:
metadata:
labels:
app: myapp
spec:
containers:
- name: app
image: nginx:1.30@sha256:d5792f71a9496b833bc08ea834a758c46e2b6a6306c10f4be926f38a656cdc1c
envFrom:
- configMapRef:
name: app-config
---
apiVersion: v1
kind: Service
metadata:
name: app
namespace: ckad
spec:
selector:
app: myapp
ports:
- port: 80
Fenêtre de terminal
kubectl apply -f app-stack.yaml

Toutes les ressources sont créées en une seule commande.

Réécrire un fichier puis le réappliquer coûte du temps quand il n'y a qu'un champ à changer. Trois commandes modifient la ressource directement dans le cluster, de la plus souple à la plus expéditive. Le revers est connu : ces modifications ne se retrouvent pas dans vos fichiers YAML, donc le prochain kubectl apply les écrasera.

C'est l'option la plus permissive : tout le manifest est éditable, y compris les champs que vous n'aviez pas prévu de toucher.

Fenêtre de terminal
kubectl edit deployment web

Ouvre le manifest dans votre éditeur. Les changements sont appliqués à la sauvegarde.

Le patch vise un chemin précis dans l'objet, sans ouvrir d'éditeur : il est donc scriptable. Avec --type merge, la portion JSON fournie est fusionnée dans la ressource existante, et tout le reste est laissé intact.

Fenêtre de terminal
# Changer le nombre de réplicas
kubectl patch deployment web --type merge -p '{"spec":{"replicas":5}}'
# Changer l'image
kubectl patch deployment web --type merge \
-p '{"spec":{"template":{"spec":{"containers":[{"name":"nginx","image":"nginx:1.30"}]}}}}'

Pour les modifications les plus fréquentes, kubectl set évite d'écrire le JSON à la main. Sur un Deployment, changer l'image déclenche immédiatement un rolling update.

Fenêtre de terminal
# Changer l'image
kubectl set image deployment/web nginx=nginx:1.30
# Changer les resources
kubectl set resources deployment/web -c=nginx --limits=cpu=500m,memory=256Mi
SituationCommande
Modification complexekubectl edit
Un seul champkubectl patch ou kubectl set
Changer l'imagekubectl set image

Trois niveaux de vérification existent, du moins coûteux au plus complet, et ils ne détectent pas les mêmes erreurs. La validation côté client attrape les fautes de syntaxe, la validation côté serveur attrape les champs inconnus et les refus d'admission, kubectl diff montre ce qui va changer sur une ressource déjà déployée.

Rien n'est créé et le manifest n'atteint jamais l'étape d'admission du cluster.

Fenêtre de terminal
kubectl apply --dry-run=client -f mon-manifest.yaml

Vérifie la syntaxe YAML et la structure de base. Rapide mais limité.

Le manifest fait cette fois l'aller-retour complet, mais rien n'est persisté dans etcd.

Fenêtre de terminal
kubectl apply --dry-run=server -f mon-manifest.yaml

Envoie le manifest à l'API server pour validation complète :

  • Vérifie les admission controllers
  • Détecte les champs inconnus
  • Valide les contraintes (quotas, policies...)

Ce que le client laisse passer et que le serveur arrête

Section intitulée « Ce que le client laisse passer et que le serveur arrête »

La différence entre les deux n'est pas théorique, et un seul manifeste suffit à la rendre évidente. Celui-ci demande plus de mémoire qu'il ne s'en autorise, ce qui est incohérent :

faux.yaml
resources:
limits:
memory: "128Mi"
requests:
memory: "256Mi"

Le client ne voit rien, parce que la syntaxe est parfaite :

Fenêtre de terminal
kubectl apply --dry-run=client -f faux.yaml
Sortie
pod/faux created (dry run)

Le serveur, lui, refuse, parce que la règle qui lie requests et limits vit dans l'API et pas dans le fichier :

Fenêtre de terminal
kubectl apply --dry-run=server -f faux.yaml
Sortie
The Pod "faux" is invalid: spec.containers[0].resources.requests:
Invalid value: "256Mi": must be less than or equal to memory limit of 128Mi

Retenez la règle : le client valide la forme, le serveur valide le sens. En examen comme en production, c'est le second qui vous évite de perdre du temps.

Cette commande n'a de sens que si la ressource existe déjà dans le cluster : sur une ressource inconnue, elle affiche simplement une création complète.

Fenêtre de terminal
kubectl diff -f mon-manifest.yaml

Compare le manifest local avec la ressource existante :

replicas: 3
replicas: 5

Son code de retour en fait un outil de pipeline autant qu'un outil d'examen : 0 quand rien ne change, 1 dès qu'il existe une différence. Une étape de CI peut donc refuser un déploiement qui n'a pas été relu.

Une nuance à connaître avant de bâtir une garde là-dessus : une ressource qui n'existe pas encore compte comme une différence, et rend elle aussi 1. Un pipeline qui traite ce code comme une anomalie échouera donc sur toute première création, ce qui n'en est pas une.

Avant d'appliquer :

  1. --dry-run=client pour la syntaxe, réponse instantanée
  2. --dry-run=server pour ce que seule l'API sait juger
  3. kubectl diff si la ressource existe déjà
  4. kubectl apply en dernier, une fois les trois précédents propres

Ces erreurs ont un point commun : elles ne produisent aucun message clair. La ressource est acceptée, le Pod démarre parfois, et c'est le comportement observé qui ne correspond pas à l'attendu. Les reconnaître d'un coup d'œil vaut plusieurs minutes gagnées par question.

ErreurConséquenceSolution
Oublier le namespaceRessource dans defaultChanger le namespace courant
Mauvais label Service/PodService sans EndpointsAligner les selectors
port vs targetPort inversésConnexion impossibleport = Service, targetPort = conteneur
Indentation YAML incorrecteErreur de parsingUtiliser 2 espaces, pas de tabs
Valeur nue interprétée comme un booléencannot unmarshal boolMettre la valeur entre guillemets
Modifier selector d'un DeploymentErreur "immutable"Supprimer et recréer
Champ au mauvais niveauChamp ignorékubectl explain pour vérifier
ConfigMap dans un autre namespacenot foundMême namespace que le Pod

Exemple de piège : une valeur qui n'est pas celle que vous avez écrite

Section intitulée « Exemple de piège : une valeur qui n'est pas celle que vous avez écrite »

YAML interprète huit mots nus comme des booléens : y, n, yes, no, on, off, true et false. Dans un champ que Kubernetes attend en chaîne de caractères, le manifeste est alors rejeté, et le message ne parle pas de YAML :

containers:
- name: n # ❌ YAML lit le booléen false, pas la lettre « n »
image: nginx:1.30
Sortie
Deployment in version "v1" cannot be handled as a Deployment:
json: cannot unmarshal bool into Go struct field Container.spec.containers.name
of type string

Le mot null appartient à la même famille mais produit une erreur différente, plus déroutante encore, puisqu'elle prétend que le champ est absent : spec.containers[0].name: Required value.

La parade tient en un caractère, les guillemets, et elle vaut pour tout champ libre : nom de conteneur, valeur de label, variable d'environnement.

containers:
- name: "n" # accepté

Le cas se rencontre pour de bon avec les codes pays : no pour la Norvège devient false dans un label ou une variable d'environnement, sans que rien ne le signale si le champ accepte les booléens.

Le selector d'un Deployment est immuable après création : aucun patch, aucun apply ne le fera changer. Le seul chemin est la suppression suivie d'une recréation, ce qui interrompt le service le temps du remplacement.

Fenêtre de terminal
# Impossible de modifier le selector d'un Deployment existant
kubectl patch deployment web --type merge \
-p '{"spec":{"selector":{"matchLabels":{"app":"newapp"}}}}'

L'API rend deux erreurs d'un coup, et la première surprend : en changeant le selector sans toucher aux labels du template, vous avez aussi cassé leur correspondance.

Sortie
The Deployment "web" is invalid:
* spec.template.metadata.labels: Invalid value: {"app":"web"}:
`selector` does not match template `labels`
* spec.selector: Invalid value: {"matchLabels":{"app":"newapp"}}: field is immutable

Solution : supprimer et recréer le Deployment.

Fenêtre de terminal
kubectl delete deployment web
kubectl apply -f web.yaml # avec le nouveau selector

Ces contrôles se passent avant d'appliquer, et ils attrapent les erreurs que Kubernetes accepterait sans broncher. Un manifeste syntaxiquement valide peut très bien créer un Service qui ne route vers rien ou un Deployment qui ne démarre jamais : la validation de l'API ne juge pas la cohérence de vos intentions.

Avant chaque kubectl apply :

  • Namespace : suis-je dans le bon namespace ?
  • Labels : sont-ils cohérents entre Deployment, Pod et Service ?
  • Image : nom et tag corrects ?
  • Ports : port et targetPort alignés ?
  • Resources : requests ≤ limits ?
  • Probes : path et port corrects ?
  • Volumes : même nom entre volumes et volumeMounts ?

Voici les cinq ressources manipulées dans ce guide, avec les champs sur lesquels se concentrer quand il faut écrire vite. La colonne « Champs clés » correspond à ce qu'un correcteur d'examen vérifie en premier.

RessourceRôleChamps clés
PodExécuter des conteneurscontainers[].image, ports, resources
DeploymentGérer des réplicasreplicas, selector, template
ServiceExposer des Podsselector, ports, type
ConfigMapConfiguration non sensibledata (clé/valeur ou fichier)
SecretDonnées sensiblesstringData (écriture) ou data (base64)

Ce bloc rassemble les commandes du guide dans l'ordre de travail : explorer, générer, valider, corriger, diagnostiquer. Entraînez-vous à les taper sans les relire : la vitesse de frappe fait partie de l'épreuve.

Fenêtre de terminal
# Explorer l'API
kubectl api-resources
kubectl explain deployment.spec.template.spec.containers
# Générer des manifests
kubectl create deployment web --image=nginx:1.30@sha256:d5792f71a9496b833bc08ea834a758c46e2b6a6306c10f4be926f38a656cdc1c \
--dry-run=client -o yaml > web.yaml
kubectl expose deployment web --port=80 --dry-run=client -o yaml > svc.yaml
# Valider
kubectl apply --dry-run=client -f manifest.yaml
kubectl apply --dry-run=server -f manifest.yaml
kubectl diff -f manifest.yaml
# Corriger rapidement
kubectl edit deployment web
kubectl patch deployment web --type merge -p '{"spec":{"replicas":5}}'
kubectl set image deployment/web nginx=nginx:1.30
# Débugger
kubectl get pods -A # tous les namespaces
kubectl describe pod web
kubectl logs web
kubectl exec -it web -- sh
  1. Workflow examen : namespace → générer → compléter → valider → appliquer
  2. kubectl explain documente chaque champ depuis le cluster
  3. --dry-run=client -o yaml génère un squelette à compléter
  4. kubectl expose crée un Service avec les bons labels automatiquement
  5. Labels cohérents entre Deployment, Pod template et Service selector
  6. Le client valide la forme, le serveur valide le sens : --dry-run=server est le seul à voir requests supérieurs aux limits
  7. kubectl diff rend 0 sans différence et 1 avec, donc utilisable en CI
  8. kubectl edit et kubectl patch pour corriger rapidement
  9. Multi-document YAML (---) pour regrouper les ressources liées
  10. Namespace courant : le changer évite les erreurs de cible
  11. Le selector d'un Deployment est immuable : suppression et recréation
  12. stringData dans les Secrets évite l'encodage base64 manuel

Sept questions pour vérifier que les réflexes sont acquis : structure d'un manifest, cohérence des labels et choix de la bonne commande de validation.

Contrôle de connaissances

Validez vos connaissances avec ce quiz interactif

7 questions
5 min.
80% requis

Informations

  • Le chronomètre démarre au clic sur Démarrer
  • Questions à choix multiples, vrai/faux et réponses courtes
  • Vous pouvez naviguer entre les questions
  • Les résultats détaillés sont affichés à la fin

Lance le quiz et démarre le chronomètre

  • Les Jobs et CronJobs : Deux ressources dont le manifeste réserve des pièges de champs immuables.
  • Définir les Probes : Les champs de sondes à ajouter dans chaque manifeste de conteneur destiné à la production.
  • Requests et Limits : Le dimensionnement CPU et mémoire, section qu'aucun manifeste de production ne devrait omettre.

Ce site vous est utile ?

Sachez que moins de 1% des lecteurs soutiennent ce site.

Je maintiens ce site gratuitement, sans publicité, sans profilage et sans compte à créer. Un soutien, même symbolique, m'aide à couvrir l'hébergement et à garder ces ressources gratuites. Merci pour votre appui.

Le formulaire ne s'affiche pas ? Ouvrir Ko-fi dans un onglet.

Abonnez-vous et suivez mon actualité DevSecOps sur LinkedIn