P19 - Kubernetes (avec K3s)
Objectif
Comprendre comment est fait un cluster Kubernetes, monter un petit cluster K3s (1 control-plane + 1 worker), connaître les objets principaux (Pod, Deployment, Service, Ingress, PVC, Secret) et les commandes
kubectlde base.
Source
Fiche rédigée à partir de cours de lidstah (cours de 2 400 lignes), cours de lidstah. Prérequis : V05 - Docker et P08 - Conteneurs avancés (Docker, Kubernetes).
1. Pourquoi Kubernetes ?
Docker Compose lance des conteneurs sur une machine. Kubernetes (« k8s ») les répartit sur plusieurs machines et maintient l’état demandé : « je veux 3 copies de mon site ». Si un nœud tombe, il relance les copies ailleurs, tout seul. Concurrents : Docker Swarm, HashiCorp Nomad.
2. Architecture
flowchart TB K["kubectl<br/>(ton PC)"] -->|"API HTTPS :6443"| CP subgraph CP["Control-plane (3 pour le quorum en prod)"] API["kube-apiserver"] SCH["scheduler<br/>(où placer les pods)"] CM["controller-manager<br/>(fait respecter l'état voulu)"] ETCD[("etcd<br/>état du cluster")] end CP --> W1 & W2 subgraph W1["Worker 1"] KL1["kubelet + containerd"] --> P1["Pod"] & P2["Pod"] end subgraph W2["Worker 2"] KL2["kubelet + containerd"] --> P3["Pod"] end
| Composant | Rôle |
|---|---|
| kube-apiserver | Porte d’entrée unique : tout passe par l’API |
| etcd | Base clé/valeur qui contient tout l’état du cluster (à sauvegarder !) |
| scheduler | Choisit sur quel nœud lancer chaque pod |
| controller-manager | Compare l’état réel à l’état voulu et corrige |
| kubelet | Agent sur chaque nœud, pilote le moteur de conteneurs |
| CRI | Moteur de conteneurs (containerd, CRI-O ; Docker n’est plus utilisé directement) |
| CNI | Réseau des pods (Flannel, Calico, Cilium) |
| CoreDNS | DNS interne : postgres.mon-ns.svc.cluster.local |
3. Installer un cluster K3s
K3s = Kubernetes allégé (un seul binaire, inclut Traefik comme Ingress et un petit LoadBalancer). Autres options vues par lidstah : kubeadm (« hardcore »), k0s / k0sctl, Talos Linux (OS immuable dédié).
Prérequis : VMs Debian avec IP fixes et noms d’hôte différents (attention aux clones), 2 vCPU, 4 Go de RAM.
# sur le control-plane
curl -sfL https://get.k3s.io | sh -
sudo cat /var/lib/rancher/k3s/server/node-token # jeton pour joindre les workers
# sur chaque worker
curl -sfL https://get.k3s.io | K3S_URL=https://IP_CONTROL_PLANE:6443 K3S_TOKEN=LE_JETON sh -
# utiliser kubectl sans sudo (sur le control-plane)
mkdir -p ~/.kube
sudo cp /etc/rancher/k3s/k3s.yaml ~/.kube/config
sudo chown "$USER": ~/.kube/config && chmod 600 ~/.kube/config
kubectl get nodes # les deux nœuds doivent être Ready4. Les objets à connaître
| Objet | Rôle | Analogie |
|---|---|---|
| Namespace | Dossier pour isoler (par client, par projet) | Un dossier |
| Pod | Un ou plusieurs conteneurs qui vivent ensemble (même IP) | Un mini docker-compose |
| Deployment | « N copies de ce pod », mises à jour progressives | Le gérant des pods |
| StatefulSet | Comme Deployment, pour les bases de données (noms et volumes stables) | |
| DaemonSet | Un pod sur chaque nœud (agents de supervision, logs) | |
| Service | Adresse stable vers un groupe de pods (ClusterIP interne, NodePort, LoadBalancer) | Un répartiteur interne |
| Ingress | Routage HTTP(S) par nom de domaine vers les Services, avec une seule IP publique | Un reverse proxy |
| PersistentVolumeClaim | Demande de stockage durable (sinon tout est perdu au redémarrage du pod) | Un volume Docker |
| ConfigMap / Secret | Configuration / données sensibles injectées dans les pods | Fichier .env |
| RBAC | Qui a le droit de faire quoi (rôles, serviceAccounts) | Les droits Linux |
Un Secret n'est pas chiffré
Par défaut, un Secret Kubernetes est seulement encodé en base64 (lisible par quiconque a accès à l’API ou à etcd). Ne jamais le mettre dans Git tel quel.
Exemple complet : un site nginx en 3 copies
apiVersion: apps/v1
kind: Deployment
metadata:
name: site
namespace: demo
spec:
replicas: 3
selector:
matchLabels: { app: site }
template:
metadata:
labels: { app: site }
spec:
containers:
- name: nginx
image: nginx:1.27
ports: [ { containerPort: 80 } ]
resources:
requests: { cpu: 50m, memory: 64Mi }
limits: { cpu: 200m, memory: 128Mi }
---
apiVersion: v1
kind: Service
metadata:
name: site
namespace: demo
spec:
selector: { app: site }
ports: [ { port: 80, targetPort: 80 } ]kubectl create namespace demo
kubectl apply -f site.yaml
kubectl get pods -n demo -o wide # 3 pods, répartis sur les nœuds
kubectl delete pod -n demo NOM_D_UN_POD # il est recréé tout seul !
kubectl scale deploy site -n demo --replicas=55. Aide-mémoire kubectl
| Commande | Rôle |
|---|---|
kubectl get nodes / pods / svc / deploy / ingress -A | Lister (-A = tous les namespaces) |
kubectl describe pod NOM -n NS | Détails + événements (pourquoi ça ne démarre pas) |
kubectl logs -f NOM -n NS | Journaux d’un pod |
kubectl exec -it NOM -n NS -- /bin/sh | Shell dans un pod |
kubectl apply -f fichier.yaml / delete -f | Créer/mettre à jour / supprimer depuis un fichier |
kubectl port-forward svc/site 8080:80 -n demo | Accéder à un service depuis ton PC |
kubectl config get-contexts / use-context | Changer de cluster |
kubectl create secret generic NOM --from-env-file=.env -n NS | Secret depuis un fichier .env |
kubectl create secret tls NOM --key=privkey.pem --cert=fullchain.pem -n NS | Secret TLS |
Outils pratiques : k9s (interface texte), Helm (gestionnaire de paquets : « charts »).
6. Superviser un cluster
helm repo add prometheus https://prometheus-community.github.io/helm-charts
helm repo update
helm install -n prom --create-namespace prometheus prometheus/kube-prometheus-stack
kubectl get pods,svc,ingress -n promCela installe Prometheus (collecte), Alertmanager (alertes), Grafana (tableaux de bord), node-exporter (un DaemonSet sur chaque nœud). Les applications exposent leurs métriques sur /metrics ; un ServiceMonitor ou PodMonitor dit à Prometheus de les lire.
Aller plus loin : OpenFaaS (fonctions serverless sur K3s) dans cours de lidstah.
Exercices
- Un pod est en état
CrashLoopBackOff. Quelles deux commandes lances-tu en premier ? - Pourquoi 3 control-planes et pas 2 ?
- Quelle différence entre un Service et un Ingress ?
Correction
kubectl describe pod NOM -n NS(événements) etkubectl logs NOM -n NS --previous(journaux du conteneur qui vient de planter).- etcd fonctionne par quorum (majorité) : avec 2, la perte d’un seul bloque tout ; avec 3, on peut en perdre 1.
- Le Service donne une adresse stable vers des pods (souvent interne). L’Ingress expose plusieurs services HTTP(S) vers l’extérieur sur une seule IP, en triant par nom de domaine ou chemin.
Erreurs fréquentes / dépannage
| État / symptôme | Cause probable |
|---|---|
ImagePullBackOff | Nom d’image faux, ou registre privé sans secret docker-registry |
Pending | Pas assez de ressources sur les nœuds, ou PVC qui ne trouve pas de stockage |
CrashLoopBackOff | L’application plante au démarrage : lire les logs |
Worker absent de get nodes | Mauvais jeton, port 6443 bloqué, ou même nom d’hôte que le control-plane |
Voir aussi
- V05 - Docker · P08 - Conteneurs avancés (Docker, Kubernetes) · P18 - Haute disponibilité (stockage, BDD, réseau) · P20 - DevSecOps · P10 - Automatisation avec Ansible
- Cours complet : cours de lidstah · cours de lidstah · cours de lidstah · cours de lidstah
- Retour : P00 - Programme pas encore vu