Ce document rassemble une série de questions et scénarios types que vous pourriez rencontrer lors de l'examen CKA (Certified Kubernetes Administrator). L'objectif est de vous familiariser avec les tâches courantes et les commandes kubectl
essentielles.
Il est fortement recommandé de configurer quelques alias et options dès le début de l'examen pour gagner du temps.
alias k=kubectl # Alias court pour kubectl (généralement pré-configuré)
export do="--dry-run=client -o yaml" # Pour générer rapidement le YAML d'une ressource sans la créer
export now="--force --grace-period=0" # Pour forcer la suppression immédiate d'un Pod (à utiliser avec prudence)
# Optionnel mais utile : définir le namespace par défaut pour un contexte donné
# k config set-context --current --namespace=<nom-namespace>
Pour l'éditeur vim
(si vous l'utilisez), ajouter ceci au fichier ~/.vimrc
améliore la lisibilité du YAML :
set tabstop=2 # Taille d'une tabulation
set expandtab # Utiliser des espaces au lieu des tabulations
set shiftwidth=2 # Nombre d'espaces pour l'indentation automatique
autocmd FileType yaml setlocal ts=2 sts=2 sw=2 expandtab # Applique spécifiquement pour les fichiers YAML
Dans cette liste de questions, j'ai mis des questions typiques qui m'ont posé problème durant l'examen blanc.
Objectif : Créer un Pod nommé nginx-master
utilisant l'image nginx:1.25
(ou une version récente). Ce Pod ne doit pouvoir être schedulé que sur un nœud ayant le rôle control-plane
ou master
.
Étapes de résolution :
Identifier les Taints et Labels des nœuds master : Les nœuds master ont souvent un taint (ex: node-role.kubernetes.io/control-plane:NoSchedule
ou node-role.kubernetes.io/master:NoSchedule
) pour empêcher les Pods normaux d'y être schedulés, et un label correspondant (ex: node-role.kubernetes.io/control-plane=
ou node-role.kubernetes.io/master=
).
# Examiner un nœud master (remplacer master1 par le nom réel)
k describe node master1
# Rechercher spécifiquement les Taints
k describe node master1 | grep Taints
# Exemple de sortie: Taints: node-role.kubernetes.io/control-plane:NoSchedule
# Rechercher spécifiquement les Labels
k describe node master1 | grep -A 5 "Labels:"
# Exemple de sortie pertinente: node-role.kubernetes.io/control-plane=
# node-role.kubernetes.io/master=
Créer le manifeste du Pod : Le Pod aura besoin :
toleration
pour le taint NoSchedule
trouvé.nodeSelector
(ou nodeAffinity
) pour cibler le label du nœud master.apiVersion: v1
kind: Pod
metadata:
name: nginx-master
spec:
containers:
- name: nginx-container
image: nginx:1.25 # Utiliser une version récente
tolerations:
# Tolérer le taint appliqué aux nœuds control-plane/master
- key: "node-role.kubernetes.io/control-plane"
operator: "Exists"
effect: "NoSchedule"
# Alternative si le taint est node-role.kubernetes.io/master
- key: "node-role.kubernetes.io/master"
operator: "Exists"
effect: "NoSchedule"
nodeSelector:
# Exiger le label présent sur les nœuds control-plane/master
node-role.kubernetes.io/control-plane: ""
# Alternative:
# node-role.kubernetes.io/master: ""
Note : L'opérateur Exists
pour la toleration correspond à un taint sans valeur ou avec n'importe quelle valeur. Le nodeSelector
avec une valeur vide ""
correspond à un label présent sans valeur spécifique assignée (ce qui est courant pour les labels de rôle).
Appliquer le manifeste :
k apply -f pod-master.yaml # En supposant que le YAML est dans ce fichier
Vérifier le placement du Pod :
k get pod nginx-master -o wide
# Vérifier que le Pod est Running et que la colonne NODE correspond à un nœud master.
Afficher l'utilisation des noeuds :
kubectl top node
Afficher l'utilisation des pods ET des containers s'il y en a :
kubectl top pod --containers=true
Objectif : Déterminer comment les composants principaux (kubelet
, kube-apiserver
, kube-scheduler
, kube-controller-manager
, etcd
) et le service DNS sont installés et exécutés sur un nœud master du cluster.
Méthodologie :
Identifier le mode d'exécution du Kubelet : Le Kubelet est presque toujours exécuté comme un service systemd directement sur le nœud.
# Se connecter au nœud master (ex: ssh cluster1-master1)
systemctl status kubelet
# ou
ps aux | grep kubelet
Résultat probable : process
(géré par systemd).
Identifier le mode d'exécution des autres composants du Control Plane : Dans un cluster kubeadm
, ces composants (kube-apiserver
, etcd
, kube-scheduler
, kube-controller-manager
) sont généralement exécutés comme des Pods Statiques gérés par le Kubelet du nœud master.
/etc/kubernetes/manifests/
).ls /etc/kubernetes/manifests/
# Sortie attendue : etcd.yaml, kube-apiserver.yaml, kube-controller-manager.yaml, kube-scheduler.yaml
kube-system
. Les Pods Statiques auront le nom du nœud master ajouté comme suffixe.# Depuis n'importe où avec kubectl configuré
k get pods -n kube-system -o wide | grep <nom-du-noeud-master>
# Sortie attendue :
# etcd-<nom-master> 1/1 Running ...
# kube-apiserver-<nom-master> 1/1 Running ...
# kube-controller-manager-<nom-master> 1/1 Running ...
# kube-scheduler-<nom-master> 1/1 Running ...
Résultat probable : static-pod
pour ces 4 composants.
Identifier le mode d'exécution du DNS : Le service DNS (généralement CoreDNS) est typiquement exécuté comme un Deployment standard dans le namespace kube-system
.
# Lister les Deployments dans kube-system
k get deployment -n kube-system
# Sortie attendue (contient généralement 'coredns') :
# NAME READY UP-TO-DATE AVAILABLE AGE
# coredns 2/2 2 2 155m
# Confirmer avec les Pods
k get pods -n kube-system -l k8s-app=kube-dns # Label standard pour le DNS
# Sortie attendue :
# NAME READY STATUS RESTARTS AGE
# coredns-xxxx-yyyy 1/1 Running 0 155m
# coredns-xxxx-zzzz 1/1 Running 0 155m
Résultat probable : pod
(géré par un Deployment), Nom : coredns
.
Exemple de fichier réponse (/opt/response/X/master-components.txt
) :
kubelet: process
kube-apiserver: static-pod
kube-scheduler: static-pod
kube-controller-manager: static-pod
etcd: static-pod
dns: pod coredns # Ou juste 'pod' si le nom n'est pas demandé
(Note : Si une question mentionne un composant spécifique comme kube-scheduler-special
, il faut vérifier son état de la même manière)
Connectez-vous au nœud maître avec ssh cluster2-master1
. Arrêtez temporairement le kube-scheduler, de manière à pouvoir le relancer par la suite.
Créez un Pod unique nommé manual-schedule
de l'image httpd:2.4-alpine
, confirmez sa création mais ne le schedule sur aucun noeud.
Maintenant que vous êtes le scheduler et que vous avez tout son pouvoir, planifiez manuellement ce pod sur le nœud cluster1-master1
. Assurez-vous qu'il est en cours d'exécution.
Redémarrez le kube-scheduler et confirmez son fonctionnement correct en créant un second pod nommé manual-schedule2 de l'image httpd:2.4-alpine et vérifiez qu'il fonctionne sur cluster2-worker1.
# On va aller déplacer le manifest du static-pod kube-scheduler :
$ cd /etc/kubernetes/manifests/
$ mv kube-scheduler.yaml ../
# Cela va stopper le static-pod
Si on vérifier :
$ kubectl -n kube-system get pod | grep scheduler-master
Rien ne ressort car le pod s'est stoppé. On va maintenant lancer un pod, mais celui-ci ne sera pas assigné :
$ k run manual-schedule --image=httpd:2.4-alpine
$ k get pod manual-schedule -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
manual-schedule 0/1 Pending 0 17s <none> <none> <none> <none>
On va donc récupérer le yaml de ce pod :
$ k get pod manual-schedule -o yaml > manual-schedule.yaml
En réalité, le scheduler rajoute juste le nom de noeud (nodeName
) dans le YAML :
apiVersion: v1
kind: Pod
metadata:
creationTimestamp: "2022-03-20T15:26:44Z"
labels:
run: manual-schedule
name: manual-schedule
namespace: default
resourceVersion: "30688"
uid: 23f5daf4-7aed-437e-90f5-122a5f173481
spec:
nodeName: cluster1-master1 # ajouter le nom du noeud master
containers:
- image: httpd:2.4-alpine
imagePullPolicy: IfNotPresent
name: manual-schedule
...
On ne peut pas simplement appliquer le fichier :
$ k apply -f manual-schedule.yaml
Warning: resource pods/manual-schedule is missing the kubectl.kubernetes.io/last-applied-configuration annotation which is required by kubectl apply. kubectl apply should only be used on resources created declaratively by either kubectl create --save-config or kubectl apply. The missing annotation will be patched automatically.
On va donc forcer la recréation du pod :
$ k -f manual-schedule.yaml replace --force
pod "manual-schedule" deleted
pod/manual-schedule replaced
On va maintenant relancer le kube-scheduler
:
$ cd /etc/kubernetes/
$ mv kube-scheduler.yaml manifests/
Créer un nouveau ServiceAccount au nom de processor
dans le Namespace project-hamster
. Créez un Role et un RoleBinding, tous deux nommés processor
également. Ceux-ci devraient permettre au nouveau ServiceAccount de créer uniquement des Secrets et des ConfigMaps dans ce Namespace.
Tips: Plutot que de tout faire en yaml. Faire via CLI :
$ k -n project-hamster create role processor --verb=create --resource=secrets --resource=configmaps
RoleBinding :
$ k -n project-hamster create rolebinding processor --role processor --serviceaccount project-hamster:processor
Pour tester notre configuration RBAC, nous pouvons utiliser kubectl auth can-i
:
$ k -n project-hamster auth can-i -h
...
$ k -n project-hamster auth can-i create secret --as system:serviceaccount:project-hamster:processor
yes
$ k -n project-hamster auth can-i get secret --as system:serviceaccount:project-hamster:processor
no
Le but est de lancer un déploiement avec 3 réplicas. 1 pod maximum par noeud ! (l'équivalent d'un daemonset via un deployment) :
apiVersion: apps/v1
kind: Deployment
metadata:
creationTimestamp: null
labels:
id: very-important
name: deploy-important
namespace: project-tiger
spec:
replicas: 3
selector:
matchLabels:
id: very-important
strategy: {}
template:
metadata:
creationTimestamp: null
labels:
id: very-important
spec:
containers:
- image: nginx:1.17.6-alpine
name: container1
affinity:
# Ici, si un pod avec l'id "very-important" tourne déjà sur le noeud, on en démarre pas un 2ème
podAntiAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
- labelSelector:
matchExpressions:
- key: id
operator: In
values:
- very-important
topologyKey: kubernetes.io/hostname
Si on regarde le pod qui n'est pas schedule on voit :
Warning FailedScheduling 63s (x3 over 65s) default-scheduler 0/3 nodes are available: 1 node(s) had taint {node-role.kubernetes.io/master: }, that the pod didn't tolerate, 2 node(s) didn't match pod affinity/anti-affinity, 2 node(s) didn't satisfy existing pods anti-affinity rules.
Le déploiement peut aussi se faire via topologySpreadConstraints
, plus simple à comprendre pour ma part :
apiVersion: apps/v1
kind: Deployment
metadata:
labels:
id: very-important
name: deploy-important
namespace: project-tiger
spec:
replicas: 3
selector:
matchLabels:
id: very-important
template:
metadata:
labels:
id: very-important
spec:
containers:
- image: nginx:1.17.6-alpine
name: container1
topologySpreadConstraints:
- maxSkew: 1
topologyKey: kubernetes.io/hostname
whenUnsatisfiable: DoNotSchedule
labelSelector:
matchLabels:
id: very-important
Si on regarde le pod qui n'est pas schedule on voit :
Warning FailedScheduling 16s default-scheduler 0/3 nodes are available: 1 node(s) had taint {node-role.kubernetes.io/master: }, that the pod didn't tolerate, 2 node(s) didn't match pod topology spread constraints.
1/ et 2/ On en voit 1 master et 2 workers
$ k get node
NAME STATUS ROLES AGE VERSION
cluster1-master1 Ready master 27h v1.32.1
cluster1-worker1 Ready <none> 27h v1.32.1
cluster1-worker2 Ready <none> 27h v1.32.1
3/ Pour voir cela, il faut se connecter sur le master et aller dans les fichiers de conf de kube-apiserver
. 10.96.0.0/12 est la réponse.
$ cat /etc/kubernetes/manifests/kube-apiserver.yaml | grep range
- --service-cluster-ip-range=10.96.0.0/12
4/ En regardant dans le dossier de conf du cni, on voit que c'est weave
qui est utilisé :
$ find /etc/cni/net.d/
/etc/cni/net.d/
/etc/cni/net.d/10-weave.conflist
$ cat /etc/cni/net.d/10-weave.conflist
{
"cniVersion": "0.3.0",
"name": "weave",
...
5/ Le suffixe sera -cluster1-worker1
.
Créez un nouveau namespace appelé cka-master
.
Écrivez les noms de toutes les ressources Kubernetes qui peuvent contenir des namespaces (comme Pod, Secret, ConfigMap...) dans /opt/course/16/resources.txt.
Trouvez le Namespace du projet-*
avec le plus grand nombre de Roles définis et écrivez son nom et le nombre de Roles dans /opt/course/16/crowded-namespace.txt.
$ k create ns cka-master
$ k api-resources --namespaced -o name > /opt/course/16/resources.txt
Ensuite on va lister les roles et connaitre le nombre par namespace :
$ k -n project-c13 get role --no-headers | wc -l
No resources found in project-c13 namespace.
0
$ k -n project-c14 get role --no-headers | wc -l
300
$ k -n project-hamster get role --no-headers | wc -l
No resources found in project-hamster namespace.
0
$ k -n project-snake get role --no-headers | wc -l
No resources found in project-snake namespace.
0
$ k -n project-tiger get role --no-headers | wc -l
No resources found in project-tiger namespace.
0
Etonnemment je ne l'avais jamais vu jusque là :
$ kubeadm token create --print-join-command
kubeadm join 192.168.100.31:6443 --token leqq1l.1hlg4rw8mu7brv73 --discovery-token-ca-cert-hash sha256:2e2c3407a256fc768f0d8e70974a8e24d7b9976149a79bd08858c4d7aa2ff79a
Il peut être demandé d'écrire la date d'expiration dans un fichier txt. Pour ce faire :
$ openssl x509 -noout -text -in /etc/kubernetes/pki/etcd/server.crt | grep Validity -A2
Validity
Not Before: Sep 13 13:01:31 2021 GMT
Not After : Sep 13 13:01:31 2022 GMT