KUBERNETES

Concernant les supports de cours

Supports de cours

Les cours sont maintenus et donnés par : https://www.alterway.fr/formation

Licence Creative Commons BY-SA 4.0
Licence Creative Commons BY-SA 4.0

KUBERNETES : Environnement de Travail & Labs

Bienvenue dans votre Mission SRE / Platform !

  • Contexte : Vous intégrez l'équipe Platform Engineering d'une application Cloud Native en pleine croissance.
  • Votre mission :
  • Déployer, interconnecter et sécuriser une architecture microservices.
  • Gérer la résilience (zéro downtime, pannes matérielles, autoscaling).
  • Industrialiser les déploiements avec GitOps (ArgoCD & Kustomize).
  • Résoudre des incidents réels en direct (CSI Kubernetes).

Accès aux Labs Pratiques

  • Portail interactif des Labs : https://bit.ly/3XQHbUk
  • Plateformes supportées : Killercoda, vcluster, k3d, Kind ou cluster dédié.

Votre Boîte à Outils du Quotidien

  • kubectl : Le CLI officiel indispensable.
  • k9s : Interface TUI ultra-rapide pour explorer et déboguer le cluster en temps réel.
  • helm & kustomize : Pour packager et spécialiser les manifestes YAML.
  • kubectx / kubens : Pour switcher de cluster et de namespace en 1 seconde.
# Alias incontournable pour les pros :
alias k=kubectl
complete -o default -F __start_kubectl k

Méthode d'Apprentissage : « Think, Break & Fix »

  1. Comprendre le flux interne : Ne pas juste copier du YAML, comprendre ce que le Control Plane exécute.
  2. Pratiquer par le défi : Chaque module comporte des mini-défis et pièges réels de production.
  3. Investigation méthodique : Observer les événements, inspecter les statuts et auditer les logs.

KUBERNETES : Écosystème & Gouvernance Cloud Native

De Borg à la CNCF : L'Histoire de Kubernetes

  • Origines (2003-2014) : Conçu chez Google d'après les retours d'expérience des orchestrateurs internes Borg puis Omega.
  • Juillet 2015 : Sortie de Kubernetes 1.0 et don du projet à la toute nouvelle CNCF (Cloud Native Computing Foundation).
  • Aujourd'hui : L'OS universel du Cloud et le standard incontournable d'orchestration distribuée.

La CNCF : La Fondation du Cloud Native

  • Branche de la Linux Foundation dédiée à l'écosystème Cloud Native.
  • Rôle clé : Garantir la neutralité vis-à-vis des fournisseurs cloud (anti-vendor lock-in) et pérenniser les projets open source majeurs.
  • Quelques diplômés célèbres : Kubernetes, Prometheus, Envoy, Helm, Fluentd, containerd, Argo, Cilium.

Les Niveaux de Maturité CNCF

    [ Sandbox ] ──► [ Incubating ] ──► [ Graduated ]
(Expérimentation)    (Adoption active)  (Production Ready &
                                         Gouvernance éprouvée)
  1. Sandbox : Projets émergents, innovants, testés par la communauté.
  2. Incubating : Utilisés en pré-production / production, stables, avec gouvernance claire.
  3. Graduated : Le plus haut niveau d'exigence (sécurité auditée, gouvernance mature, adoption massive à l'échelle mondiale).

Comment Évolue Kubernetes ? Les SIGs

Le projet est piloté par des Special Interest Groups (SIGs) autonomes :

  • SIG Architecture : Cohérence globale du système et principes directeurs.
  • SIG API Machinery : Cœur de l'APIServer, etcd, sérialisation et Custom Resource Definitions (CRD).
  • SIG Node : Kubelet, interaction avec le runtime conteneur (CRI).
  • SIG Network : Services, Ingress, Gateway API, CNI.
  • SIG Storage : CSI, PersistentVolumes, gestion des disques.
  • SIG Security : Modèle de sécurité, RBAC, CVEs.

Le Cycle de Vie des Versions

  • 3 versions mineures par an (tous les 4 mois : ex. 1.29, 1.30, 1.31, 1.32).
  • Support officiel : Les 3 dernières versions mineures bénéficient de correctifs de sécurité (support d'environ 1 an par version).
  • Processus d'évolution des fonctionnalités :
  • Alpha : Désactivé par défaut, expérimental, breaking changes possibles.
  • Beta : Activé par défaut, stable, recommandé pour tests poussés.
  • GA (General Availability) / Stable : Prêt pour la production, rétro-compatibilité garantie.

Mini-Défi : Décodez le Paysage CNCF

Question flash : Dans quel statut CNCF se situent ces projets incontournables ?

  1. Kubernetes ?
  2. ArgoCD ?
  3. Cilium ?
  4. OpenTelemetry ?

Mini-Défi : Décodez le Paysage CNCF (Réponses)

Question flash : Dans quel statut CNCF se situent ces projets incontournables ?

  1. Kubernetes ?
  2. ArgoCD ?
  3. Cilium ?
  4. OpenTelemetry ?

Réponse : Kubernetes, ArgoCD et Cilium sont Graduated ! OpenTelemetry est Incubating en voie rapide vers la graduation.

KUBERNETES : Architecture & Mécanique Interne

Vue d'Ensemble : Deux Mondes Complémentaires

 ┌─────────────────────────────────────────────────────────────┐
 │                CONTROL PLANE (Cerveau)                      │
 │   [ etcd ] ◄──► [ kube-apiserver ] ◄──► [ Controllers ]     │
 │                        ▲                                    │
 │                        │  (Reconcile loop & Scheduler)      │
 └────────────────────────┼────────────────────────────────────┘
                          |
                          │ HTTPS / TLS (Port 6443)
                          |
 ┌────────────────────────┼────────────────────────────────────┐
 │                        ▼                                    │
 │   WORKER NODE 1                 WORKER NODE 2               │
 │   [ kubelet ] ──► [ CRI ]       [ kubelet ] ──► [ CRI ]     │
 │   [ kube-proxy / eBPF ]         [ kube-proxy / eBPF ]       │
 │   [ Pods / Containers ]         [ Pods / Containers ]       │
 └─────────────────────────────────────────────────────────────┘
  • Control Plane (Master) : Prend les décisions globales, stocke l'état, détecte et réconcilie les écarts.
  • Worker Nodes : Exécutent vos conteneurs applicatifs et gèrent le trafic réseau local.

1. etcd : Le Cœur Persistant

  • Nature : Base clé-valeur distribuée, ultra-rapide et transactionnelle.
  • Algorithme de consensus RAFT : Nécessite un quorum strict : ⌊N/2⌋+1 nœuds (clusters de 3 ou 5 nœuds).
  • Règle absolue : Seul le kube-apiserver dialogue avec etcd. Aucun worker ni utilisateur n'y accède directement.
  • En production : Exige des disques SSD/NVMe à très faible latence (fsync critique).

2. kube-apiserver : L'Unique Porte d'Entrée

Toute action (kubectl, CI/CD, Kubelet) passe par un pipeline strict en 4 étapes :

Requête HTTP ──► [1. Authentication] ──► [2. Authorization (RBAC)]
                      │
                      ▼
                 [3. Mutation & Validation (Admission Webhooks)]
                      │
                      ▼
                 [4. Sauvegarde dans etcd]
  • Stateless : Horizontalement scalable derrière un Load Balancer.
  • Émet des flux d'événements (watch) vers tous les autres contrôleurs.

3. kube-controller-manager : La Boucle de Réconciliation

La promesse fondamentale de Kubernetes : Déclaratif vs Réel.

┌───────────────────────────────────────────────┐
│          LA BOUCLE INFINIE DE CONTRÔLE        │
│                                               │
│      1. Observer l'état réel actuel           │
│      2. Comparer avec l'état désiré (YAML)    │
│      3. Agir pour résorber l'écart (Reconcile)│
└───────────────────────────────────────────────┘
  • Regroupe des dizaines de contrôleurs : NodeController, ReplicaSetController, EndpointsController, ServiceAccountController...

4. kube-scheduler : L'Aiguilleur Intelligent

Comment un Pod trouve-t-il son Node d'hébergement ?

[ Nouveau Pod non assigné (nodeName="") ]
               │
               ▼
      1. Filtrage (Predicates / Filtering)
      (Élimine les nœuds sans CPU/RAM, taints incompatibles...)
               │
               ▼
      2. Notation (Priorities / Scoring)
      (Classe les nœuds restants selon affinité, étalement des charges...)
               │
               ▼
[ Assignation au nœud avec le meilleur score ]

Les Composants du Worker Node

  1. kubelet : L'agent de bord du nœud. Reçoit les ordres de l'APIServer, pilote le runtime conteneur et remonte la santé du nœud.
  2. CRI (Container Runtime Interface) : Le moteur bas niveau (containerd, CRI-O) qui instancie les namespaces Linux et cgroups.
  3. kube-proxy / eBPF : Configure le routage réseau local et le load balancing entre Pods.

Les Modes Réseau : Du Legacy à l'eBPF

  • Userspace (historique) : Lent, passage constant entre kernel et user space.
  • iptables : Standard historique par règles de filtrage séquentielles (sature à partir de 10 000 services).
  • IPVS (IP Virtual Server) : Tables de hachage Linux, rapide et taillé pour la volumétrie.
  • eBPF (ex. Cilium) : Remplacement moderne de kube-proxy, routage directement dans le noyau sans surcoût iptables !

Le Modèle Réseau Fondamental

Kubernetes impose 3 règles absolues à tous les plugins réseau (CNI) :

  1. Tous les Pods peuvent communiquer entre eux sans NAT.
  2. Tous les Nodes peuvent communiquer avec tous les Pods sans NAT.
  3. L'adresse IP qu'un Pod voit pour lui-même est exactement la même que celle vue par les autres.

Exemples de CNI majeurs : Cilium (eBPF & haute performance), Calico (politiques de sécurité BGP/IP-in-IP), Flannel (overlay simple).

Mini-Défi SRE : Autopsie de Pannes

Scénario 1 : Si le kube-apiserver devient inaccessible pendant 10 minutes, que deviennent les Pods applicatifs en cours d'exécution ?

Scénario 2 : Si le kube-scheduler crashe, que se passe-t-il lors d'un kubectl apply d'un nouveau Deployment ?

Mini-Défi SRE : Autopsie de Pannes (Réponses)

Scénario 1 : Si le kube-apiserver devient inaccessible pendant 10 minutes, que deviennent les Pods applicatifs en cours d'exécution ?

Réponse : Ils continuent de tourner normalement ! Seules les opérations de modification et le redémarrage en cas de crash sont gelés.

Scénario 2 : Si le kube-scheduler crashe, que se passe-t-il lors d'un kubectl apply d'un nouveau Deployment ?

Réponse : Le Deployment et les Pods sont créés dans etcd, mais les Pods restent bloqués en état Pending.

KUBERNETES : Stratégies d'Installation & Typologies

Quelle Installation pour Quel Besoin ?

Environnement Outils Recommandés Cas d'Usage Principal
Dev Local & CI/CD KinD, k3d / k3s, Minikube Tester des manifests, reproduire un cluster multi-nœuds en 30s
On-Premise / Bare Metal kubeadm, kubespray (Ansible), Cluster API Contrôle total du matériel, isolation souveraine
Cloud Managé (Public Cloud) GKE, EKS, AKS, Kapsule Délégation du Control Plane, autoscaling nœuds cloud natif

Le Dev Local Moderne : KinD & K3d

# Cluster multi-nœuds en local en 1 commande avec KinD
cat <<EOF | kind create cluster --config=-
kind: Cluster
apiVersion: kind.x-k8s.io/v1alpha4
nodes:
- role: control-plane
- role: worker
- role: worker
EOF
  • KinD (Kubernetes-in-Docker) : Chaque nœud K8s est un conteneur Docker. Idéal pour simuler des pannes de nœuds sans latence !
  • k3d / K3s : K8s allégé (remplace etcd par SQLite si 1 nœud), démarrage en 5 secondes.

L'Installation Standard : kubeadm

kubeadm est l'outil officiel standard pour initialiser un cluster de production :

[ Étape 1 : Control Plane ]
  $ sudo kubeadm init --pod-network-cidr=10.244.0.0/16
         |
         │
         ▼ (Génère certificats TLS, etcd, apiserver, et le token de join)
[ Étape 2 : Plugin Réseau (CNI) ]
  $ kubectl apply -f https://docs.cilium.io/...
         |
         │
         ▼ (Les nœuds passent de NotReady à Ready)
[ Étape 3 : Raccordement des Workers ]
  $ sudo kubeadm join 192.168.1.10:6443 --token <token> --discovery-token-ca-cert-hash sha256:...

Modèle de Responsabilité Partagée (Managed Cloud)

┌─────────────────────────────────────────────────────────────┐
|                                                             |
│ Fournisseur Cloud (GCP/AWS/Azure/Scaleway/OVH...)           │
│  ► etcd, kube-apiserver, scheduler, controller-manager      │
│  ► Sauvegardes etcd, patchs de sécurité, haute dispo        │
|                                                             |
├─────────────────────────────────────────────────────────────┤
|                                                             |
│ Votre Responsabilité (DevOps / SRE)                         │
│  ► Gestion des versions de Worker Nodes (Node Pools)        │
│  ► Sécurité réseau, RBAC, ingress, dimensionnement (HPA)    │
│  ► Vos manifests applicatifs et vos données                 │
|                                                             |
└─────────────────────────────────────────────────────────────┘

Audit & Conformité : Sécuriser Dès l'Installation

Avant de mettre un cluster en production, auditez sa conformité :

  • kube-bench : Vérifie la stricte conformité avec le CIS Kubernetes Benchmark (droits des fichiers de config, arguments TLS).
  • popeye : Scanner assainisseur qui détecte les ressources orphelines, allocations manquantes et erreurs de config.
  • sonobuoy : Outil officiel de validation de conformité CNCF.
# Scanner son cluster avec Popeye en 1 commande :
popeye
 ___     ___ _____   _____                                                      K          .-'-.
| _ \___| _ \ __\ \ / / __|                                                      8     __|      `\
|  _/ _ \  _/ _| \ V /| _|                                                        s   `-,-`--._   `\
|_| \___/_| |___| |_| |___|                                                      []  .->'  a     `|-'
  Biffs`em and Buffs`em!                                                          `=/ (__/_       /
                                                                                    \_,    `    _)
                                                                                       `----;  |


GENERAL [NEXTERWAY] (2026-08-27T08:43:48+02:00)
┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅
  · Connectivity...................................................................................✅
  · MetricServer...................................................................................✅


CILIUMENDPOINTS (0 SCANNED)                                                  💥 0 😱 0 🔊 0 ✅ 0 100٪
┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅
  · Nothing to report.

...  

Mini-Défi : Choix d'Architecture (Réponses)

Contexte : Votre entreprise veut monter une chaîne CI/CD GitHub Actions pour tester le déploiement d'opérateurs K8s multi-nœuds à chaque Pull Request.

Question : Quelle solution privilégiez-vous ? 1. Un cluster EKS dédié allumé 24/7. 2. Un cluster local KinD instancié dynamiquement dans le runner de la CI. 3. Une VM avec Minikube.

Mini-Défi : Choix d'Architecture (Réponses)

Contexte : Votre entreprise veut monter une chaîne CI/CD GitHub Actions pour tester le déploiement d'opérateurs K8s multi-nœuds à chaque Pull Request.

Question : Quelle solution privilégiez-vous ? 1. Un cluster EKS dédié allumé 24/7. 2. Un cluster local KinD instancié dynamiquement dans le runner de la CI. 3. Une VM avec Minikube.

Réponse : Option 2 (KinD) ! Démarrage en ~20s, destruction automatique à la fin du job, coût quasi nul et isolation parfaite.

KUBERNETES : Piloter le Cluster avec kubectl & Kubeconfig

L'Arme Absolue de l'Ops : kubectl

  • CLI officiel pour interagir avec le kube-apiserver.
  • Transforme chaque commande en appel REST HTTPS sécurisé.
  • Lit par défaut sa configuration dans ~/.kube/config ou via $KUBECONFIG.
# L'indispensable autocomplétion shell (Bash / Zsh)
source <(kubectl completion zsh)
alias k=kubectl
complete -o default -F __start_kubectl k

Anatomie d'un Fichier kubeconfig

Un kubeconfig regroupe 3 entités distinctes :

     CLUSTERS                      USERS
(URL API + CA TLS)           (Certificats / Tokens)
        │                            │
        │                            │
        └─────────────┬──────────────┘
                      │        
                      ▼
                  CONTEXTS
     (Association Cluster + User + Namespace)
                      │     
                      │
                      ▼
               CURRENT-CONTEXT
           (Le contexte actif ciblé)

Gérer Plusieurs Clusters Comme un Pro

Au lieu de jongler manuellement avec des fichiers dispersés :

# 1. Fusionner plusieurs kubeconfigs en une seule vue
export KUBECONFIG=~/.kube/config:~/.kube/eks-prod:~/.kube/gke-dev
kubectl config view --flatten > ~/.kube/config.all
mv ~/.kube/config.all ~/.kube/config

# 2. Utiliser kubectx et kubens pour changer en 1 clic
kubectx prod-cluster
kubens billing-app

Explorer l'API : Ne Devinez Plus le YAML !

  • kubectl api-resources : Découvre toutes les ressources supportées (CRDs, versions, abréviations, portée namespace).
  • kubectl explain : La documentation intégrée directement dans votre terminal.
# Trouver la structure exacte d'un champ
kubectl explain pod.spec.containers.livenessProbe

# Explorer toute l'arborescence
kubectl explain deployment.spec.strategy --recursive

Requêtes Expertes : Sortir des Tableaux Basiques

# 1. Vue enrichie immédiate
kubectl get pods -o wide

# 2. Colonnes sur mesure (Custom Columns)
kubectl get pods -o custom-columns=NAME:.metadata.name,IP:.status.podIP,NODE:.spec.nodeName

# 3. Tri avancé (ex: trier les pods par nombre de redémarrages)
kubectl get pods --sort-by='.status.containerStatuses[0].restartCount'

# 4. JSONPath chirurgical (extraire toutes les images utilisées)
kubectl get pods -A -o jsonpath='{range .items[*]}{.spec.containers[*].image}{"\n"}{end}' | sort -u

Sélecteurs de Labels : Filtrer à l'Échelle

Ne cherchez jamais vos pods un par un :

# Ciblage par égalité ou ensemble
kubectl get pods -l 'app=frontend,environment in (production, staging)'

# Afficher les labels directement dans les colonnes
kubectl get pods -L app,version,tier

L'Écosystème des Plugins krew

krew est le gestionnaire de paquets officiel pour étendre kubectl :

kubectl krew install ctx ns neat who-can tree

# kubectl-tree : Visualise toute la hiérarchie d'objets créés par un Deployment
kubectl tree deploy web-app

# kubectl-who-can : Qui a le droit de supprimer des namespaces ?
kubectl who-can delete namespaces

Mini-Défi : La Commande Ninja

Objectif : Vous suspectez une fuite de mémoire sur un cluster de 150 pods.

Quelle commande en une ligne permet de lister le nom de tous les pods ayant déjà redémarré au moins 1 fois, triés par leur nœud ?

Mini-Défi : La Commande Ninja

Objectif : Vous suspectez une fuite de mémoire sur un cluster de 150 pods.

Quelle commande en une ligne permet de lister le nom de tous les pods ayant déjà redémarré au moins 1 fois, triés par leur nœud ?

# Solution experte :
kubectl get pods --field-selector status.phase=Running \
  -o custom-columns=POD:.metadata.name,NODE:.spec.nodeName,RESTARTS:.status.containerStatuses[0].restartCount \
  --sort-by='.spec.nodeName'

KUBERNETES : Méthodes Impérative vs Déclarative

Le Dilemme : Impératif vs Déclaratif

   APPROCHE IMPÉRATIVE                          APPROCHE DÉCLARATIVE
"Fais ceci, puis cela maintenant !"           "Voici l'état final que je veux."
        (Exécution directe)                         (GitOps / Versionné)
               │                                           │
               ▼                                           ▼
  kubectl run / kubectl create                   kubectl apply -f app.yaml
  Rapide en lab / CLI / Urgence                  Reproductible, auditable, CI/CD

Quand Utiliser Chaque Approche ?

Critère Impératif (kubectl create/run/scale) Déclaratif (kubectl apply / GitOps)
Idéal pour Examen CKA/CKAD, tests jetables, dépannage à chaud Production, revue de code, déploiements automatisés
Historique Non tracé (hors audit logs) Versionné dans Git (Source of Truth)
Gestion des Dérives Écrasement partiel sans contexte Réconciliation automatique (apply 3-way merge)

L'Arme Secrète des Pros : Le Générateur de Boilerplate

Ne tapez jamais un squelette YAML à la main ! Utilisez l'impératif pour générer du déclaratif propre :

# 1. Générer le squelette d'un Pod multi-ports
kubectl run api-server --image=nginx:alpine --port=8080 --dry-run=client -o yaml > pod.yaml

# 2. Générer un Deployment 3 réplicas avec labels
kubectl create deployment web-backend --image=my-api:1.0 --replicas=3 --dry-run=client -o yaml > deploy.yaml

# 3. Générer un Service ClusterIP raccordé au port cible
kubectl expose deployment web-backend --port=80 --target-port=8080 --dry-run=client -o yaml > svc.yaml

# 4. Générer un ConfigMap à partir de variables
kubectl create configmap app-cfg --from-literal=DB_HOST=postgres --dry-run=client -o yaml > cm.yaml

Le Cycle de Vie Déclaratif : Le « 3-Way Merge »

Quand vous faites kubectl apply -f manifest.yaml, Kubernetes compare 3 versions :

[ Fichier Local (Nouveau souhait) ]
               │
               ▼
[ Last-Applied-Configuration (Annotation dans l'objet) ]
               │
               ▼
[ État Actuel en Direct dans etcd ]
  • Cela permet de fusionner vos changements sans écraser les champs injectés dynamiquement (ex: annotations de monitoring, tokens de sécurité ou IP de statut).

Détecter les Dérives : kubectl diff

Avant d'appliquer une modification en production :

# Affiche le diff exact entre le cluster et vos fichiers locaux (couleurs git diff)
kubectl diff -f k8s/production/

# Appliquer avec Server-Side Apply (standard moderne K8s 1.22+)
kubectl apply --server-side -f k8s/production/

Mini-Défi : Le « Speedrun » YAML

Objectif : En moins de 30 secondes et sans ouvrir d'éditeur de texte, générez un fichier stack.yaml contenant à la fois un Deployment redis (image redis:alpine) et son Service redis-svc exposant le port TCP 6379.

Mini-Défi : Le « Speedrun » YAML

Objectif : En moins de 30 secondes et sans ouvrir d'éditeur de texte, générez un fichier stack.yaml contenant à la fois un Deployment redis (image redis:alpine) et son Service redis-svc exposant le port TCP 6379.

# Solution en 2 lignes chainées :
kubectl create deployment redis --image=redis:alpine --dry-run=client -o yaml > stack.yaml
echo "---" >> stack.yaml
kubectl create service clusterip redis-svc --tcp=6379:6379 --dry-run=client -o yaml >> stack.yaml

KUBERNETES : Les Objets Cœurs & Workloads

La Hiérarchie des Objets Kubernetes

┌─────────────────────────────────────────────────────────────┐
│                      NAMESPACE                              │
│                                                             │
│   ┌─────────────────────────────────────────────────────┐   │
│   │                   DEPLOYMENT                        │   │
│   │    (Gère les Rolling Updates & l'historique)        │   │
│   │                        │                            │   │
│   │                        ▼                            │   │
│   │                   REPLICASET                        │   │
│   │        (Maintient le nombre exact de Pods)          │   │
│   │                        │                            │   │
│   │                        ▼                            │   │
│   │                      PODS                           │   │
│   │         [ App Container | Native Sidecar ]          │   │
│   └─────────────────────────────────────────────────────┘   │
└─────────────────────────────────────────────────────────────┘

1. Le Pod : L'Atome Indivisible

  • Définition : La plus petite unité déployable dans Kubernetes.
  • Ce que partagent les conteneurs d'un même Pod :
  • Réseau : Même adresse IP, communication sur localhost, mêmes ports.
  • Stockage : Volumes partagés montés dans leurs systèmes de fichiers.
  • IPC & UTC : Mémoire partagée et horloge commune.

Les Sidecars Natifs (K8s 1.28+)

Fini les bricolages pour attendre qu'un proxy démarre avant votre application principale :

apiVersion: v1
kind: Pod
metadata:
  name: secured-webapp
spec:
  initContainers:
  - name: vault-agent-sidecar
    image: hashicorp/vault:latest
    restartPolicy: Always   # <── Transforme l'initContainer en Sidecar natif !
  containers:
  - name: app
    image: my-app:2.0
  • Le sidecar démarre avant l'application principale et s'éteint après elle !

2. Labels vs Annotations : Ne Les Confondez Plus !

            LABELS                                       ANNOTATIONS
Utilisés par Kubernetes pour sélectionner              Utilisés par des outils tiers
      (Sélecteurs, Services, RBAC)                     (Monitoring, CI/CD, Git commit)
              │                                                 │
              ▼                                                 ▼
- app: bookstore                                       - git-commit: 7f8a92b
- env: production                                      - prometheus.io/scrape: "true"
- tier: backend                                        - ingress.class: nginx

Règle d'or :

  • Si Kubernetes doit filtrer ou router dessus → Label.
  • Si c'est informatif → Annotation.

3. Le Deployment & ReplicaSet : Piloter les Versions

Le Deployment orchestre des ReplicaSets pour garantir des mises à jour sans interruption :

[ Deployment v1 ] ──► [ ReplicaSet v1 (3 Pods) ]  ◄── Traffic 100%
         │
         │         
    (Mise à jour d'image vers v2 : RollingUpdate)
         │
         │
         ├──► [ ReplicaSet v1 (1 Pod) ]           ◄── Traffic 33%
         └──► [ ReplicaSet v2 (2 Pods) ]          ◄── Traffic 67%

Contrôler le Rolling Update

spec:
  replicas: 5
  strategy:
    type: RollingUpdate
    rollingUpdate:
      maxSurge: 1        # Max 1 pod en plus pendant le déploiement (total 6)
      maxUnavailable: 0  # 0 interruption tolérée : aucun pod détruit avant qu'un nouveau soit Ready
# Surveiller en direct le déploiement
kubectl rollout status deployment/web-app

# Revenir en arrière en cas de bug en 1 seconde !
kubectl rollout undo deployment/web-app

4. StatefulSet : Pour les Données & l'Ordre Strict

Quand vos applications ne sont pas jetables (bases de données, Kafka, ElasticSearch, postgress, mongo, redis, etc) :

[ StatefulSet: redis ]
   |
   ├── redis-0  ──► Lié au PVC data-redis-0 (Disque SSD 1)
   ├── redis-1  ──► Lié au PVC data-redis-1 (Disque SSD 2)
   └── redis-2  ──► Lié au PVC data-redis-2 (Disque SSD 3)
  • Nommage prédictible et stable (nom-0, nom-1...).
  • Création ordonnée : Le pod 1 démarre uniquement quand le pod 0 est Ready.
  • Persistance garantie : Si un pod meurt, son remplaçant récupère exactement le même disque !

5. DaemonSet, Job & CronJob

Objet Rôle Principal Cas d'Usage Typique
DaemonSet Exécute exactement 1 copie du Pod sur chaque nœud Collecteur de logs (Fluentd), agent de métriques (Prometheus node-exporter), CNI
Job Exécute une tâche jusqu'à son terme (succès) puis s'arrête Migration de schéma SQL, calcul de rapport ponctuel
CronJob Planifie des Jobs selon une expression temporelle cron Sauvegarde nocturne 0 2 * * *, nettoyage de logs

5. DaemonSet, Job & CronJob (Suite)

apiVersion: batch/v1
kind: CronJob
metadata:
  name: db-backup
spec:
  schedule: "0 3 * * *"  # Tous les jours à 3h du matin
  jobTemplate:
    spec:
      template:
        spec:
          containers:
          - name: pg-dump
            image: postgres:16-alpine
          restartPolicy: OnFailure

6. Strategies de rolling Update

Mini-Défi : Le « Piège du Sélecteur »

Code Review : Pourquoi le Service ci-dessous ne reçoit aucun trafic bien que les 3 Pods du Deployment soient Running ?

# Deployment
spec:
  template:
    metadata:
      labels:
        app: backend-api
        tier: core
---
# Service
spec:
  selector:
    app: backend-api
    tier: api

Mini-Défi : Le « Piège du Sélecteur »

Code Review : Pourquoi le Service ci-dessous ne reçoit aucun trafic bien que les 3 Pods du Deployment soient Running ?

# Deployment
spec:
  template:
    metadata:
      labels:
        app: backend-api
        tier: core
---
# Service
spec:
  selector:
    app: backend-api
    tier: api

Réponse : Le sélecteur du Service exige tier: api, alors que les Pods ont tier: core. Les labels doivent correspondre exactement !.

KUBERNETES : Configuration & Secrets des Applications

Le Principe 12-Factor : Découpler Code & Configuration

[ Image de Conteneur Immuable ]     +       [ ConfigMap / Secret ]
       (my-app:1.0)                        (dev / staging / prod)
             │                                       │
             └──────────────────────┬────────────────┘
                                    ▼
                       [ Pod Exécutable ]
  • Une même image binaire doit pouvoir tourner en Dev, Pré-prod et Prod sans être recompilée.
  • Les données non sensibles vont dans ConfigMap, les secrets d'accès dans Secret.

1. ConfigMaps : Deux Modes d'Injection

apiVersion: v1
kind: Pod
metadata:
  name: app-config-demo
spec:
  containers:
  - name: app
    image: my-app:1.0
    # Mode A : Injecté comme Variable d'Environnement
    env:
    - name: DATABASE_URL
      valueFrom:
        configMapKeyRef:
          name: app-config
          key: db_url
    # Mode B : Monté comme Fichier de Configuration
    volumeMounts:
    - name: config-volume
      mountPath: /etc/app/config.json
      subPath: config.json   # <── Ne masque pas les autres fichiers du dossier !
  volumes:
  - name: config-volume
    configMap:
      name: app-config

Injection Globale : envFrom

Pour injecter 50 variables d'un seul coup sans les déclarer une par une :

spec:
  containers:
  - name: app
    image: my-app:1.0
    envFrom:
    - configMapRef:
        name: app-environment-vars
    - secretRef:
        name: app-sensitive-credentials

2. Secrets Kubernetes : Attention aux Fausses Idées !

Alerte Sécurité : Les Secrets Kubernetes natifs sont simplement encodés en Base64, ils ne sont PAS chiffrés par défaut dans etcd si l'EncryptionConfiguration n'est pas activée !

# Encoder une valeur
echo -n "SuperSecretPass123" | base64
# -> U3VwZXJTZWNyZXRQYXNzMTIz

# Décoder
echo "U3VwZXJTZWNyZXRQYXNzMTIz" | base64 -d
  • Types de Secrets natifs :
  • Opaque : Clés/Valeurs personnalisées (mots de passe, tokens API).
  • kubernetes.io/tls : Certificat (tls.crt) et clé privée (tls.key).
  • kubernetes.io/dockerconfigjson : Identifiants de registre privé d'images (imagePullSecrets).

Gestion Industrielle des Secrets (GitOps Ready)

Dans une démarche GitOps, interdiction de commiter des Secrets K8s en clair dans Git !

  1. Sealed Secrets (Bitnami) : Chiffrement asymétrique. Seul le cluster K8s possède la clé privée pour déchiffrer le manifeste commit dans Git.
  2. External Secrets Operator (ESO) : Synchronise les secrets directement depuis HashiCorp Vault, AWS Secrets Manager, GCP Secret Manager ou Azure Key Vault.
 [ AWS Secrets Mgr / Vault ] ──► [ External Secrets Operator ] ──► [ Secret K8s local ]

Exemple d'utilisation avec ESO (Vault)

# 1. Déclaration du SecretStore (Connexion à Vault)
apiVersion: external-secrets.io/v1beta1
kind: SecretStore
metadata:
  name: vault-store
spec:
  provider:
    vault:
      server: "https://vault.my-company.com"
      path: "kv"
      auth:
        kubernetes:
          role: "k8s-role"

# 2. Déclaration du ExternalSecret
apiVersion: external-secrets.io/v1beta1
kind: ExternalSecret
metadata:
  name: external-db-secret
spec:
  secretStoreRef:
    name: vault-store
    kind: SecretStore
  target:
    name: db-secret-from-vault  # Le nom du Secret K8s qui va être créé
  data:
  - secretKey: password
    remoteRef:
      key: my-org/prod/database
      property: DB_PASSWORD

Mini-Défi : Le Mystère de la Variable Fantôme

Incident SRE : Vous modifiez une valeur dans un ConfigMap existant via kubectl edit cm app-config.

Exemple de ConfigMap :

apiVersion: v1
kind: ConfigMap
metadata:
  name: app-config
spec:
  data:
    db_url: "postgresql://user:pass@db-service:5432/prod"

Exemple d'utilisation de ce configmap :

apiVersion: v1
kind: Pod
metadata:
  name: app-config-demo
spec:
  containers:
  - name: app
    image: my-app:1.0
    env:
    - name: DATABASE_URL
      valueFrom:
        configMapKeyRef:
          name: app-config
          key: db_url

Pourtant, l'application continue d'utiliser l'ancienne valeur. Pourquoi ?

Mini-Défi : Le Mystère de la Variable Fantôme

Incident SRE : Vous modifiez une valeur dans un ConfigMap existant via kubectl edit cm app-config.

Exemple de ConfigMap :

apiVersion: v1
kind: ConfigMap
metadata:
  name: app-config
spec:
  data:
    db_url: "postgresql://user:pass@db-service:5432/prod"

Exemple d'utilisation de ce configmap :

apiVersion: v1
kind: Pod
metadata:
  name: app-config-demo
spec:
  containers:
  - name: app
    image: my-app:1.0
    env:
    - name: DATABASE_URL
      valueFrom:
        configMapKeyRef:
          name: app-config
          key: db_url

Pourtant, l'application continue d'utiliser l'ancienne valeur. Pourquoi ?

Réponse : Les variables d'environnement (env / envFrom) sont injectées au moment du démarrage du processus dans le conteneur. Elles ne se mettent pas à jour à chaud ! Il faut redémarrer le Pod (kubectl rollout restart deployment/mon-app) ou monter le ConfigMap en tant que Volume.

KUBERNETES : Dimensionnement, CPU/RAM & Quotas

Le Défi SRE : Ni Pénurie, Ni Gaspillage

   SUR-ALLOCATION (Gaspillage)                     SOUS-ALLOCATION (Instabilité)
Requests trop hautes = Nœuds vides              Requests trop basses = Noisy neighbors,
   Facture Cloud x3 inutile !                      OOMKilled & CPU Throttling !
  • Requests : Ce que le kube-scheduler réserve pour décider sur quel nœud placer le Pod.
  • Limits : Le plafond absolu imposé par le noyau Linux (cgroups v2) au runtime.

CPU vs RAM : Deux Comportements Radicaux

Ressource Nature Comportement en cas de Dépassement Unité
CPU Compressible CPU Throttling : Le processus est bridé en temps CPU (latence p99 explose, mais le pod reste vivant). Millicores (500m = 0.5 vCPU)
Mémoire (RAM) Incompressible OOMKilled (Code 137) : Le Kernel Linux tue instantanément le conteneur via un SIGKILL. Mébibytes (256Mi, 1Gi)

Un mébibyte (MiB ) est également une unité d'information numérique, mais il est basé sur le système binaire. Un mébibyte équivaut à 1024 x 1024 octets = 1 048 576 octets, soit 2^20 octets. Le terme "mébibyte" a été introduit par la Commission électrotechnique internationale (CEI) en 1998 pour établir une distinction claire avec le mégaoctet basé sur le système décimal. Malheureusement, le terme mébibyte n'a pas encore été adopté par le grand public, de sorte que vous verrez régulièrement "mégaoctet" ou simplement MB, se référant soit à 1 000 000 d'octets, soit à 1 048 576 octets, sans savoir lequel est le bon.

CPU vs RAM : Deux Comportements Radicaux (Suite)

resources:
  requests:
    cpu: "200m"      # 0.2 vCPU garanti
    memory: "256Mi"  # 256 MiB réservés
  limits:
    cpu: "1000m"     # Max 1 vCPU
    memory: "512Mi"  # Au-delà de 512MiB -> Crash OOMKilled !

Les 3 Classes de Qualité de Service (QoS)

Kubernetes classe automatiquement chaque Pod pour savoir qui sacrifier en premier en cas de saturation d'un nœud :

1. Guaranteed (Protégé) ──► requests == limits (CPU et RAM) pour tous les conteneurs
2. Burstable (Standard) ──► Au moins 1 request définie, requests < limits
3. BestEffort (Sacrifié) ──► Aucune request ni limit définie

En cas de pénurie de mémoire sur un Worker Node, le Kubelet évince et détruit en priorité les Pods BestEffort, puis Burstable, et enfin Guaranteed en tout dernier recours.

Gouvernance : LimitRange vs ResourceQuota

         LIMITRANGE                             RESOURCEQUOTA
  (S'applique à CHAQUE Pod)             (S'applique à TOUT le Namespace)
             │                                         │
             ▼                                         ▼
Injecte des requests par défaut         Plafonne la consommation totale
et empêche les pods démesurés           (ex: Max 32 vCPU et 64GiB au total)
apiVersion: v1
kind: LimitRange
metadata:
  name: team-limit-range
spec:
  limits:
  - type: Pod
    max:
      cpu: "2"
      memory: "512Mi"
    min:
      cpu: "100m"
      memory: "128Mi"
apiVersion: v1
kind: ResourceQuota
metadata:
  name: team-quota
spec:
  hard:
    requests.cpu: "10"
    requests.memory: "20Gi"
    limits.cpu: "20"
    limits.memory: "40Gi"
    pods: "30"

Mini-Défi : Le Mystère du Pod Pending

Scénario d'incident : Vous déployez un pod avec requests.cpu: "4".

En regardant avec htop sur le Worker Node, le CPU réel consommé n'est que de 10%. Pourtant, le Pod reste bloqué en statut Pending. Pourquoi ?

Mini-Défi : Le Mystère du Pod Pending

Scénario d'incident : Vous déployez un pod avec requests.cpu: "4".

En regardant avec htop sur le Worker Node, le CPU réel consommé n'est que de 10%. Pourtant, le Pod reste bloqué en statut Pending. Pourquoi ?

Réponse : Le scheduler prend ses décisions sur la base de la somme des Requests réservées, et non sur la charge réelle du CPU à l'instant T ! Si les autres pods ont réservé tout le CPU théorique du nœud, le scheduler refuse d'en ajouter un nouveau.

KUBERNETES : Ordonnancement & Stratégies de Placement

L'Objectif : Maîtriser le Placement des Pods

       ATTIRER des Pods                            REPOUSSER des Pods
  (Affinité & Sélecteurs de Nœuds)              (Taints & Tolerations)
               │                                           │
               ▼                                           ▼
"Ce pod IA DOIT tourner sur                    "Ce nœud GPU est réservé,
 des machines équipées de GPU"                  aucun pod standard n'y entre !"

1. Du Simple nodeSelector à la nodeAffinity

# Méthode 1 : nodeSelector basique
spec:
  nodeSelector:
    disktype: ssd

# Méthode 2 : nodeAffinity Avancée (Hard vs Soft)
spec:
  affinity:
    nodeAffinity:
      requiredDuringSchedulingIgnoredDuringExecution: # Hard (Obligatoire)
        nodeSelectorTerms:
        - matchExpressions:
          - key: topology.kubernetes.io/zone
            operator: In
            values: [eu-west-1a, eu-west-1b]
      preferredDuringSchedulingIgnoredDuringExecution: # Soft (Préférentiel)
      - weight: 80
        preference:
          matchExpressions:
          - key: instance-type
            operator: In
            values: [c5.xlarge]

2. Haute Disponibilité : podAntiAffinity & topologySpreadConstraints

Pour éviter que tous vos pods ne tombent si une seule zone de disponibilité (AZ) du Cloud crashe :

spec:
  topologySpreadConstraints:
  - maxSkew: 1                             # Tolérance max de déséquilibre : 1 pod d'écart
    topologyKey: topology.kubernetes.io/zone # Répartir entre les datacenters / AZs
    whenUnsatisfiable: DoNotSchedule       # Bloquer si la règle n'est pas respectée
    labelSelector:
      matchLabels:
        app: bookstore-api
  • topologySpreadConstraints est le standard moderne recommandé pour équilibrer parfaitement les charges.

3. Taints & Tolerations : La Ségrégation des Nœuds

  • Taint (sur le Nœud) : kubectl taint nodes node-gpu dedicated=gpu:NoSchedule
  • Toleration (sur le Pod) : Seul un Pod ayant cette tolérance peut s'installer sur le nœud.
  • Les 3 Effets :
  1. NoSchedule : Empêche le placement des nouveaux pods non tolérants.
  2. PreferNoSchedule : Évite si possible, mais accepte en dernier recours.
  3. NoExecute : Expulse immédiatement les pods déjà présents s'ils ne tolèrent pas le taint !

Résumé des Règles de Placement

Mécanisme Portée Type de Règle Cas d'Usage Typique
nodeSelector Nœud Binaire (Strict) Cibler un type d'instance simple (ex: SSD)
nodeAffinity Nœud Hard (required) / Soft (preferred) Ciblage multi-zones & architectures CPU
podAntiAffinity Pod ↔ Pod Hard / Soft Répartir les réplicas sur des hôtes distincts
topologySpread Nœuds / Zones Étalement uniforme Équilibrer les pods sur plusieurs AZs
Taints / Tolerations Nœud ↔ Pod Répulsion (NoSchedule / NoExecute) Nœuds GPU, isolation Control Plane, maintenance

Mini-Défi : Le Pod Éjecté

Question flash : Vous exécutez la commande suivante sur un worker node hébergeant 10 pods de production : kubectl taint node worker-1 maintenance=true:NoExecute

Que se passe-t-il pour ces 10 pods ?

Mini-Défi : Le Pod Éjecté

Question flash : Vous exécutez la commande suivante sur un worker node hébergeant 10 pods de production : kubectl taint node worker-1 maintenance=true:NoExecute

Que se passe-t-il pour ces 10 pods ?

Réponse : S'ils n'ont pas de tolerations explicite pour maintenance=true, ils sont immédiatement tués et expulsés (NoExecute). Le Deployment Controller recréera de nouveaux pods sur les autres nœuds sains.

KUBERNETES : Stockage Persistant & CSI

Éphémère vs Persistant : L'Architecture du Stockage

           ÉPHÉMÈRE                                       PERSISTANT
  (Meurt avec le Pod)                         (Survit à la destruction du Pod)
           │                                                  │
           ▼                                                  ▼
  emptyDir / configMap / secret                  PersistentVolumeClaim (PVC)
  Cache local, scratchpad, RAM                   Bases de données, fichiers partagés

Les Volumes Éphémères

  1. emptyDir :
  • Créé au démarrage du Pod, détruit à sa mort.
  • Idéal pour partager des fichiers temporaires entre un conteneur principal et un sidecar.
  • Peut être monté en RAM (medium: Memory) pour des performances maximales.
  1. hostPath :
  • Monte un dossier physique du Worker Node (ex: /var/log).
  • ⚠️ Danger en production : Si le pod migre sur un autre nœud, il ne retrouve pas ses données.

Le Trio Gagnant : StorageClass, PV & PVC

  1. L'Administrateur déclare le fournisseur
     [ StorageClass: "fast-ssd" ] (AWS gp3, GCP pd-ssd, Ceph)
                  │
                  ▼ (Provisionnement Dynamique automatique)
  2. Le Développeur demande du stockage
     [ PersistentVolumeClaim (PVC) : 50Gi, ReadWriteOnce ]
                  │
                  ▼ (Liaison / Binding automatique)
  3. Kubernetes crée le volume physique
     [ PersistentVolume (PV) : 50Gi alloué sur le Cloud/SAN ]
                  │
                  ▼
  4. Monté dans le Pod : /var/lib/postgresql/data

Les Modes d'Accès aux Volumes

Mode d'Accès Abréviation Description Exemples Typiques
ReadWriteOnce RWO Montable en lecture/écriture par 1 seul Nœud à la fois Disques Blocs Cloud (AWS EBS, GCP PD, Azure Disk)
ReadOnlyMany ROX Montable en lecture seule par plusieurs Nœuds Dépôt de binaires, modèles IA statiques
ReadWriteMany RWX Montable en lecture/écriture par plusieurs Nœuds simultanément NFS, CephFS, AWS EFS, Azure Files
ReadWriteOncePod RWOP Montable en lecture/écriture par 1 seul Pod unique Verrouillage exclusif haute sécurité

Exemple de Manifeste PVC

apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: postgres-data-pvc
spec:
  accessModes:
    - ReadWriteOnce
  storageClassName: premium-ssd-csi
  resources:
    requests:
      storage: 50Gi
  • Le StorageClass s'occupe d'appeler l'API Cloud (via le driver CSI) pour créer le disque automatiquement.

Politiques de Récupération (Reclaim Policy)

Que devient le disque physique (PV) quand le développeur supprime le PVC ?

  • Delete (par défaut) : Le volume physique dans le Cloud/SAN est immédiatement détruit.
  • Retain (recommandé en production) : Le volume physique est conservé avec ses données pour permettre une récupération manuelle en cas d'erreur humaine.

Mini-Défi : Le PVC Bloqué en Pending

Scénario d'incident : Vous créez un PVC avec storageClassName: standard et accessModes: [ReadWriteMany] sur un cluster EKS avec disques EBS.

Le PVC reste indéfiniment en statut Pending. Pourquoi ?

Mini-Défi : Le PVC Bloqué en Pending

Scénario d'incident : Vous créez un PVC avec storageClassName: standard et accessModes: [ReadWriteMany] sur un cluster EKS avec disques EBS.

Le PVC reste indéfiniment en statut Pending. Pourquoi ?

Réponse : Les disques blocs EBS d'AWS ne supportent que le mode ReadWriteOnce (RWO). Le provisionneur CSI refuse d'allouer un disque EBS pour une demande de type ReadWriteMany (RWX), qui nécessite un système de fichiers distribué comme EFS / NFS

KUBERNETES : Réseau, Services & Découverte DNS

Le Problème : Les Adresses IP des Pods Sont Mortelles !

 [ Pod Web 1 (10.244.1.42) ] ──► (Meurt) ──► [ Nouveau Pod (10.244.2.89) ]
                                                   ▲
                                                   │
  "Comment le Frontend peut-il joindre le Backend si son IP change sans cesse ?"
  • La Solution : L'Objet Service
  • Fournit une IP Virtuelle stable (ClusterIP) qui ne change jamais.
  • Fournit un Nom DNS interne résolu par CoreDNS.
  • Répartit la charge (Load Balancing L4) vers tous les Pods sains correspondants.

1. La Découverte DNS Interne (CoreDNS)

Chaque Service enregistré reçoit automatiquement un nom DNS FQDN :

     ┌───────────────────────────────────────────────────────┐
     │       mon-service.mon-namespace.svc.cluster.local     │
     └─────────────────────────┬─────────────────────────────┘
                               │
         ┌─────────────────────┼────────────────────────┐
         ▼                     ▼                        ▼
[ Mêmes Namespace ]    [ Autre Namespace ]      [ FQDN Complet ]
"curl mon-service"    "curl mon-service.prod".  "curl mon-service.prod.svc..."

2. Les 4 Types de Services Kubernetes

         [ LoadBalancer ]          (Cloud Public : AWS NLB / GCP LB)
                │
                ▼
           [ NodePort ]            (Port 30000-32767 sur chaque Nœud)
                │
                ▼
          [ ClusterIP ]            (IP Virtuelle interne au Cluster)
                │
                ▼
[ Pod 1 ]   [ Pod 2 ]   [ Pod 3 ]

Typologie Détaillée des Services

Type de Service Portée Adresse IP Cas d'Usage
ClusterIP (Défaut) Interne au cluster IP Virtuelle interne Communication Est-Ouest (API ↔ BDD)
Headless (clusterIP: None) Interne Aucune VIP (DNS direct vers les IPs des Pods) StatefulSets, Kafka, Elasticsearch
NodePort Accessible depuis l'extérieur Port élevé (30000-32767) Dev local, passerelles bare-metal
LoadBalancer Public Internet IP Publique Cloud managée Exposition externe principale d'un cluster Cloud
ExternalName Interne → Externe Alias CNAME DNS Rediriger vers une BDD managée (ex: RDS AWS)

3. Sous le Capot : Les Endpoints & EndpointSlice

Comment le Service sait-il où envoyer le trafic ?

[ Service (port: 80) ] ──► [ EndpointSlice ] ──► [ Pod 1: 10.244.1.15:8080 ]
   (Sélecteur: app=web)                             [ Pod 2: 10.244.2.20:8080 ]
# Vérifier la liste des pods réellement connectés au service
kubectl get endpoints mon-service
kubectl get endpointslices -l kubernetes.io/service-name=mon-service
  • Si kubectl get endpoints renvoie <none>, aucun pod ne recevra jamais de trafic !

Manifeste YAML Type d'un Service

apiVersion: v1
kind: Service
metadata:
  name: bookstore-backend
spec:
  type: ClusterIP
  selector:
    app: bookstore
    tier: backend
  ports:
    - name: http
      protocol: TCP
      port: 80         # Le port exposé par le Service
      targetPort: 8080 # Le port réel écouté dans le conteneur du Pod

Mini-Défi : Le Service Fantôme

Incident SRE : Vous créez un Service auth-service pour exposer 3 Pods. Lorsque vous faites curl auth-service depuis un autre Pod, la requête renvoie instantanément : Connection refused.

Quand vous tapez kubectl get endpoints auth-service, la colonne ENDPOINTS affiche <none>.

Quelles sont les 2 causes les plus probables ?

Mini-Défi : Le Service Fantôme

Incident SRE : Vous créez un Service auth-service pour exposer 3 Pods. Lorsque vous faites curl auth-service depuis un autre Pod, la requête renvoie instantanément : Connection refused.

Quand vous tapez kubectl get endpoints auth-service, la colonne ENDPOINTS affiche <none>.

Quelles sont les 2 causes les plus probables ?

Réponses :

  1. Le sélecteur de labels du Service ne correspond pas aux labels des Pods.
  2. Les Pods ne passent pas leurs Readiness Probes (Pods non Ready), donc Kubernetes les a retirés des Endpoints.

KUBERNETES : Ingress & Gateway API (Routage L7)

Le Problème : 50 Microservices = 50 Load Balancers Payants ?

          APPROCHE NAÏVE (L4)                           APPROCHE INGRESS / L7
 50 Services type LoadBalancer                 1 SEUL Load Balancer Cloud Public
  = 50 IP publiques à payer                     = 1 seule IP, routage L7 intelligent
  = Facture Cloud x50 !                         = Répartition par nom de domaine & URL !
  • Ingress Controller : Le proxy inverse (NGINX, Traefik, Envoy, HAProxy) qui écoute le trafic mondial.
  • Ressource Ingress : La règle déclarative qui indique à quel Service envoyer chaque URL.

1. Ingress Class & Contrôleurs

  1. Déployer un Ingress Controller (ex: ingress-nginx).
  2. Déclarer une IngressClass pour indiquer quel contrôleur doit interpréter vos règles.
  3. Créer vos objets Ingress applicatifs.
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: bookstore-ingress
  annotations:
    cert-manager.io/cluster-issuer: letsencrypt-prod # TLS automatique !
spec:
  ingressClassName: nginx
  tls:
  - hosts:
    - store.formation.com
    secretName: bookstore-tls-cert
  rules:
  - host: store.formation.com
    http:
      paths:
      - path: /api
        pathType: Prefix
        backend:
          service:
            name: backend-api-svc
            port:
              number: 8080
      - path: /
        pathType: Prefix
        backend:
          service:
            name: frontend-ui-svc
            port:
              number: 80

2. Automatisation TLS : cert-manager

Fini les certificats SSL expirés un dimanche soir à 23h !

[ Ingress avec annotation ] ──► [ cert-manager ] ──► [ Let's Encrypt API ]
                                     |
                                     │ (Challenge HTTP-01 / DNS-01)
                                     ▼
                        [ Secret TLS auto-renouvelé ]

3. Le Futur Est Là : La Gateway API

La Gateway API est le successeur officiel de l'Ingress standard, conçu pour les architectures modernes et la séparation des responsabilités :

┌─────────────────────────────────────────────────────────────┐
│                                                             │
│ ÉQUIPE INFRA / CLOUD (Cluster Admin)                        │
│  ► GatewayClass : Définit le fournisseur (Envoy, Cilium...) │
│  ► Gateway : Déclare le point d'entrée, IP et ports 80/443  │
│                                                             │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│ ÉQUIPE DÉVELOPPEMENT / APPLICATION                          │
│  ► HTTPRoute : Règle de routage autonome par namespace      │
│  ► GRPCRoute / TCPRoute : Support natif au-delà du HTTP !   │
│                                                             │
└─────────────────────────────────────────────────────────────┘
  • Avantages clés : Séparation des rôles (RBAC granulaire), routage multi-namespaces, splitting de trafic natif (Canary / Blue-Green) sans annotations constructeur obscures !

Mini-Défi : Diagnostic d'une Erreur 502 / 503

Incident SRE : Un utilisateur essaie de joindre https://store.formation.com/api et obtient une erreur 502 Bad Gateway de la part d'Ingress Nginx.

Quelle est la démarche méthodique d'investigation ?

Mini-Défi : Diagnostic d'une Erreur 502 / 503

Incident SRE : Un utilisateur essaie de joindre https://store.formation.com/api et obtient une erreur 502 Bad Gateway de la part d'Ingress Nginx.

Quelle est la démarche méthodique d'investigation ?

# 1. Vérifier si l'Ingress pointe vers un service existant
kubectl describe ingress bookstore-ingress

# 2. Vérifier si le Service a des Pods Endpoints actifs
kubectl get endpoints backend-api-svc

# 3. Vérifier les logs du contrôleur Ingress en temps réel
kubectl logs -n ingress-nginx -l app.kubernetes.io/name=ingress-nginx --tail=50

KUBERNETES : Stratégies de Déploiement & Zéro Downtime

L'Objectif : Déployer en Pleine Journée Sans Peur !

   STRATÉGIE RECREATE                            STRATÉGIE ROLLING UPDATE
[ Ancien Pod ] ──► (Détruit)                    [ V1 ] [ V1 ] ──► [ V1 ] [ V2 ]
         │ (Downtime !)                                            │
         ▼                                                         ▼
[ Nouveau Pod ] ──► (Démarré)                                   [ V2 ] [ V2 ] (0 Downtime)

1. Les 4 Stratégies de Déploiement

Stratégie Downtime Coût Infrastructure Risque Cas d'Usage
RollingUpdate 0s Faible (+1 pod de maxSurge) Moyen Applications stateless standard
Recreate Oui (10s-2min) Nul Faible Incompatibilité de schéma de BDD entre versions
Blue/Green 0s Élevé (+100% de ressources) Faible Bascule atomique instantanée après tests réels
Canary 0s Modéré (+5-10%) Très faible Envoi de 5% du vrai trafic sur la v2 (Argo Rollouts)

2. Le Secret du Zéro Downtime Réel : Les 3 Probes

Sans Probes bien configurées, Kubernetes envoie du trafic utilisateur à des conteneurs qui n'ont même pas fini d'initialiser leur serveur web !

   1. StartupProbe ──► "Est-ce que l'application a fini de charger ses dépendances ?"
         │ (Si OK)
         ▼
   2. ReadinessProbe ──► "Est-ce que l'application est prête à recevoir du trafic ?"
         │               (Si KO : Retiré des Endpoints du Service, 0 trafic envoyé !)
         ▼
   3. LivenessProbe ──► "Est-ce que l'application est toujours vivante ou en deadlock ?"
                         (Si KO : Le Kubelet redémarre le conteneur !)

Exemple de Configuration des Probes

spec:
  containers:
  - name: api
    image: my-app:2.0
    startupProbe:
      httpGet:
        path: /healthz
        port: 8080
      failureThreshold: 30
      periodSeconds: 2    # Laisse jusqu'à 60s pour démarrer
    readinessProbe:
      httpGet:
        path: /ready
        port: 8080
      periodSeconds: 5
    livenessProbe:
      httpGet:
        path: /live
        port: 8080
      periodSeconds: 10

3. L'Arrêt Propre (Graceful Shutdown)

Lorsqu'un Pod est arrêté (scale down ou rolling update) :

1. Le Pod passe en statut Terminating
2. L'IP du Pod est retirée des Endpoints (les nouvelles requêtes ne vont plus vers lui)
3. Exécution du Hook preStop (sleep 5s pour laisser le temps aux iptables de se synchroniser)
4. Envoi du signal SIGTERM au processus applicatif (pour finir les requêtes en cours)
5. Attente jusqu'à terminationGracePeriodSeconds (défaut: 30s)
6. Envoi du SIGKILL destructeur si le processus n'a pas quitté proprement
lifecycle:
  preStop:
    exec:
      command: ["/bin/sh", "-c", "sleep 5"]

Mini-Défi : L'Erreur de la Liveness Probe

Code Review : Vous observez qu'en pic de charge, vos Pods se mettent à redémarrer en boucle de façon incontrôlable.

En inspectant la config :

livenessProbe:
  httpGet:
    path: /api/v1/check-database-and-heavy-query # <── Requête SQL complexe en BDD !

Pourquoi cette configuration est-elle une bombe à retardement en production ?

Mini-Défi : L'Erreur de la Liveness Probe

Code Review : Vous observez qu'en pic de charge, vos Pods se mettent à redémarrer en boucle de façon incontrôlable.

En inspectant la config :

livenessProbe:
  httpGet:
    path: /api/v1/check-database-and-heavy-query # <── Requête SQL complexe en BDD !

Pourquoi cette configuration est-elle une bombe à retardement en production ?

Réponse : Si la base de données ralentit ou sature, la liveness probe échoue en timeout. Kubernetes croit à tort que le Pod web est mort et le tue, provoquant une cascade de redémarrages catastrophique (thundering herd). La Liveness Probe doit être locale et ultra-légère !.

KUBERNETES : Autoscaling (HPA, VPA, Karpenter & KEDA)

Les 3 Dimensions de l'Autoscaling

┌─────────────────────────────────────────────────────────────┐
|                                                             |
│ 1. HPA (Horizontal Pod Autoscaler) : Varie le NOMBRE de Pods│
│    [ Pod ] ──► [ Pod ] [ Pod ] [ Pod ] [ Pod ]              |
|                                                             |
├─────────────────────────────────────────────────────────────┤
|                                                             |
| 2. VPA (Vertical Pod Autoscaler) : Varie la TAILLE du Pod   |
│    [ Petit Pod (1 CPU) ] ──► [ GROS Pod (4 CPU) ]           |
|                                                             |
├─────────────────────────────────────────────────────────────┤
|                                                             |
| 3. Cluster Autoscaler / Karpenter : Varie le NOMBRE de NŒUDS|
│    [ Node 1 ] ──► [ Node 1 ] [ Node 2 ] [ Node 3 ]          |
|                                                             |
└─────────────────────────────────────────────────────────────┘

1. Horizontal Pod Autoscaler (HPA v2)

Le HPA interroge metrics-server toutes les 15 secondes pour adapter le nombre de réplicas :

apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
  name: bookstore-api-hpa
spec:
  scaleTargetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: bookstore-api
  minReplicas: 2
  maxReplicas: 20
  metrics:
  - type: Resource
    resource:
      name: cpu
      target:
        type: Utilization
        averageUtilization: 70  # Vise 70% de la Request CPU
  - type: Resource
    resource:
      name: memory
      target:
        type: AverageValue
        averageValue: 400Mi

2. KEDA : Autoscaling Piloté par les Événements

Le CPU/RAM ne suffit plus : comment réagir AVANT que le CPU ne sature ?

[ File RabbitMQ / Kafka ] ──► [ KEDA Controller ] ──► [ HPA K8s ] ──► [ Scale Pods ]
  (10 000 messages en attente)
  • KEDA (Kubernetes Event-driven Autoscaling) permet de scaler sur :
  • La longueur d'une file RabbitMQ / SQS / Redis.
  • Le lag de consommation d'un topic Kafka.
  • Le nombre de requêtes HTTP par seconde (Prometheus metric).
  • Scale to Zero : Descendre à 0 Pod quand la file est vide (économies maximales !).

3. Provisionnement des Nœuds : Karpenter vs Cluster Autoscaler

Quand 50 nouveaux Pods passent en statut Pending faute de place :

  • Cluster Autoscaler standard : Ajuste les Node Groups Cloud (délai : 2 à 4 minutes).
  • Karpenter (CNCF) : Provisionneur sans groupe de nœuds, démarre la VM Cloud exacte sur-mesure (Spot/On-Demand) en moins de 45 secondes !

Mini-Défi : Le HPA Aveugle

Incident SRE : Vous configurez un HPA sur un Deployment. En tapant kubectl get hpa, la colonne TARGETS affiche obstinément : <unknown>/70%.

Quelles sont les 2 causes directes de ce blocage ?

Mini-Défi : Le HPA Aveugle

Incident SRE : Vous configurez un HPA sur un Deployment. En tapant kubectl get hpa, la colonne TARGETS affiche obstinément : <unknown>/70%.

Quelles sont les 2 causes directes de ce blocage ?

# Diagnostic :
kubectl describe hpa bookstore-api-hpa

Réponses :

  1. Le Deployment n'a pas de resources.requests.cpu définie (le HPA ne sait pas sur quelle base calculer le pourcentage !).
  2. Le composant metrics-server n'est pas installé ou ne parvient pas à joindre les Kubelets.

KUBERNETES : Observabilité & Monitoring (Métriques & Traces)

Les 3 Piliers de l'Observabilité Cloud Native

┌───────────────────────────────────────────────┐
│               OBSERVABILITÉ                   │
│                                               │
│   [ MÉTRIQUES ]   [ LOGS ]     [ TRACES ]     │
│    Prometheus      Loki      OpenTelemetry    │
│   (Tendances)   (Détails)     (Parcours)      │
└───────┬─────────────┬──────────────┬──────────┘
        └─────────────┼──────────────┘
                      ▼
            [ GRAFANA DASHBOARDS ]

1. La Pile Prometheus & Grafana

Prometheus collecte les métriques par Pull (Scraping HTTP) à intervalles réguliers :

[ Pod Applicatif ] ──► Expose /metrics (format Prometheus)
                             ▲
                             │ (Scrape toutes les 15s)
                             │
[ Prometheus Operator ] ─────┴──────► Enregistre dans la base TSDB
                             │
                             │ 
                             ▼
[ Grafana Dashboard ]   ◄─────────── (Requêtes PromQL)
  • kube-state-metrics : Génère des métriques sur l'état des objets K8s (nombre de pods en Pending, réplicas désirés vs réels).
  • node-exporter : Métriques matérielles des nœuds (CPU, RAM, I/O disque, réseau).

Le ServiceMonitor (Prometheus Operator)

Ne configurez plus Prometheus à la main ! Déclarez le scraping en YAML :

apiVersion: monitoring.coreos.com/v1
kind: ServiceMonitor
metadata:
  name: bookstore-monitor
  labels:
    release: prometheus
spec:
  selector:
    matchLabels:
      app: bookstore
  endpoints:
  - port: metrics
    interval: 15s
    path: /metrics

2. Tracing Distribué avec OpenTelemetry (OTel)

Comment identifier quel microservice ralentit une transaction utilisateur sur 15 étapes ?

[ Client ] ──► [ Frontend ] (20ms)
                   │
                   ▼
               [ Auth API ] (15ms)
                   │
                   ▼
               [ Payment Gateway ] ──► (Bloqué 3.2s sur la banque !)
  • OpenTelemetry Collector reçoit les traces (spans), corrèle les contextes distribués (traceparent) et les stocke dans Jaeger ou Grafana Tempo.

3. Monitoring CLI Temps Réel : k9s & kubectl top

# Vérifier la consommation réelle instantanée
kubectl top nodes
kubectl top pods -A --sort-by=memory

# Ouvrir k9s (TUI interactif pour naviguer dans les pods, logs et métriques)
k9s

Mini-Défi : Alerte PromQL

Question SRE : Vous devez écrire une règle d'alerte pour être réveillé uniquement si le taux d'erreur 5xx de votre API dépasse 5% pendant 5 minutes.

Mini-Défi : Alerte PromQL

Question SRE : Vous devez écrire une règle d'alerte pour être réveillé uniquement si le taux d'erreur 5xx de votre API dépasse 5% pendant 5 minutes.

# Requête PromQL experte :
sum(rate(http_requests_total{status=~"5.."}[5m])) 
/ 
sum(rate(http_requests_total[5m])) * 100 > 5

KUBERNETES : Centralisation des Logs (Loki, Fluent Bit & Vector)

Le Flux des Logs dans Kubernetes

 [ Pod Container ] ──► Écrit sur stdout / stderr
                             │
                             ▼ (Géré par le CRI : containerd)
  Fichier JSON local : /var/log/pods/<namespace>_<pod>_<uid>/...
                             │
                             ▼ (Scrapé par un DaemonSet sur le Nœud)
               [ Fluent Bit / Promtail / Vector ]
                             │
                             ▼ (Envoi compressé)
               [ Grafana Loki / OpenSearch ]

1. Pourquoi stdout/stderr est la Règle d'Or ?

  • Les conteneurs doivent toujours écrire leurs logs sur la sortie standard (stdout) et la sortie d'erreur standard (stderr).
  • containerd capture ces flux, gère la rotation automatique des fichiers sur le disque du nœud hôte et les expose via kubectl logs.
# Consulter les logs en direct d'un Pod
kubectl logs -f deployment/bookstore-api

# Voir les logs d'un conteneur spécifique dans un Pod multi-conteneurs
kubectl logs pod-avec-sidecar -c mon-application

# Consulter les logs du conteneur AVANT son dernier crash (Indispensable !)
kubectl logs mon-pod -p

2. Le Modèle de Collecte : DaemonSet vs Sidecar

Modèle Consommation Ressources Complexité Cas d'Usage Recommandé
DaemonSet (Node-level) Très faible (1 seul agent par serveur physique) Simple et transparent pour les devs 95% des cas en production (Fluent Bit, Vector)
Sidecar (Pod-level) Élevée (1 conteneur en plus par Pod) Spécifique Applications legacy écrivant dans un fichier rigide /var/log/app.log

3. La Stack Moderne : Grafana Loki & LogQL

Contrairement à ElasticSearch qui indexe l'intégralité du texte des logs (gourmand en RAM/Disque), Grafana Loki indexe uniquement les métadonnées (labels K8s) :

# Requête LogQL pour filtrer les erreurs 500 sur le namespace production
{namespace="production", app="bookstore"} |= "ERROR" | json | status_code >= 500

Mini-Défi : Le Crash Mystère

Incident SRE : Un Pod s'est crashé il y a 30 secondes et vient d'être recréé par son ReplicaSet. Quand vous tapez kubectl logs mon-pod, vous ne voyez que 2 lignes de démarrage, les logs de l'erreur qui a causé la mort du pod ont disparu.

Quelle option magique de kubectl logs permet de voir les derniers instants du conteneur décédé ?

Mini-Défi : Le Crash Mystère

Incident SRE : Un Pod s'est crashé il y a 30 secondes et vient d'être recréé par son ReplicaSet. Quand vous tapez kubectl logs mon-pod, vous ne voyez que 2 lignes de démarrage, les logs de l'erreur qui a causé la mort du pod ont disparu.

Quelle option magique de kubectl logs permet de voir les derniers instants du conteneur décédé ?

Réponse : kubectl logs mon-pod --previous ou kubectl logs mon-pod -p.

KUBERNETES : Troubleshooting & Scènes de Crime (CSI)

La Méthodologie d'Investigation en 4 Étapes

[ 1. ÉTAT GLOBAL ] ──► kubectl get pods -o wide (Identifier les coupables)
        │
        ▼
[ 2. AUTOPSIE ]    ──► kubectl describe pod <nom> (Section Events & Last State)
        │
        ▼
[ 3. DERNIERS MOTS ] ──► kubectl logs <nom> --previous (Pourquoi a-t-il crashé ?)
        │
        ▼
[ 4. INFILTRATION ]  ──► kubectl debug -it <nom> --image=busybox (Debug éphémère)

Le Guide des Statuts de Pods

Statut Affiché Signification Réelle Cause Racine Typique
ImagePullBackOff Impossible de télécharger l'image Erreur de nom de tag, registre privé sans imagePullSecrets
CrashLoopBackOff L'app démarre puis s'arrête en erreur Variable d'environnement manquante, erreur de config, exit code ≠0
OOMKilled (Exit 137) Tué par le noyau Linux L'application a dépassé sa resources.limits.memory
Pending Le scheduler ne trouve aucun nœud Pas assez de CPU/RAM libre, Taints sans Tolerations, PVC non lié
CreateContainerConfigError Configuration invalide ConfigMap ou Secret référencé introuvable
Terminating (Bloqué) Le pod refuse de mourir Finalizer bloqué, ou I/O disque NFS gelé

Diagnostic Avancé : Les Conteneurs Éphémères (kubectl debug)

Comment déboguer une image de production moderne ultra-sécurisée (Distroless / Scratch) qui ne contient ni sh, ni curl, ni ps ?

# Injecter un conteneur de debug avec tous vos outils sans redémarrer le pod !
kubectl debug -it mon-pod-distroless --image=nicolaka/netshoot --target=mon-app
  • Le conteneur de debug partage le même namespace réseau et les mêmes processus que l'application !

Scène de Crime CSI N°1 : Le Crash Incompréhensible

Constat : Un conteneur Java SpringBoot redémarre toutes les 2 minutes sans aucun message d'erreur dans les logs applicatifs.

En tapant kubectl describe pod java-app :

Last State:     Terminated
  Reason:       OOMKilled
  Exit Code:    137

Diagnostic : La JVM a alloué son heap sans tenir compte de la limite cgroups K8s. Le noyau Linux a envoyé un SIGKILL (Code 137 = 128 + 9).

Correction : Ajouter l'option JVM -XX:MaxRAMPercentage=75.0 ou augmenter la limite mémoire dans le YAML.

Scène de Crime CSI N°2 : Le Pod Terminating Éternel

Constat : Vous tapez kubectl delete pod mon-pod --force, mais le Pod reste bloqué indéfiniment en statut Terminating.

# 1. Inspecter les finalizers qui retiennent la destruction
kubectl get pod mon-pod -o jsonpath='{.metadata.finalizers}'

# 2. Débloquer la situation en supprimant le finalizer bloquant
kubectl patch pod mon-pod -p '{"metadata":{"finalizers":null}}'

Mini-Défi : Le Pod Qui Se Termine Immédiatement

Incident SRE : Vous lancez un pod avec l'image ubuntu:latest sans argument particulier : kubectl run test --image=ubuntu.

Le pod passe immédiatement en Completed puis en CrashLoopBackOff.

Pourquoi ce conteneur refuse-t-il de rester en vie ?

Mini-Défi : Le Pod Qui Se Termine Immédiatement

Incident SRE : Vous lancez un pod avec l'image ubuntu:latest sans argument particulier : kubectl run test --image=ubuntu.

Le pod passe immédiatement en Completed puis en CrashLoopBackOff.

Pourquoi ce conteneur refuse-t-il de rester en vie ?

Réponse : Le processus principal par défaut d'une image Ubuntu est /bin/bash. Sans terminal interactif (-it) ni commande longue (sleep infinity), le shell termine son exécution immédiatement (PID 1 s'arrête avec code 0), ce qui éteint le Pod.

KUBERNETES : Opérations Avancées au Quotidien

L'Arsenal Opérationnel de l'Ingénieur Platform

┌─────────────────────────────────────────────────────────────┐
│                 OPÉRATIONS EN DIRECT                        │
│                                                             │
│  ► Tunneling Sécurisé  : kubectl port-forward               │
│  ► Transfert Fichiers  : kubectl cp                         │
│  ► Automatisation CI/CD: kubectl wait --for=condition=...   │
│  ► Débogage Live       : kubectl exec / kubectl attach      │
└─────────────────────────────────────────────────────────────┘

1. Tunnel Sécurisé : kubectl port-forward

Accéder à une base de données PostgreSQL ou une UI interne sans jamais exposer de port public sur Internet :

# Rediriger le port local 5432 vers le port 5432 du Pod postgres
kubectl port-forward pod/postgres-0 5432:5432

# Rediriger vers un Service ou un Deployment
kubectl port-forward svc/prometheus-k8s 9090:9090 -n monitoring
  • Le trafic passe à travers le tunnel HTTPS chiffré de l'APIServer !

2. Automatisation CI/CD : kubectl wait

Fini les boucles sleep 30 fragiles et hasardeuses dans vos pipelines CI/CD !

# Attendre que le déploiement soit 100% opérationnel avec un timeout de 2 minutes
kubectl rollout status deployment/bookstore-api --timeout=120s

# Attendre qu'une condition spécifique soit validée
kubectl wait --for=condition=ready pod -l app=bookstore-api --timeout=60s

# Attendre la fin d'un Job de migration de base de données
kubectl wait --for=condition=complete job/db-migration --timeout=180s

3. Transfert de Données : kubectl cp

# Copier un fichier de log local vers un conteneur
kubectl cp dump.sql default/postgres-0:/tmp/dump.sql

# Récupérer un fichier généré à l'intérieur d'un pod
kubectl cp default/reporting-pod:/app/report.pdf ./report.pdf -c app-container

Mini-Défi : Le Pipeline CI/CD Résilient

Objectif : Vous écrivez un script de déploiement qui applique un manifest, attend que l'application soit prête, et effectue un rollback automatique immédiat si le déploiement échoue au bout de 90 secondes.

Mini-Défi : Le Pipeline CI/CD Résilient

Objectif : Vous écrivez un script de déploiement qui applique un manifest, attend que l'application soit prête, et effectue un rollback automatique immédiat si le déploiement échoue au bout de 90 secondes.

# Le script SRE parfait :
kubectl apply -f app-deployment.yaml
if ! kubectl rollout status deployment/web-app --timeout=90s; then
    echo "Déploiement échoué ! Rollback immédiat..."
    kubectl rollout undo deployment/web-app
    exit 1
fi
echo "Déploiement réussi avec succès !"

KUBERNETES : Maintenance, Drains & Mises à Niveau

La Procédure de Maintenance d'un Nœud Sans Interruption

[ Nœud Actif avec Pods ] ──► [ 1. CORDON ] ──► Aucun nouveau pod n'est admis
                                   │
                                   ▼
                             [ 2. DRAIN ]  ──► Évacue les pods vers d'autres nœuds
                                   │           (En respectant les PDB !)
                                   ▼
                             [ 3. REBOOT / UPGRADE OS ]
                                   │
                                   ▼
                             [ 4. UNCORDON ] ──► Nœud réactivé et sain !

1. Cordon & Drain en Pratique

# Étape 1 : Empêcher le scheduling sur le nœud
kubectl cordon worker-01

# Étape 2 : Évacuer tous les pods proprement
kubectl drain worker-01 --ignore-daemonsets --delete-emptydir-data

# Étape 3 : Une fois la maintenance terminée (reboot, kernel patch...)
kubectl uncordon worker-01
  • --ignore-daemonsets : Indispensable pour ne pas bloquer sur les agents locaux de logs/monitoring.
  • --delete-emptydir-data : Accepte la suppression des données locales temporaires.

2. Le Garde-Fou Absolu : PodDisruptionBudget (PDB)

Sans PDB, un drain peut tuer tous les réplicas d'un service en même temps et créer une coupure de service !

apiVersion: policy/v1
kind: PodDisruptionBudget
metadata:
  name: bookstore-pdb
spec:
  minAvailable: 2     # Garantit qu'au moins 2 pods restent vivants en permanence !
  selector:
    matchLabels:
      app: bookstore
  • Le kubectl drain sera mis en attente et évincera les pods un par un, en attendant que le nouveau pod soit Ready avant de tuer le suivant.

3. Stratégie de Mise à Niveau du Cluster (kubeadm)

   1. Mettre à niveau le Control Plane Master 1
      $ sudo apt update && sudo apt install -y kubeadm=1.31.0-1.1
      $ sudo kubeadm upgrade plan
      $ sudo kubeadm upgrade apply v1.31.0
      $ sudo apt install -y kubelet=1.31.0-1.1 kubectl=1.31.0-1.1
      $ sudo systemctl daemon-reload && sudo systemctl restart kubelet
            │
            ▼
   2. Mettre à niveau les Worker Nodes (Un par Un !)
      $ kubectl drain worker-01 ...
      $ sudo kubeadm upgrade node
      $ sudo apt install -y kubelet=1.31.0-1.1
      $ sudo systemctl restart kubelet
      $ kubectl uncordon worker-01

4. Sauvegarde & Disaster Recovery avec Velero

  • Velero (CNCF) : L'outil standard de sauvegarde et de migration de clusters.
  • Sauvegarde la totalité des manifests dans un bucket S3/GCS et déclenche des snapshots des disques persistants (CSI).
# Sauvegarder tout le namespace production en 1 commande
velero backup create prod-backup-$(date +%F) --include-namespaces production

# Restaurer l'environnement complet en cas de désastre
velero restore create --from-backup prod-backup-2026-08-25

Mini-Défi : Le Drain Bloqué

Incident SRE : Vous lancez kubectl drain worker-02, mais la commande reste bloquée indéfiniment avec le message : Cannot evict pod: PodDisruptionBudget "api-pdb" is currently violated.

Quel est le problème et comment le résoudre proprement ?

Mini-Défi : Le Drain Bloqué

Incident SRE : Vous lancez kubectl drain worker-02, mais la commande reste bloquée indéfiniment avec le message : Cannot evict pod: PodDisruptionBudget "api-pdb" is currently violated.

Quel est le problème et comment le résoudre proprement ?

Réponse : L'application a un PDB exigeant minAvailable: 2, mais il n'y a plus assez de place sur les autres nœuds du cluster pour démarrer le second pod. Solution : Ajouter un nouveau worker node au cluster ou augmenter temporairement les ressources disponibles pour débloquer l'éviction.

KUBERNETES : Sécurité, RBAC & Isolation Réseau

Le Modèle des 4C de la Sécurité Cloud Native

┌───────────────────────────────────────────┐
│                  CLOUD                    │
│   ┌───────────────────────────────────┐   │
│   │              CLUSTER              │   │
│   │   ┌───────────────────────────┐   │   │
│   │   │         CONTAINER         │   │   │
│   │   │   ┌───────────────────┐   │   │   │
│   │   │   │       CODE        │   │   │   │
│   │   │   └───────────────────┘   │   │   │
│   │   └───────────────────────────┘   │   │
│   └───────────────────────────────────┘   │
└───────────────────────────────────────────┘
  • Cloud / Datacenter : Sécurité de l'infrastructure hôte (IAM Fournisseur, VPC, Pare-feu, chiffrement des disques).
  • Cluster : Protection du plan de contrôle Kubernetes (API Server, RBAC strict, chiffrement etcd, Network Policies).
  • Container : Durcissement du runtime (images minimales distroless, runAsNonRoot, drop des capabilities, profils Seccomp).
  • Code : Sécurité applicative et chaîne CI/CD (analyse SAST/DAST, scan des dépendances SCA, gestion externe des secrets).

1. Authentification & RBAC (Qui a le droit de faire quoi ?)

   SUJETS                     RÔLES                           PORTÉES
  (Qui ?)                   (Quoi ?)                         (Où ?)
     │                         │                               │
┌────┴────────────┐       ┌────┴────────────┐             ┌────┴──────────────┐
│ User / Group    │ ────► │ Verbs : get,    │ ──────────► │ RoleBinding       │
│ ServiceAccount  │       │ list, create... │             │ (Namespace local) │
└─────────────────┘       │ Resources: pods │             ├───────────────────┤
                          └─────────────────┘             │ ClusterRoleBinding│
                                                          │ (Tout le Cluster) │
                                                          └───────────────────┘

Exemple de Règle RBAC Moindre Privilège

apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
  namespace: production
  name: pod-log-viewer
rules:
- apiGroups: [""]
  resources: ["pods", "pods/log"]
  verbs: ["get", "list", "watch"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
  namespace: production
  name: dev-view-logs
subjects:
- kind: Group
  name: developers
  apiGroup: rbac.authorization.k8s.io
roleRef:
  kind: Role
  name: pod-log-viewer
  apiGroup: rbac.authorization.k8s.io

2. Pod Security Standards (PSA / PSS)

Les vieux PodSecurityPolicy (PSP) sont dépréciés. Le standard moderne est Pod Security Admission :

Profil PSS Niveau de Sécurité Restrictions Imposées
Privileged Insecure Accès complet sans restriction (réservé aux CNI / CSI)
Baseline Standard Interdit les conteneurs privilégiés et accès hostPath critiques
Restricted Durci (Prod) Non-root obligatoire, capabilities supprimées, FS en lecture seule
# Activer le mode Restricted sur un namespace en 1 commande :
kubectl label namespace production \
  pod-security.kubernetes.io/enforce=restricted \
  pod-security.kubernetes.io/warn=restricted

3. Durcissement du Pod : SecurityContext

spec:
  securityContext:
    runAsNonRoot: true       # Interdit l'exécution sous root (UID 0)
    seccompProfile:
      type: RuntimeDefault   # Active le filtrage des appels système
  containers:
  - name: app
    image: my-app:distroless
    securityContext:
      allowPrivilegeEscalation: false
      readOnlyRootFilesystem: true  # Empêche l'écriture de malware
      capabilities:
        drop: ["ALL"]               # Supprime toutes les capacités Linux

4. Network Policies : Le Pare-feu Interne (Zero Trust)

Par défaut dans Kubernetes, tous les Pods peuvent parler à tous les Pods !

# Règle d'or : Bloquer tout le trafic entrant par défaut (Default Deny)
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: default-deny-ingress
  namespace: production
spec:
  podSelector: {}  # S'applique à tous les pods du namespace
  policyTypes:
  - Ingress
# N'autoriser QUE le Backend à parler à PostgreSQL sur le port 5432
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: allow-backend-to-postgres
spec:
  podSelector:
    matchLabels:
      app: postgres
  ingress:
  - from:
    - podSelector:
        matchLabels:
          app: bookstore-backend
    ports:
    - protocol: TCP
      port: 5432

Mini-Défi : L'Erreur d'Audit de Sécurité

Question flash : Quelle commande kubectl permet de vérifier immédiatement si l'utilisateur alice a le droit de supprimer des secrets dans le namespace production ?

Mini-Défi : L'Erreur d'Audit de Sécurité

Question flash : Quelle commande kubectl permet de vérifier immédiatement si l'utilisateur alice a le droit de supprimer des secrets dans le namespace production ?

# La réponse instantanée :
kubectl auth can-i delete secrets -n production --as alice

KUBERNETES : Gestion de Paquets avec Helm

Pourquoi Helm ? Le Package Manager Cloud Native

 [ 20 Fichiers YAML Complexes ]  ──► Packagés dans un  ──► [ 1 Helm Chart ]
 (Deployments, Services, RBAC,                             (my-app:1.2.0)
  CRDs, Ingress, Secrets...)                                     │
                                                                 ▼
                         Paramétré par un seul fichier : [ values.yaml ]
  • Rôle principal : Idéal pour déployer et maintenir des applications tierces (Prometheus, cert-manager, Ingress-NGINX, Redis).
  • Maintient un historique des versions déployées et gère les rollbacks en 1 commande.

Anatomie d'un Helm Chart

my-chart/
├── Chart.yaml          # Métadonnées (Nom, version de l'app, version du chart)
├── values.yaml         # Valeurs par défaut injectées dans les templates
├── templates/          # Gabarits YAML avec moteur Go Template
│   ├── deployment.yaml
│   ├── service.yaml
│   └── _helpers.tpl
└── charts/             # Sous-charts / Dépendances éventuelles

Le Moteur de Templating en Action

# templates/service.yaml
apiVersion: v1
kind: Service
metadata:
  name: {{ include "my-chart.fullname" . }}
spec:
  type: {{ .Values.service.type }}
  ports:
    - port: {{ .Values.service.port }}
      targetPort: http
# values.yaml (Dev vs Prod)
service:
  type: ClusterIP
  port: 8080

Commandes Indispensables du Quotidien

# 1. Ajouter un dépôt officiel (ex: Bitnami)
helm repo add bitnami https://charts.bitnami.com/bitnami
helm repo update

# 2. Installer ou Mettre à jour de façon idempotente
helm upgrade --install my-redis bitnami/redis \
  --namespace databases --create-namespace \
  --set auth.enabled=false

# 3. Tester le rendu YAML sans rien appliquer (Dry Run)
helm template my-release bitnami/redis -f my-values.yaml

# 4. Rollback immédiat vers la révision précédente
helm rollback my-redis 1

Mini-Défi : Le Piège du Template YAML

Code Review : Que se passe-t-il si vous passez une chaîne de caractères contenant des deux-points (:) dans une valeur Helm sans guillemets dans votre values.yaml ?

message: Erreur: Connexion refusée # <── Erreur de parsing YAML !

Mini-Défi : Le Piège du Template YAML

Code Review : Que se passe-t-il si vous passez une chaîne de caractères contenant des deux-points (:) dans une valeur Helm sans guillemets dans votre values.yaml ?

message: Erreur: Connexion refusée # <── Erreur de parsing YAML !

Réponse : Le parseur YAML interprète la seconde partie comme une nouvelle clé imbriquée invalide et le helm install crashe. Il faut toujours quoter les chaînes : message: "Erreur: Connexion refusée" et utiliser la fonction {{ .Values.message | quote }} dans le template.

KUBERNETES : Configuration Déclarative avec Kustomize

Le Concept : Personnaliser Sans Moteur de Templates

                  [ BASE (Manifests Communs) ]
                  (Deployment, Service, Config)
                                │
               ┌────────────────┴────────────────┐
               ▼                                 ▼
    [ OVERLAY : STAGING ]              [ OVERLAY : PRODUCTION ]
  - 1 Réplica                        - 10 Réplicas
  - Image : tag staging              - Image : tag v2.1.0 (immuable)
  - Petites ressources CPU/RAM       - Grosses ressources + HPA
  • Principe fondamental : Pas de syntaxe {{ .Values }} complexe. On part de manifests YAML purs et standards, et on applique des correctifs (patches) chirurgicaux par environnement.
  • Intégré nativement dans kubectl depuis la version 1.14 (kubectl apply -k).

Structure d'un Répertoire Kustomize

k8s/
├── base/
│   ├── deployment.yaml
│   ├── service.yaml
│   └── kustomization.yaml
└── overlays/
    ├── dev/
    │   ├── kustomization.yaml
    │   └── replica_patch.yaml
    └── production/
        ├── kustomization.yaml
        └── resources_patch.yaml

Le Fichier kustomization.yaml en Pratique

# overlays/production/kustomization.yaml
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization

namespace: production
namePrefix: prod-

resources:
  - ../../base

images:
  - name: bookstore-api
    newTag: v2.1.4

patches:
  - path: resources_patch.yaml
    target:
      kind: Deployment
      name: bookstore-api

Générateurs Automatiques : Adieu les Bugs de Cache

configMapGenerator:
  - name: app-config
    files:
      - config.properties
  • Fonctionnalité magique : Kustomize calcule un hash SHA du contenu du fichier (ex: app-config-7b2m8f4k9g).

  • Lorsque vous modifiez config.properties, le nom du ConfigMap change, ce qui force Kubernetes à déclencher automatiquement un Rolling Update propre de tous les Pods sans manipulation manuelle !

Helm vs Kustomize : Le Tableau Décisionnel

Critère Helm Kustomize
Philosophie Templating Go paramétré Héritage & Patches déclaratifs
Dépendance externe Binaire helm requis Intégré dans kubectl
Gestion des Secrets / CM Manuelle Hash automatique (Rolling update forcé)
Cas d'usage Idéal Applications tierces CNCF (Ingress, Cert-Manager) Vos propres microservices internes en GitOps

Mini-Défi : Déploiement Kustomize

# Quelle commande permet d'inspecter le rendu final de l'overlay production sans rien appliquer ?
kubectl kustomize overlays/production/

# Quelle commande permet d'appliquer directement l'overlay en production ?
kubectl apply -k overlays/production/

KUBERNETES : Industrialisation GitOps avec ArgoCD

Qu'est-ce que le GitOps ?

          APPROCHE CI CLASSIQUE                     APPROCHE GITOPS (ArgoCD)
  [ Runner CI / GitHub Actions ]              [ Git Repository (Source of Truth) ]
                │ (Push)                                       │
                ▼ (Droits admin requis !)                      ▼ (Pull & Reconcile)
       [ Cluster Kubernetes ]                       [ ArgoCD dans le Cluster ]
  (Sécurité faible : tokens exposés)            (Sécurité maximale : 0 port ouvert !)
  • Les 4 Principes OpenGitOps :
  1. Déclaratif : Tout le système est décrit en YAML.
  2. Versionné & Immuable : Git est l'unique source de vérité (Single Source of Truth).
  3. Tiré Automatiquement : Le cluster tire les changements depuis Git (Pull model).
  4. Réconcilié en Continu : ArgoCD répare automatiquement toute dérive manuelle (Self-Healing).

1. L'Objet Application d'ArgoCD

apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
  name: bookstore-production
  namespace: argocd
spec:
  project: default
  source:
    repoURL: https://github.com/mon-orga/k8s-manifests.git
    targetRevision: main
    path: overlays/production
  destination:
    server: https://kubernetes.default.svc
    namespace: production
  syncPolicy:
    automated:
      prune: true       # Supprime du cluster les objets supprimés dans Git !
      selfHeal: true    # Annule et réécrit toute modification manuelle directe faite via kubectl !

2. Le Tableau de Bord & les Statuts ArgoCD

  SYNC STATUS                              HEALTH STATUS
┌───────────────┐                        ┌───────────────┐
│ Synced        │ (Cluster == Git)       │ Healthy       │ (Tous les pods sont Ready)
│ OutOfSync     │ (Git a changé)         │ Progressing   │ (Rolling update en cours)
│ Unknown       │ (Erreur de parsing)    │ Degraded      │ (CrashLoopBackOff / Erreur)
└───────────────┘                        └───────────────┘
  • Drift Correction (Self-Healing) : Si un développeur supprime un Pod ou modifie un Service avec kubectl edit en production, ArgoCD détecte l'écart en quelques secondes et rétablit immédiatement l'état déclaré dans Git !

3. Gestion Multi-Clusters : ApplicationSet

Pour gérer 100 applications réparties sur 10 clusters (Dev, Staging, Prod, Multi-Cloud) sans dupliquer 1000 lignes de YAML :

apiVersion: argoproj.io/v1alpha1
kind: ApplicationSet
metadata:
  name: all-microservices
  namespace: argocd
spec:
  generators:
  - git:
      repoURL: https://github.com/mon-orga/k8s-manifests.git
      revision: main
      directories:
      - path: services/*
  template:
    metadata:
      name: "{{path.basename}}"
    spec:
      project: default
      source:
        repoURL: https://github.com/mon-orga/k8s-manifests.git
        targetRevision: main
        path: "{{path}}"
      destination:
        server: https://kubernetes.default.svc
        namespace: apps

Mini-Défi : L'Effet du Self-Healing

Scénario de test : Vous avez configuré une Application ArgoCD avec syncPolicy.automated.selfHeal: true. Un administrateur supprime accidentellement un Deployment avec kubectl delete deployment bookstore-api.

Que se passe-t-il dans les 10 secondes qui suivent ?

Mini-Défi : L'Effet du Self-Healing

Scénario de test : Vous avez configuré une Application ArgoCD avec syncPolicy.automated.selfHeal: true. Un administrateur supprime accidentellement un Deployment avec kubectl delete deployment bookstore-api.

Que se passe-t-il dans les 10 secondes qui suivent ?

Réponse : ArgoCD détecte que le Deployment manque dans le cluster par rapport à la branche main de Git, et le recrée instantanément à l'identique !.

KUBERNETES : Se Préparer aux Certifications CNCF

Le Panorama des Certifications Officielles

      KCNA (Fondamentaux)          CKAD (Développeur)           CKA (Administrateur)          CKS (Sécurité)
   QCM Théorique Cloud Native    100% Pratique Hands-on       100% Pratique Hands-on      100% Pratique Hands-on
   Architecture & CNCF           Pods, Deploy, Services,      Control Plane, Upgrades,    PSA, Falco, Trivy,
                                 Jobs, ConfigMaps             ETCD Backup, CNI, Troubleshoot  RBAC, NetworkPolicy
  • Format des examens CKA / CKAD / CKS :
  • Durée : 2 heures en conditions réelles sur terminal Linux.
  • Accès autorisé : Documentation officielle (https://kubernetes.io/docs/).
  • Note minimale : 66% pour réussir.

Les 5 Astuces en Or pour Réussir (Speedrun Mode)

  1. Maîtriser la génération impérative : kubectl run ... --dry-run=client -o yaml (interdiction d'écrire du YAML de zéro !).

  2. Configurer son environnement dès la 1ère minute : alias k=kubectl export do="--dry-run=client -o yaml" export now="--force --grace-period=0"

  3. Optimiser sa configuration .vimrc : vim set tabstop=2 shiftwidth=2 expandtab autoindent number

  4. Utiliser les signets et la recherche doc : Aller directement sur les pages d'exemples de la documentation officielle.
  5. S'entraîner sur Killer.sh : Deux sessions de simulation d'examen gratuites sont incluses avec l'inscription à l'examen.

KUBERNETES : Bilan & Prêts pour la Production !

Félicitations : Vous Êtes Désormais Parés !

┌─────────────────────────────────────────────────────────────┐
│                 VOTRE NOUVEAU SOCLE OPS / SRE               │
│                                                             │
│  ✔ Maîtrise de l'architecture interne & du Control Plane    │
│  ✔ Déploiements résilients & Zéro Downtime (Probes, PDB)    │
│  ✔ Réseau L4/L7, Ingress, Gateway API & Découverte DNS      │
│  ✔ Sécurité Zero-Trust (PSA Restricted, RBAC, NetPol)       │
│  ✔ Observabilité complète (Prometheus, Grafana Loki, OTel)  │
│  ✔ Industrialisation GitOps avec ArgoCD & Kustomize         │
└─────────────────────────────────────────────────────────────┘

La Checklist Avant Toute Mise en Production

  • [ ] Les resources.requests et limits sont-elles définies sur tous les conteneurs ?
  • [ ] Les readinessProbe et livenessProbe sont-elles en place et légères ?
  • [ ] Le PodDisruptionBudget protège-t-il les services critiques ?
  • [ ] Les pods tournent-ils en runAsNonRoot: true avec le profil PSA Restricted ?
  • [ ] Les manifests sont-ils versionnés dans Git et synchronisés par ArgoCD ?

À Vous de Jouer sur Vos Clusters ! ⎈