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 kubectl de 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
ComposantRôle
kube-apiserverPorte d’entrée unique : tout passe par l’API
etcdBase clé/valeur qui contient tout l’état du cluster (à sauvegarder !)
schedulerChoisit sur quel nœud lancer chaque pod
controller-managerCompare l’état réel à l’état voulu et corrige
kubeletAgent sur chaque nœud, pilote le moteur de conteneurs
CRIMoteur de conteneurs (containerd, CRI-O ; Docker n’est plus utilisé directement)
CNIRéseau des pods (Flannel, Calico, Cilium)
CoreDNSDNS 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 Ready

4. Les objets à connaître

ObjetRôleAnalogie
NamespaceDossier pour isoler (par client, par projet)Un dossier
PodUn ou plusieurs conteneurs qui vivent ensemble (même IP)Un mini docker-compose
Deployment« N copies de ce pod », mises à jour progressivesLe gérant des pods
StatefulSetComme Deployment, pour les bases de données (noms et volumes stables)
DaemonSetUn pod sur chaque nœud (agents de supervision, logs)
ServiceAdresse stable vers un groupe de pods (ClusterIP interne, NodePort, LoadBalancer)Un répartiteur interne
IngressRoutage HTTP(S) par nom de domaine vers les Services, avec une seule IP publiqueUn reverse proxy
PersistentVolumeClaimDemande de stockage durable (sinon tout est perdu au redémarrage du pod)Un volume Docker
ConfigMap / SecretConfiguration / données sensibles injectées dans les podsFichier .env
RBACQui 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=5

5. Aide-mémoire kubectl

CommandeRôle
kubectl get nodes / pods / svc / deploy / ingress -ALister (-A = tous les namespaces)
kubectl describe pod NOM -n NSDétails + événements (pourquoi ça ne démarre pas)
kubectl logs -f NOM -n NSJournaux d’un pod
kubectl exec -it NOM -n NS -- /bin/shShell dans un pod
kubectl apply -f fichier.yaml / delete -fCréer/mettre à jour / supprimer depuis un fichier
kubectl port-forward svc/site 8080:80 -n demoAccéder à un service depuis ton PC
kubectl config get-contexts / use-contextChanger de cluster
kubectl create secret generic NOM --from-env-file=.env -n NSSecret depuis un fichier .env
kubectl create secret tls NOM --key=privkey.pem --cert=fullchain.pem -n NSSecret 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 prom

Cela 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

  1. Un pod est en état CrashLoopBackOff. Quelles deux commandes lances-tu en premier ?
  2. Pourquoi 3 control-planes et pas 2 ?
  3. Quelle différence entre un Service et un Ingress ?

Erreurs fréquentes / dépannage

État / symptômeCause probable
ImagePullBackOffNom d’image faux, ou registre privé sans secret docker-registry
PendingPas assez de ressources sur les nœuds, ou PVC qui ne trouve pas de stockage
CrashLoopBackOffL’application plante au démarrage : lire les logs
Worker absent de get nodesMauvais jeton, port 6443 bloqué, ou même nom d’hôte que le control-plane

Voir aussi