Aller au contenu

Module 9 : Kubernetes

Prérequis : Module 3 (Docker — containers, images), Module 2 (Réseau — services, ports)

En résumé : Tu passes de “1 serveur avec docker-compose” à un orchestrateur qui gère des dizaines de containers automatiquement. Kubernetes redémarre les containers qui crashent, répartit le traffic et permet de scaler en une commande.

Chemin alternatif : Ce module est optionnel. Le chemin principal du cursus est Modules 0 → 1 → 2 → 3 → 4 → 5 → 6. Kubernetes est une branche parallèle qui part du Module 3 (Docker). Tu n’as pas besoin d’avoir fait les Modules 5-7 pour le suivre. Si tu débutes, concentre-toi sur le chemin principal et reviens ici plus tard.

Le problème : Tu as 1 serveur avec docker-compose, ça marche. Mais si tu as 50 containers sur 10 serveurs ? Qui redémarre un container qui crash à 3h du matin ? Qui répartit le traffic entre les containers ? Qui fait un deployment sans downtime ?

Kubernetes (K8s) est un orchestrateur — un programme qui coordonne et gère automatiquement un grand nombre de containers sur plusieurs machines. Tu lui dis “je veux 3 copies de mon backend qui tournent en permanence”, et il se débrouille : où les placer, les redémarrer si elles crashent (self-healing = auto-réparation), répartir le traffic entre elles.

Les analogies (restaurant) :

  • Pod = un cuisinier à son poste
  • Deployment = “garde toujours 3 cuisiniers pasta en service”
  • Service = le serveur qui route les commandes aux bons cuisiniers
  • Node = une cuisine (un serveur physique/virtuel)
  • Un cuisinier tombe malade ? Le manager en embauche un nouveau automatiquement.
┌─── Control Plane (le cerveau) ─────────────────┐
│ API Server = le réceptionniste │
│ Scheduler = celui qui assigne les tâches │
│ etcd = le carnet d'adresses │
│ Controller = celui qui vérifie que tout va │
└─────────────────────────────────────────────────┘
├── Node 1 (une machine)
│ ├── Pod (backend)
│ └── Pod (frontend)
└── Node 2 (une machine)
├── Pod (backend)
└── Pod (backend)
  • Control Plane : décide OÙ placer les pods, surveille tout
  • Node : une machine qui fait tourner les pods
  • kubelet : l’agent sur chaque node qui communique avec le control plane

Minikube crée un cluster Kubernetes local (un seul node) pour apprendre.

Fenêtre de terminal
# Installer kubectl (le client)
# -L = suivre les redirections, -O = sauvegarder avec le nom original du fichier
# $(...) = exécuter la commande entre parenthèses et utiliser le résultat (récupère la dernière version)
curl -LO "https://dl.k8s.io/release/$(curl -L -s https://dl.k8s.io/release/stable.txt)/bin/linux/amd64/kubectl"
# install = copier le fichier avec les bonnes permissions (exécutable par tous)
sudo install -o root -g root -m 0755 kubectl /usr/local/bin/kubectl
kubectl version --client
# Client Version: v1.x.x
# Installer minikube
curl -LO https://storage.googleapis.com/minikube/releases/latest/minikube-linux-amd64
sudo install minikube-linux-amd64 /usr/local/bin/minikube
# Lancer le cluster
minikube start
# 🎉 minikube v1.x.x
# ✅ Using the docker driver
# 🏄 Done! kubectl is now configured to use "minikube"
kubectl get nodes
# NAME STATUS ROLES AGE VERSION
# minikube Ready control-plane 1m v1.x.x

L’unité de base. Un pod = un ou plusieurs containers qui tournent ensemble. En pratique, 1 pod = 1 container.

Chaque objet Kubernetes est décrit en YAML avec 4 sections : apiVersion (version de l’API K8s), kind (le type d’objet), metadata (nom et labels), spec (la configuration).

# pod.yml (on n'en crée presque jamais directement — on utilise un Deployment)
apiVersion: v1 # Version de l'API Kubernetes (v1 = la plus basique)
kind: Pod # Type d'objet : un Pod
metadata:
name: mon-backend # Le nom du pod
spec:
containers: # Liste des containers dans ce pod (souvent 1 seul)
- name: backend
image: mon-user/devops-backend:latest
ports:
- containerPort: 8000 # Le port que le container écoute

Gère un groupe de pods identiques. Un replica = une copie de ton app. Si tu veux 2 replicas, K8s maintient 2 copies qui tournent en permanence. Si un pod crash → il en recrée un. Si tu veux 3 replicas → il en maintient 3.

backend-deployment.yml
apiVersion: apps/v1 # apps/v1 = l'API pour les Deployments (différent de v1 pour les Pods)
kind: Deployment
metadata:
name: backend
spec: # spec du DEPLOYMENT (combien de replicas, comment les trouver)
replicas: 2 # Nombre de copies de ton app à faire tourner
selector:
matchLabels:
app: backend # "Trouve les pods qui ont le label app=backend"
# Doit matcher EXACTEMENT les labels dans template ci-dessous
template: # Modèle pour créer chaque pod
metadata:
labels:
app: backend # Ce label est utilisé par le selector ci-dessus pour trouver les pods
spec: # spec du POD (quels containers, quels ports)
# Attention : il y a 2 "spec" — celui du Deployment et celui du Pod
containers:
- name: backend
image: mon-user/devops-backend:latest
ports:
- containerPort: 8000

Expose les pods sur le réseau. Même si les pods meurent et sont recréés (avec de nouvelles IPs), le Service garde une adresse stable.

backend-service.yml
apiVersion: v1
kind: Service
metadata:
name: backend-service
spec:
selector:
app: backend # Trouve les pods avec le label app=backend (du Deployment ci-dessus)
ports:
- port: 8000 # Le port pour accéder au Service
targetPort: 8000 # Le port du container vers lequel le traffic est redirigé
# (souvent les mêmes, mais on pourrait mapper 80 → 8000 par exemple)
type: ClusterIP # Accessible uniquement dans le cluster (pas depuis l'extérieur)
Type de ServiceC’est quoi
ClusterIPAccessible uniquement dans le cluster (défaut)
NodePortAccessible depuis l’extérieur via un port sur le node
LoadBalancerCrée un load balancer externe (cloud)

Le concept de load balancer vient du Module 2 (Réseau). Kubernetes Services font la même chose : répartir le traffic entre les pods.

# ConfigMap = config non sensible
apiVersion: v1
kind: ConfigMap
metadata:
name: app-config
data:
APP_ENV: "production"
# Secret = config sensible (encodé en base64)
apiVersion: v1
kind: Secret
metadata:
name: app-secret
type: Opaque
data:
DB_PASSWORD: bW9ucGFzcw== # base64 de "monpass"
Fenêtre de terminal
# Appliquer un fichier YAML
kubectl apply -f backend-deployment.yml
# Voir les pods
kubectl get pods
# NAME READY STATUS RESTARTS AGE
# backend-6d4f5b7c9d-abc12 1/1 Running 0 1m
# backend-6d4f5b7c9d-def34 1/1 Running 0 1m
# Voir les deployments
kubectl get deployments
# Voir les services
kubectl get services
# Détails sur un pod
kubectl describe pod backend-6d4f5b7c9d-abc12
# Voir les logs
kubectl logs backend-6d4f5b7c9d-abc12
# Supprimer
kubectl delete -f backend-deployment.yml
# Scaler
kubectl scale deployment backend --replicas=5

Un namespace isole les ressources dans le cluster (comme des dossiers). Par défaut, tout est dans le namespace default.

Fenêtre de terminal
kubectl get namespaces
kubectl get pods -n kube-system # Voir les pods système
Fenêtre de terminal
mkdir -p ~/devops-k8s
cd ~/devops-k8s

Crée backend.yml :

apiVersion: apps/v1
kind: Deployment
metadata:
name: backend
spec:
replicas: 2
selector:
matchLabels:
app: backend
template:
metadata:
labels:
app: backend
spec:
containers:
- name: backend
image: mon-user/devops-backend:latest
ports:
- containerPort: 8000
---
apiVersion: v1
kind: Service
metadata:
name: backend-service
spec:
selector:
app: backend
ports:
- port: 8000
targetPort: 8000
type: NodePort

Crée frontend.yml :

apiVersion: apps/v1
kind: Deployment
metadata:
name: frontend
spec:
replicas: 2
selector:
matchLabels:
app: frontend
template:
metadata:
labels:
app: frontend
spec:
containers:
- name: frontend
image: mon-user/devops-frontend:latest
ports:
- containerPort: 80
---
apiVersion: v1
kind: Service
metadata:
name: frontend-service
spec:
selector:
app: frontend
ports:
- port: 80
targetPort: 80
type: NodePort

Minikube a son propre registre d’images, séparé de ton Docker local. Si tes images ne sont pas sur Docker Hub, tu dois les charger manuellement :

Fenêtre de terminal
# Option A : charger des images locales
docker build -t mon-user/devops-backend:latest ~/devops-project/backend
docker build -t mon-user/devops-frontend:latest ~/devops-project/frontend
minikube image load mon-user/devops-backend:latest
minikube image load mon-user/devops-frontend:latest
# Option B : si tes images sont sur Docker Hub, K8s les télécharge automatiquement
# (il faut que le nom dans le YAML corresponde à l'image sur Docker Hub)
Fenêtre de terminal
kubectl apply -f backend.yml
kubectl apply -f frontend.yml
kubectl get pods
# NAME READY STATUS RESTARTS AGE
# backend-xxx-abc12 1/1 Running 0 30s
# backend-xxx-def34 1/1 Running 0 30s
# frontend-xxx-ghi56 1/1 Running 0 25s
# frontend-xxx-jkl78 1/1 Running 0 25s
kubectl get services
# NAME TYPE CLUSTER-IP PORT(S)
# backend-service NodePort 10.96.x.x 8000:3xxxx/TCP
# frontend-service NodePort 10.96.x.x 80:3yyyy/TCP
Fenêtre de terminal
minikube service frontend-service --url
# http://192.168.49.2:3yyyy
# Ouvre cette URL dans ton navigateur
Fenêtre de terminal
# Passer de 2 à 5 replicas
kubectl scale deployment backend --replicas=5
kubectl get pods
# 5 pods backend !
# Supprimer un pod (K8s en recrée un automatiquement)
kubectl delete pod backend-xxx-abc12
kubectl get pods
# Toujours 5 pods — K8s a recréé le pod manquant
# Rolling update = mise à jour progressive (changer l'image sans downtime)
# K8s remplace les pods un par un : il crée un nouveau pod avec la v2,
# attend qu'il soit prêt, puis supprime un ancien pod v1, et ainsi de suite.
kubectl set image deployment/backend backend=mon-user/devops-backend:v2
kubectl rollout status deployment/backend
# Waiting for deployment "backend" rollout to finish...
# deployment "backend" successfully rolled out
Fenêtre de terminal
kubectl delete -f backend.yml
kubectl delete -f frontend.yml
minikube stop

Exercice debug : Pourquoi le pod ne démarre pas ?

Section intitulée « Exercice debug : Pourquoi le pod ne démarre pas ? »

Tu déploies le backend et tu vois ça :

Fenêtre de terminal
kubectl get pods
# NAME READY STATUS RESTARTS AGE
# backend-6d4f5b7c9d-abc12 0/1 ImagePullBackOff 0 2m

Et quand tu fais kubectl describe pod backend-6d4f5b7c9d-abc12, tu vois :

Events:
Warning Failed 2m kubelet Failed to pull image "mon-user/devops-backend:latest":
rpc error: code = Unknown desc = Error response from daemon: pull access denied
Warning Failed 2m kubelet Error: ImagePullBackOff

Que se passe-t-il et comment tu fixes ?

💡 Indice 1

Le message dit “pull access denied”. K8s essaie de télécharger l’image depuis Docker Hub mais n’y arrive pas.

💡 Indice 2

Avec minikube, les images Docker locales ne sont pas automatiquement disponibles dans le cluster.

✅ Solution

Le problème : Kubernetes essaie de télécharger (pull) l’image depuis Docker Hub, mais elle n’existe pas sur Docker Hub (soit le nom est faux, soit le repo est privé, soit l’image est uniquement locale).

3 solutions possibles :

  1. Charger l’image locale dans minikube (le plus courant en dev) :
Fenêtre de terminal
minikube image load mon-user/devops-backend:latest
  1. Pousser l’image sur Docker Hub (si tu veux que K8s la télécharge) :
Fenêtre de terminal
docker push mon-user/devops-backend:latest
  1. Vérifier le nom de l’image dans le YAML — une typo dans image: cause ce problème.

Après le fix, force K8s à réessayer :

Fenêtre de terminal
kubectl rollout restart deployment backend

Q : C’est quoi Kubernetes ? R : Un orchestrateur de containers. Il gère le deployment, le scaling, et la high availability des applications containerisées sur un cluster de machines.

Q : Différence entre Docker et Kubernetes ? R : Docker fait tourner UN container. Kubernetes orchestre des DIZAINES/CENTAINES de containers sur plusieurs machines (scheduling, scaling, self-healing).

Q : C’est quoi un Pod ? R : L’unité de base dans K8s. Un pod contient un ou plusieurs containers qui partagent le même réseau et stockage. En pratique, 1 pod = 1 container.

Q : C’est quoi un Deployment ? R : Un objet qui gère un groupe de pods identiques. Il maintient le nombre de replicas voulu, gère les updates (rolling update), et recrée les pods qui crashent.

Q : C’est quoi un Service ? R : Un point d’accès réseau stable vers un groupe de pods. Les pods ont des IPs éphémères, le Service a une IP fixe et répartit le traffic.

Q : Comment K8s gère un pod qui crash ? R : Le Controller détecte que le nombre de replicas ne correspond plus au nombre voulu, et recrée automatiquement un pod pour compenser (self-healing).

Q : C’est quoi un Namespace ? R : Un moyen d’isoler les ressources dans un cluster. Utile pour séparer les environnements (dev, staging, prod) ou les équipes.

  • Déclare les ressources (CPU/RAM). Sans resources.requests et resources.limits, un pod peut consommer tout le node et faire crasher les autres. Toujours définir des limites.
  • Utilise des health checks. readinessProbe (le pod est prêt à recevoir du traffic ?) et livenessProbe (le pod est encore vivant ?). C’est l’endpoint /api/health qu’on a ajouté dans le Module 3 (Docker). Sans ça, K8s envoie du traffic à des pods qui ne sont pas prêts.
  • Ne déploie jamais :latest. Tag tes images avec un hash de commit ou un numéro de version. :latest change sans prévenir, et tu ne peux pas faire de rollback propre.
  • Un namespace par environnement. dev, staging, prod. Ça isole les ressources et évite de supprimer la prod par erreur.
  • Stocke tes YAML dans Git. Les fichiers de deployment K8s sont du code — ils doivent être versionnés, reviewés en PR, et jamais appliqués à la main en prod.
  • Image pas trouvée → Vérifie le nom de l’image dans le YAML. Si c’est une image locale, utilise minikube image load.
  • CrashLoopBackOff → Le container crash en boucle. kubectl logs POD pour voir l’erreur.
  • Pending → Pas assez de ressources sur le node. Réduis les replicas ou les resource requests.
  • Oublier le selector → Le Service ne trouve pas les pods. Les labels dans le Deployment doivent matcher le selector du Service.
  • Ingress : routage HTTP — un seul point d’entrée pour plusieurs services, avec des noms de domaine. C’est la première chose que tu configureras pour exposer tes services
  • Helm : gestionnaire de packages pour K8s — tu décris ton app dans un “chart” réutilisable au lieu de 10 fichiers YAML séparés
  • Horizontal Pod Autoscaler (HPA) : scaler automatiquement le nombre de pods en fonction du CPU/RAM — essentiel en production
  • RBAC : contrôle d’accès dans K8s — qui a le droit de faire quoi dans quel namespace. Plus de la gouvernance que du technique
  • Tu sais la différence entre Docker (1 container) et Kubernetes (orchestration de N containers)
  • Tu connais les objets de base : Pod, Deployment, Service
  • Tu sais utiliser kubectl get, kubectl apply, kubectl logs, kubectl scale
  • Tu as déployé le projet sur minikube avec 2 replicas
  • Tu as testé le self-healing (supprimer un pod → K8s le recrée)
  • Tu as nettoyé avec minikube stop