TP14 : Kubernetes avec K3s (cluster, objets de base, configuration, santé, stockage)

Kubernetes orchestre des conteneurs sur un ensemble de machines : il les démarre, les redémarre, les répartit, les expose et les met à l’échelle à partir d’un état désiré décrit en YAML. K3s est une distribution Kubernetes certifiée CNCF, légère (un seul binaire), idéale pour un lab : elle embarque containerd, CoreDNS, l’Ingress Traefik, un load balancer de service, le stockage local-path et metrics-server.

Ce TP réutilise Vagrant (TP5) et Ansible (TP4) pour monter un cluster à deux nœuds, puis fait découvrir les objets Kubernetes indispensables sur une application de démonstration (traefik/whoami).


Fichiers de départ : k3s-lab.zip, k8s-starter.zip

1. Objectifs pédagogiques

À l’issue du TP, vous serez capable de :

  • décrire l’architecture d’un cluster (control plane, nœuds, kubelet, API server, etcd/SQLite) ;
  • installer un cluster K3s multi-nœuds de façon reproductible avec Vagrant + Ansible ;
  • utiliser kubectl (get, describe, logs, exec, apply, rollout, port-forward) ;
  • écrire Namespace, Deployment, Service, Ingress, ConfigMap, Secret, PVC et HPA ;
  • configurer probes et requests / limits, et réaliser un rolling update et un rollback.

Les images de cette page sont des schémas explicatifs, pas des captures d’écran.

2. Environnement du TP

  • VM de lab Ubuntu Server 26.04 avec VirtualBox, Vagrant et Ansible (TP5), ≥ 4 vCPU et ≥ 8 Go de RAM, virtualisation imbriquée activée.

  • Installer kubectl sur la VM de lab :

    curl -LO "https://dl.k8s.io/release/$(curl -Ls https://dl.k8s.io/release/stable.txt)/bin/linux/amd64/kubectl"
    sudo install -m 0755 kubectl /usr/local/bin/kubectl && kubectl version --client
  • Ajouter dans /etc/hosts de la VM de lab : 192.168.56.10 whoami.lab.local.

Plan B si la RAM manque : installer K3s directement sur la VM de lab (curl -sfL https://get.k3s.io | sh -). Les parties 2 à 7 fonctionnent à l’identique sur un nœud unique.

Cluster K3s : la VM de lab pilote un server et un agent créés par Vagrant Cluster K3s : la VM de lab pilote un server et un agent créés par Vagrant

3. Partie 1 : Cluster K3s avec Vagrant + Ansible

  • Ouvrir k3s-lab/Vagrantfile : deux VM, et Ansible lancé une seule fois après la dernière VM, sur tout le cluster.

  • Compléter les TODO de k3s-lab/playbook.yml :

    • installation idempotente du server (creates: /usr/local/bin/k3s) avec --node-ip, --tls-san et --write-kubeconfig-mode 644 ;
    • lecture du jeton du server (slurp), puis installation de l’agent avec K3S_URL et K3S_TOKEN ;
    • rapatriement du fichier k3s.yaml sur la VM de lab, avec l’IP du server à la place de 127.0.0.1.
  • Créer le cluster et le vérifier :

    cd k3s-lab && vagrant up
    mkdir -p ~/.kube && cp k3s.yaml ~/.kube/config && chmod 600 ~/.kube/config
    kubectl get nodes -o wide          # 2 nœuds Ready
    kubectl get pods -A                # coredns, traefik, local-path, metrics-server
  • Transformer le playbook en rôle k3s (roles/k3s/tasks/{server,agent}.yml, defaults/main.yml avec la version de K3s épinglée via INSTALL_K3S_VERSION).

  • Détruire et recréer le cluster uniquement avec vagrant destroy -f && vagrant up. Noter le temps nécessaire.

Composant Rôle
API server Point d’entrée unique : kubectl et tous les composants lui parlent.
Datastore État du cluster (SQLite par défaut avec K3s, etcd en haute disponibilité).
Scheduler Choisit le nœud de chaque Pod selon les ressources demandées.
Controllers Boucles qui rapprochent l’état réel de l’état désiré (ReplicaSet, Deployment…).
kubelet Agent de chaque nœud : démarre les conteneurs via containerd.

4. Partie 2 : Pods et Deployments

Objets du TP : Ingress, Service, Pods, Deployment, ReplicaSet, ConfigMap, Secret, PVC, HPA Objets du TP : Ingress, Service, Pods, Deployment, ReplicaSet, ConfigMap, Secret, PVC, HPA

  • Lancer un Pod « à la main » puis le supprimer : est-il recréé ? Pourquoi ?

    kubectl run test --image=traefik/whoami:v1.10.0
    kubectl get pods -o wide && kubectl delete pod test
  • Appliquer le Namespace et le Deployment de k8s-starter/ :

    kubectl apply -f k8s-starter/01-namespace.yaml -f k8s-starter/03-deployment.yaml
    kubectl -n tp14 get deploy,rs,pods -o wide
  • Supprimer un Pod du Deployment et observer sa recréation (kubectl -n tp14 get pods -w). Sur quels nœuds sont répartis les Pods ?

  • Explorer : kubectl describe pod, kubectl logs, kubectl exec -it … -- sh (l’image whoami n’a pas de shell : que se passe-t-il ?), kubectl explain deployment.spec.strategy.

5. Partie 3 : Services et Ingress

  • Compléter le selector de 04-service.yaml, appliquer, puis vérifier les endpoints :

    kubectl -n tp14 get svc,endpointslices
    kubectl -n tp14 port-forward svc/whoami 8080:80 &
    curl -s localhost:8080 | grep Hostname     # relancer : le Pod change
  • Tester la résolution DNS interne depuis un Pod temporaire :

    kubectl -n tp14 run tmp --rm -it --image=busybox:1.37 --restart=Never -- \
      wget -qO- http://whoami.tp14.svc.cluster.local
  • Appliquer 05-ingress.yaml puis curl http://whoami.lab.local. Compléter le TODO : un Deployment + Service nginx servi sous /api.

Type de Service Accès
ClusterIP Interne au cluster uniquement (par défaut).
NodePort Port 30000–32767 ouvert sur chaque nœud.
LoadBalancer IP externe (fournie par ServiceLB sur K3s, par le cloud ailleurs).
Ingress Routage HTTP par nom d’hôte et chemin vers des Services ClusterIP.

6. Partie 4 : ConfigMap et Secret

  • Appliquer 02-config.yaml et créer le Secret en ligne de commande (il ne doit pas être versionné) :

    kubectl -n tp14 create secret generic whoami-secret --from-literal=api-key='lab-only-value'
    kubectl -n tp14 get secret whoami-secret -o yaml       # la valeur est en base64
    echo '<valeur>' | base64 -d                             # base64 n'est PAS du chiffrement
  • Injecter la ConfigMap (envFrom) et la clé du Secret (secretKeyRef) dans le Deployment, puis vérifier dans la page renvoyée par whoami (variables d’environnement affichées avec ?env=true).

  • Modifier la ConfigMap : la valeur change-t-elle dans les Pods existants ? Que faut-il faire (kubectl rollout restart) ?

7. Partie 5 : Santé, ressources et déploiements progressifs

  • Ajouter une readinessProbe et une livenessProbe HTTP sur /health, ainsi que des requests / limits.

  • Simuler une panne : whoami permet de forcer le code de /health :

    kubectl -n tp14 port-forward deploy/whoami 8081:8080 &
    curl -X POST -d '500' localhost:8081/health
    kubectl -n tp14 get pods -w     # le Pod sort des endpoints puis redémarre (RESTARTS)
  • Réaliser un rolling update puis un rollback :

    kubectl -n tp14 set image deploy/whoami whoami=traefik/whoami:v1.10.1
    kubectl -n tp14 rollout status deploy/whoami
    kubectl -n tp14 set image deploy/whoami whoami=traefik/whoami:v9.9.9   # image inexistante
    kubectl -n tp14 get pods                     # ImagePullBackOff, l'ancienne version reste servie
    kubectl -n tp14 rollout history deploy/whoami
    kubectl -n tp14 rollout undo deploy/whoami
  • Expliquer pourquoi le service n’a jamais été interrompu (maxUnavailable, maxSurge, readinessProbe).

  • Mettre un limits.memory très bas (4Mi) : observer l’état OOMKilled avec kubectl describe pod.

8. Partie 6 : Stockage persistant

  • Créer le Secret postgres-secret, compléter le montage du PVC dans 06-postgres.yaml et appliquer.
  • Créer une table et y insérer une ligne (kubectl exec -it deploy/postgres -- psql -U postgres), supprimer le Pod, puis vérifier que la donnée est toujours là.
  • kubectl get pv,pvc -n tp14 : où sont physiquement stockées les données avec local-path ? Quelle limite cela pose-t-il si le Pod change de nœud ?

9. Partie 7 : HorizontalPodAutoscaler

  • Vérifier metrics-server (kubectl top nodes, kubectl top pods -n tp14), puis appliquer 07-hpa.yaml.

  • Générer de la charge et observer la montée puis la descente du nombre de replicas :

    kubectl -n tp14 run load --rm -it --image=busybox:1.37 --restart=Never -- \
      sh -c 'while true; do wget -qO- http://whoami >/dev/null; done'
    kubectl -n tp14 get hpa -w

Défi

  1. Ajouter un troisième nœud au cluster en ne modifiant que le tableau NODES du Vagrantfile, puis kubectl drain un nœud et observer le déplacement des Pods.
  2. Ajouter un PodDisruptionBudget (minAvailable: 1) et vérifier qu’il bloque un drain qui supprimerait tous les Pods.

10. Livrables

Captures à déposer dans captures/ :

Fichier Contenu attendu
cluster-nodes.png kubectl get nodes -o wide (2 nœuds Ready) et get pods -A
rebuild.png Cluster recréé avec vagrant destroy -f && vagrant up
ingress.png curl http://whoami.lab.local et /api
probes-restart.png Pod redémarré par la livenessProbe
rollout-undo.png rollout history puis rollout undo après l’image inexistante
pvc-persistence.png Donnée conservée après suppression du Pod PostgreSQL
hpa.png kubectl get hpa -w montrant la mise à l’échelle

Ainsi que : Vagrantfile, rôle Ansible k3s, manifests complétés. Pas de k3s.yaml (kubeconfig) ni de Secret dans Git.