## Introduction

Kubernetes masque de nombreux détails d'infrastructure, mais les défaillances du runtime de conteneurs peuvent arrêter complètement votre cluster. Le runtime de conteneurs est le logiciel qui exécute réellement vos conteneurs : il extrait les images, gère leur cycle de vie et assure l'isolation. Les runtimes les plus courants en production aujourd'hui sont containerd et CRI-O, Docker étant une option héritée. Lorsque le runtime se comporte mal, vous verrez des erreurs comme `FailedCreatePodSandBox`, `ErrImagePull` ou `CreateContainerConfigError`. Celles-ci apparaissent souvent sous forme d'événements de pod, mais la cause racine se situe dans la configuration ou l'environnement du runtime.

Ce guide présente les erreurs de runtime de conteneurs les plus fréquentes, comment les diagnostiquer et les corriger en toute sécurité. Vous apprendrez à vérifier votre configuration de runtime, à apporter des modifications ciblées et à récupérer après des tentatives échouées. Chaque correction comprend une commande concrète avec la sortie attendue, afin que vous puissiez l'appliquer à votre propre cluster en toute confiance.

## Inventaire des versions et de l'environnement

Avant de modifier quoi que ce soit, rassemblez les versions exactes et la configuration du runtime. Cela évite les incompatibilités et facilite le retour arrière. Les commandes suivantes fonctionnent sur n'importe quel nœud Linux.

### Vérifier le runtime actif

Kubernetes utilise l'interface CRI (Container Runtime Interface) pour communiquer avec le runtime. Déterminez quel runtime votre nœud utilise :

```bash
ps aux | grep -E 'containerd|crio|dockerd' | grep -v grep
```

Sortie attendue sur un nœud avec containerd :

```text
root 1234 0.1 2.3 1234567 234567 ? Ssl Jan01 12:34 /usr/bin/containerd
```

Si vous voyez `crio` à la place, ajustez les commandes suivantes en conséquence. Pour Docker, notez qu'il utilise un shim (`dockershim`), qui est obsolète.

### Confirmer la version du runtime

Chaque runtime fournit une commande de version. Pour containerd :

```bash
containerd --version
```

Sortie attendue :

```text
containerd github.com/containerd/containerd v1.7.11
```

Pour CRI-O :

```bash
crictl version
```

La sortie attendue inclut les versions client et serveur. Si `crictl` n'est pas installé, installez-le depuis la page des releases Kubernetes ; c'est un outil de diagnostic essentiel.

### Vérifier le socket CRI

Le kubelet doit se connecter au runtime via un socket Unix. Vérifiez le point de terminaison configuré :

```bash
ps aux | grep kubelet | grep -o '\(container-runtime-endpoint=[^ ]*\)'
```

Sortie attendue :

```text
--container-runtime-endpoint=unix:///run/containerd/containerd.sock
```

Si vous voyez `unix:///var/run/crio/crio.sock`, cela indique CRI-O. Notez le chemin pour plus tard.

### Vérifier les fichiers de configuration du runtime

Les fichiers de configuration du runtime sont souvent la source d'erreurs. Pour containerd, la configuration par défaut est `/etc/containerd/config.toml`. Vérifiez les modifications récentes :

```bash
stat /etc/containerd/config.toml
```

Regardez l'heure de modification pour voir si elle a changé avant l'incident. Pour CRI-O, le fichier de configuration principal est `/etc/crio/crio.conf`.

### Vérifier l'état du nœud

Utilisez `kubectl` pour voir les conditions du nœud, qui incluent la santé du runtime :

```bash
kubectl describe node <nom-du-nœud> | grep -A5 Conditions
```

Recherchez `ContainerRuntimeIsReady` ou `KubeletReady`. Si c'est `False` ou `Unknown`, le runtime est défaillant.

### Vérifier l'état du runtime avec systemctl

Si le service du runtime ne fonctionne pas, rien d'autre ne compte. Vérifiez avec :

```bash
systemctl status containerd
```

Sortie attendue en bonne santé :

```text
● containerd.service - containerd container runtime
   Loaded: loaded (/usr/lib/systemd/system/containerd.service; enabled; vendor preset: disabled)
   Active: active (running) since Fri 2025-04-18 10:15:22 UTC; 1 day ago
```

Si c'est `failed` ou `inactive`, consultez les journaux avec `journalctl -u containerd -n 50 --no-pager`.

### Documenter votre base de référence

| Composant | Exemple de commande | Sortie saine typique |
|-----------|-----------------|------------------------|
| Processus du runtime | `ps aux | grep containerd` | Processus listé avec chemin |
| Version du runtime | `containerd --version` | Chaîne de version, par exemple v1.7.11 |
| Socket CRI | `ls -l /run/containerd/containerd.sock` | Le fichier socket existe |
| Préparation du nœud | `kubectl get node <nœud>` | Statut `Ready` |

Cet inventaire vous donne une base de référence. Si une correction échoue, vous pouvez comparer avec ces valeurs.

## Chemin de configuration sûr

La configuration du runtime de conteneurs est sensible. Une petite erreur peut empêcher tous les pods de démarrer. Suivez une approche ciblée : modifiez un paramètre à la fois, testez dans un environnement hors production et ayez un plan de retour arrière.

### Erreurs courantes du runtime et leurs causes

Avant d'apporter des modifications, comprenez les catégories d'erreurs :

| Message d'erreur | Cause probable | Composant du runtime |
|---------------|--------------|-------------------|
| `FailedCreatePodSandBox` | Plugin CNI manquant ou mal configuré | Plugin réseau |
| `ErrImagePull` / `ImagePullBackOff` | Problème d'authentification au registre ou de réseau | Extraction d'image |
| `CreateContainerConfigError` | Spécification de conteneur invalide, par exemple ConfigMap manquante | Kubelet / runtime |
| `runc run failed: unable to start container process` | Problème de seccomp, AppArmor ou de capacités | Runtime OCI (runc) |
| `cgroups: cgroup mountpoint does not exist` | Discordance du pilote cgroup systemd | Configuration cgroup |

Nous traiterons chacune avec une correction concrète.

### Correction 1 : Configurer le bon pilote cgroup

Un problème fréquent est une discordance entre le pilote cgroup utilisé par le runtime et le kubelet. Sur les systèmes basés sur systemd, les deux doivent utiliser `systemd`. Vérifiez le pilote du kubelet :

```bash
ps aux | grep kubelet | grep -o 'cgroup-driver=[^ ]*'
```

Si cela renvoie `--cgroup-driver=cgroupfs` mais que votre runtime utilise `systemd`, vous verrez des erreurs comme :

```text
failed to create container: cgroup manager: failed to create cgroup: cgroups: cgroup mountpoint does not exist
```

Pour containerd, modifiez `/etc/containerd/config.toml` :

```bash
sudo vi /etc/containerd/config.toml
```

Assurez-vous que la section suivante est présente :

```toml
[plugins."io.containerd.grpc.v1.cri".containerd.runtimes.runc.options]
  SystemdCgroup = true
```

Puis redémarrez containerd :

```bash
sudo systemctl restart containerd
```

Redémarrez kubelet :

```bash
sudo systemctl restart kubelet
```

Vérifiez avec :

```bash
sudo journalctl -u kubelet -n 20 | grep -i cgroup
```

Vous ne devriez voir aucune erreur cgroup. Sur CRI-O, définissez `cgroup_manager = "systemd"` dans `/etc/crio/crio.conf` sous `[crio.runtime]`.

### Correction 2 : Configurer les secrets d'extraction d'image

De nombreuses erreurs `ErrImagePull` sont dues à des identifiants manquants pour les registres privés. Créez un secret :

```bash
kubectl create secret docker-registry my-registry-secret \
  --docker-server=myregistry.example.com \
  --docker-username=myuser \
  --docker-password=mypassword \
  --docker-email=my@example.com
```

Puis référencez-le dans la spécification de votre pod :

```yaml
spec:
  imagePullSecrets:
    - name: my-registry-secret
  containers:
    - name: app
      image: myregistry.example.com/app: v1
```

Appliquez et vérifiez :

```bash
kubectl apply -f pod.yaml
kubectl get pod <nom-du-pod> -o wide
```

Si le pod est `Running`, l'extraction a réussi. Sinon, vérifiez les événements : `kubectl describe pod <nom-du-pod>` et recherchez `Failed to pull image` avec une raison comme `401 Unauthorized` ou `dial tcp: lookup myregistry.example.com: no such host`.

### Correction 3 : Configurer le plugin réseau CNI

Un plugin réseau cassé provoque des erreurs `FailedCreatePodSandBox`. Vérifiez que le répertoire de configuration CNI existe et contient une configuration valide :

```bash
ls /etc/cni/net.d/
cat /etc/cni/net.d/*.conf
```

Une configuration CNI typique pour Calico ou Flannel inclut un champ `type`. Si le fichier est manquant, installez un plugin CNI. Par exemple, pour installer Calico :

```bash
kubectl apply -f https://docs.projectcalico.org/manifests/calico.yaml
```

Après l'installation, vérifiez que les pods CNI fonctionnent :

```bash
kubectl get pods -n kube-system | grep calico
```

Puis testez la création d'un pod :

```bash
kubectl run test-pod --image=nginx --restart=Never
kubectl get pod test-pod
```

S'il passe à `Running`, le réseau est corrigé.

### Correction 4 : Configurer RuntimeClass (optionnel)

Pour les charges de travail spécialisées, vous pouvez définir une RuntimeClass pour sélectionner un runtime différent. Créez un objet RuntimeClass :

```yaml
apiVersion: node.k8s.io/v1
kind: RuntimeClass
metadata:
  name: gvisor
handler: runsc
```

Appliquez-le et référencez-le dans un pod :

```bash
kubectl apply -f runtimeclass.yaml
```

Dans la spécification du pod :

```yaml
spec:
  runtimeClassName: gvisor
  containers:
    - name: test
      image: nginx
```

Si le gestionnaire n'est pas installé, vous obtiendrez une erreur comme `Failed to create pod sandbox: unknown runtime handler`. Assurez-vous que le gestionnaire de runtime est configuré sur chaque nœud.

## Vérification et diagnostics

Après avoir appliqué une correction, vérifiez que le runtime est sain et que les pods peuvent s'exécuter. Utilisez ces commandes pour confirmer.

### Vérifier le service du runtime

Vérifiez que le service du runtime est actif et ne redémarre pas :

```bash
systemctl status containerd --no-pager -l
```

Recherchez `Active: active (running)` et aucune erreur récente dans les journaux :

```bash
journalctl -u containerd -n 50 --no-pager
```

### Vérifier la connectivité CRI

Utilisez `crictl` pour parler directement au runtime via le CRI. Vérifiez la version du runtime :

```bash
crictl version
```

La sortie attendue montre les versions client et serveur. Ensuite, listez les pods actuels du point de vue du runtime :

```bash
crictl pods
```

Vous devriez voir une liste de pods. Si cela renvoie une erreur comme `connection refused`, le chemin du socket peut être incorrect. Vérifiez la configuration du kubelet et ajustez le point de terminaison `crictl` :

```bash
export CONTAINER_RUNTIME_ENDPOINT=unix:///run/containerd/containerd.sock
crictl pods
```

### Vérifier le cycle de vie des pods

Créez un pod de test simple et observez son état :

```bash
kubectl run runtime-test --image=nginx --restart=Never
kubectl get pod runtime-test -w
```

Attendez qu'il soit `Running` (ou `Completed` si vous remplacez la commande). S'il reste en `Pending` ou `ContainerCreating`, vérifiez les événements :

```bash
kubectl describe pod runtime-test
```

Recherchez des événements comme `FailedCreatePodSandBox` ou `Failed to pull image`. Une fois vérifié, supprimez le pod de test :

```bash
kubectl delete pod runtime-test
```

### Vérifier les conditions du nœud

Après une correction, attendez un moment et vérifiez à nouveau les conditions du nœud :

```bash
kubectl describe node <nom-du-nœud> | grep -A5 Conditions
```

`ContainerRuntimeIsReady` doit être `True`, et le nœud doit être `Ready`.

### Résumé des commandes de diagnostic

| Vérification | Commande | Sortie saine attendue |
|-------|---------|-------------------------|
| Statut du service du runtime | `systemctl status containerd` | active (running) |
| Connectivité CRI | `crictl version` | Version du serveur renvoyée |
| Test de création de pod | `kubectl run ... --image=nginx` | Le pod devient Running |
| Condition du nœud | `kubectl describe node` | RuntimeReady=True |

## Modes de défaillance et récupération

Même avec des changements prudents, les choses peuvent mal tourner. Voici des scénarios de défaillance courants après un changement de configuration du runtime et comment récupérer.

### Défaillance : le runtime ne démarre pas après un changement de configuration

Si containerd ne démarre pas après avoir modifié `config.toml`, vérifiez les journaux pour des erreurs de syntaxe :

```bash
sudo journalctl -u containerd -n 50 --no-pager
```

Recherchez des lignes comme `failed to load config: near line 10: unexpected token`. Faites un retour arrière en restaurant le fichier de configuration précédent :

```bash
sudo cp /etc/containerd/config.toml.backup /etc/containerd/config.toml
sudo systemctl restart containerd
```

Puis redémarrez kubelet :

```bash
sudo systemctl restart kubelet
```

### Défaillance : le kubelet ne peut pas se connecter au runtime

Si le chemin du socket CRI a changé ou si le runtime est arrêté, le kubelet renverra une erreur. Vérifiez les journaux du kubelet :

```bash
sudo journalctl -u kubelet -n 50 --no-pager
```

Recherchez `failed to connect to containerd` ou `dial unix /run/containerd/containerd.sock: connect: no such file or directory`. Vérifiez que le socket existe :

```bash
ls -l /run/containerd/containerd.sock
```

S'il est manquant, assurez-vous que containerd fonctionne. Si le chemin du socket est incorrect dans la configuration du kubelet, mettez à jour `/var/lib/kubelet/config.yaml` ou le drop-in du kubelet. Puis redémarrez le kubelet.

### Défaillance : pods bloqués en état Terminating

Si un pod ne peut pas être supprimé parce que le runtime ne répond pas, forcez la suppression du pod :

```bash
kubectl delete pod <nom-du-pod> --force --grace-period=0
```

Si cela ne fonctionne pas, redémarrez le runtime :

```bash
sudo systemctl restart containerd
```

Ensuite, nettoyez les conteneurs orphelins avec `crictl rm` :

```bash
crictl ps -a | grep <nom-du-pod>
crictl rm <id-du-conteneur>
```

### Planification du retour arrière

Faites toujours une sauvegarde des configurations du runtime avant de les modifier :

```bash
sudo cp /etc/containerd/config.toml /etc/containerd/config.toml.backup
```

Pour CRI-O :

```bash
sudo cp /etc/crio/crio.conf /etc/crio/crio.conf.backup
```

Notez également les modifications exactes apportées, afin de pouvoir les annuler manuellement si nécessaire.

## Liste de contrôle des opérations

Utilisez cette liste de contrôle pour auditer régulièrement votre runtime de conteneurs et accélérer le dépannage.

### Vérifications quotidiennes ou hebdomadaires

- Vérifiez que le service du runtime est actif : `systemctl is-active containerd` doit renvoyer `active`.
- Vérifiez la préparation des nœuds : `kubectl get nodes` doit montrer tous les nœuds `Ready`.
- Surveillez les journaux du runtime pour des erreurs répétées : `journalctl -u containerd --since "24 hours ago" | grep -i error`.
- Vérifiez l'espace disque sur `/var/lib/containerd` (stockage des images) et `/var/lib/kubelet` (stockage des pods) avec `df -h`.
- Assurez-vous que les pods CNI critiques fonctionnent : `kubectl get pods -n kube-system | grep -E 'calico|flannel|weave|cilium'`.

### Avant d'apporter des modifications de configuration

- Sauvegardez les fichiers de configuration actuels du runtime.
- Enregistrez la version actuelle du runtime et du kubelet.
- Testez les modifications dans un environnement de préproduction d'abord.
- Planifiez les étapes de retour arrière.
- Planifiez une fenêtre de maintenance si nécessaire.

### Après avoir apporté des modifications de configuration

- Redémarrez le service du runtime et vérifiez son état.
- Redémarrez kubelet et vérifiez son état.
- Vérifiez la connectivité CRI avec `crictl version`.
- Exécutez un pod de test pour confirmer la fonctionnalité.
- Vérifiez les conditions du nœud pour la préparation du runtime.
- Surveillez les journaux pour de nouvelles erreurs pendant au moins 10 minutes.

### Audit mensuel

- Examinez la configuration du runtime par rapport aux meilleures pratiques (par exemple, pilote cgroup, paramètres CNI).
- Recherchez des mises à jour du runtime et appliquez-les de manière contrôlée.
- Testez la création, la suppression et l'exécution de pods.
- Validez l'extraction d'images depuis tous les registres utilisés.

## Pièges courants et idées fausses

Même les opérateurs expérimentés tombent dans ces pièges récurrents. Les comprendre à l'avance peut économiser des heures de débogage.

### Piège 1 : Mélanger les pilotes cgroup

**Pourquoi cela arrive :** Vous installez un runtime qui utilise `cgroupfs` par défaut sur un hôte systemd, ou vous copiez une configuration Kubernetes d'un ancien déploiement sans mettre à jour le pilote.

**Comment éviter :** Définissez toujours à la fois kubelet et runtime sur `systemd` sur les systèmes d'exploitation avec systemd. Ajoutez une vérification avant le déploiement :

```bash
# Vérifier le pilote du runtime
containerd config dump | grep -i systemd_cgroup
# Vérifier le pilote du kubelet
ps aux | grep kubelet | grep -o 'cgroup-driver=[^ ]*'
```

S'ils diffèrent, corrigez avant de déployer des charges de travail.

**Récupération :** Appliquez la correction 1, puis redémarrez les services. Vérifiez en créant un pod de test ; s'il échoue toujours, inspectez `journalctl -u kubelet` pour des erreurs cgroup.

### Piège 2 : Modifier la configuration du runtime sans sauvegardes

**Pourquoi cela arrive :** Vous avez besoin d'une correction rapide pendant un incident et sautez l'étape de sauvegarde.

**Comment éviter :** Intégrez les sauvegardes dans votre processus de changement. Utilisez une copie horodatée :

```bash
sudo cp /etc/containerd/config.toml /etc/containerd/config.toml.$(date +%Y%m%d%H%M%S).bak
```

**Récupération :** Si vous n'avez pas sauvegardé, vérifiez le gestionnaire de paquets pour la configuration d'origine : `rpm -V containerd` ou `dpkg -S containerd`. Pour un système en cours d'exécution, vous pouvez extraire la configuration actuelle avec `containerd config dump` et l'enregistrer.

### Piège 3 : Supposer que CNI est toujours préinstallé

**Pourquoi cela arrive :** Les services Kubernetes gérés installent souvent CNI automatiquement, mais les clusters auto-gérés nécessitent une configuration manuelle.

**Comment éviter :** Après l'amorçage du cluster, vérifiez immédiatement que les pods CNI fonctionnent et testez avec un pod exemple avant de déployer des charges de travail de production.

**Récupération :** Installez un plugin CNI comme décrit dans la correction 3. Si les pods sont bloqués en `ContainerCreating`, vérifiez les événements pour des erreurs de sandbox.

### Piège 4 : Ignorer la rotation des journaux du runtime

**Pourquoi cela arrive :** Le runtime écrit les journaux sur le disque sans rotation, remplissant le système de fichiers au fil du temps.

**Comment éviter :** Configurez la rotation des journaux dans la configuration du runtime. Pour containerd, définissez :

```toml
[plugins."io.containerd.grpc.v1.cri".containerd]
  max_container_log_line_size = 16384
```

Et assurez-vous que les limites de journald de systemd sont appropriées. Surveillez régulièrement l'utilisation du disque.

**Récupération :** Si le disque est plein, élaguez les images inutilisées (`crictl rmi --prune`) et supprimez les anciens journaux. Ensuite, configurez la rotation pour éviter la récurrence.

### Piège 5 : Exécuter des conteneurs en tant que root par défaut

**Pourquoi cela arrive :** De nombreuses images de conteneurs utilisent par défaut l'utilisateur root, augmentant le rayon d'impact d'une évasion.

**Comment éviter :** Appliquez la non-root avec des politiques de sécurité des pods ou des contrôleurs d'admission. Dans la spécification de votre pod :

```yaml
securityContext:
  runAsNonRoot: true
  runAsUser: 1000
```

**Récupération :** Si un conteneur s'exécute en tant que root, mettez à jour l'image ou le contexte de sécurité. Vous pouvez également définir une RuntimeClass par défaut avec `runAsNonRoot` appliqué.

## Conclusion

Les erreurs de runtime de conteneurs peuvent être intimidantes, mais elles sont généralement causées par quelques mauvaises configurations courantes. En vérifiant méthodiquement les pilotes cgroup, les paramètres d'extraction d'images, le réseau CNI et la santé du runtime, vous pouvez résoudre la plupart des problèmes rapidement. Travaillez toujours avec un chemin de configuration sûr : apportez une modification à la fois, vérifiez avec des commandes concrètes et ayez un plan de retour arrière. La liste de contrôle fournie vous aidera à maintenir un runtime sain et à minimiser les temps d'arrêt. Avec ces exemples pratiques et ces commandes de diagnostic, vous pouvez dépanner et corriger en toute confiance les erreurs de runtime de conteneurs Kubernetes dans votre propre environnement.