## Introduction

Dépanner un cluster Kubernetes initialisé avec kubeadm signifie passer d'un symptôme observé à une correction vérifiée sans aggraver la situation. Le chemin le plus rapide n'est presque jamais un `kubeadm reset` aveugle. C'est une séquence de vérifications en lecture seule qui confirment la version exacte, la topologie et l'état des composants avant de modifier le moindre fichier.

Cet article s'adresse aux développeurs, consultants DevOps et petites équipes plateforme qui exploitent des clusters kubeadm autogérés. Il relie les modes de défaillance courants de kubeadm à des commandes concrètes, des sorties attendues, l'interprétation des journaux et des étapes de récupération sûres. Chaque exemple utilise des variables pour les noms d'hôte, les chemins et les versions afin que vous puissiez l'adapter à votre environnement sans exposer de secrets de production.

Les règles directrices sont : observer avant de modifier, limiter le rayon d'impact, vérifier après chaque modification et documenter le plan de récupération avant d'en avoir besoin.

## Inventaire des versions et de l'environnement

Avant de toucher à quoi que ce soit, capturez l'état actuel. Vous devez savoir exactement quelle version de Kubernetes, quelle version de kubeadm, quel runtime de conteneurs et quel système d'exploitation vous utilisez. Enregistrez ensuite la topologie des nœuds et la configuration kubeadm actuelle.

Commencez par des commandes en lecture seule :

```bash
# Versions client et serveur Kubernetes
kubectl version --output=yaml

# Versions kubeadm et kubelet
kubeadm version -o json
kubelet --version

# Système d'exploitation et noyau
cat /etc/os-release
uname -r

# Runtime de conteneurs (exemple : containerd)
ctr version
```

La sortie attendue pour `kubeadm version -o json` sur un nœud sain ressemble à ceci :

```json
{
  "clientVersion": {
    "major": "1",
    "minor": "30",
    "gitVersion": "v1.30.1",
    "gitCommit": "ac3b1c5e035d74a5c1e6f7d4d1abf0e2a8f5e7b1",
    "gitTreeState": "clean",
    "buildDate": "2024-05-15T12:00:00Z",
    "goVersion": "go1.22.2",
    "compiler": "gc",
    "platform": "linux/amd64"
  }
}
```

Si `kubeadm version` échoue avec `command not found`, alors kubeadm n'est pas installé sur ce nœud. C'est un problème différent d'un décalage de version. Installez le paquet approprié pour votre système d'exploitation, mais seulement après avoir documenté que ce nœud ne fait pas encore partie du plan de contrôle.

Vérifiez maintenant la topologie du cluster et l'état des nœuds :

```bash
# Depuis un nœud du plan de contrôle fonctionnel
kubectl get nodes -o wide

# Informations détaillées sur un nœud
kubectl describe node control-plane-1
```

Recherchez les conditions `NotReady` des nœuds. `kubectl describe node` affiche la raison sous `Conditions`. Les raisons courantes sont `KubeletNotReady`, `ContainerRuntimeUnhealthy` ou `NetworkUnavailable`.

Ensuite, enregistrez la configuration kubeadm active. La configuration du cluster générée pendant `kubeadm init` est stockée dans la ConfigMap `kube-system/kubeadm-config`. Obtenez-la en lecture seule :

```bash
kubectl -n kube-system get configmap kubeadm-config -o yaml
```

Faites attention à `ClusterConfiguration.kubernetesVersion`, `ClusterConfiguration.controlPlaneEndpoint` et `ClusterConfiguration.networking.podSubnet`. Par exemple :

```yaml
apiVersion: kubeadm.k8s.io/v1beta3
kind: ClusterConfiguration
kubernetesVersion: v1.30.1
controlPlaneEndpoint: "kube-apiserver.example.com:6443"
networking:
  podSubnet: "10.244.0.0/16"
  serviceSubnet: "10.96.0.0/12"
```

Ne modifiez pas cette ConfigMap directement. Copiez-la dans un fichier local pour référence uniquement.

Enfin, enregistrez les horodatages pour une corrélation ultérieure. `date -u` affiche l'heure UTC actuelle. Ajoutez toutes ces informations à vos notes d'incident.

## Chemin de configuration sûr

Kubeadm stocke la configuration du cluster à trois endroits que vous pourriez avoir besoin de modifier : le fichier de configuration kubeadm sur le disque, la ConfigMap kubeadm-config et la configuration du kubelet. Ne modifiez qu'un seul de ces éléments à la fois et gardez toujours une sauvegarde.

### Sauvegarder avant de modifier

Pour les fichiers sur le disque, utilisez `cp -a` pour préserver les permissions et la propriété :

```bash
sudo cp -a /etc/kubernetes/kubelet.conf /etc/kubernetes/kubelet.conf.bak-$(date +%Y%m%d)
sudo cp -a /var/lib/kubelet/config.yaml /var/lib/kubelet/config.yaml.bak-$(date +%Y%m%d)
```

Pour la ConfigMap kubeadm-config, exportez une copie propre :

```bash
kubectl -n kube-system get configmap kubeadm-config -o yaml > kubeadm-config-backup.yaml
```

Stockez les sauvegardes en dehors de `/etc/kubernetes` afin qu'un reset raté ne les supprime pas.

### Modifier la configuration du kubelet en toute sécurité

Le fichier de configuration du kubelet `/var/lib/kubelet/config.yaml` contrôle le comportement d'exécution tel que `maxPods`, `systemReserved` et `evictionHard`. Par exemple, pour augmenter le nombre maximal de pods par nœud de la valeur par défaut 110 à 150 :

Extrait du fichier original :

```yaml
apiVersion: kubelet.config.k8s.io/v1beta1
kind: KubeletConfiguration
maxPods: 110
```

Modifiez uniquement le champ `maxPods` à `150`. Redémarrez ensuite kubelet :

```bash
sudo systemctl restart kubelet
sudo systemctl status kubelet --no-pager
```

Vérifiez que la configuration active a pris effet en examinant les drapeaux du processus kubelet :

```bash
ps -ef | grep kubelet | grep -o 'config.yaml'
# Sortie attendue contient : --config=/var/lib/kubelet/config.yaml
```

Confirmez également que le nœud rapporte toujours Ready :

```bash
kubectl get node worker-1
# Attendu : NAME       STATUS   ROLES    AGE   VERSION
#          worker-1   Ready    <none>   12d   v1.30.1
```

Si le nœud devient NotReady ou si kubelet ne démarre pas, annulez la modification en restaurant la sauvegarde et en redémarrant kubelet à nouveau.

### Mettre à jour la configuration du cluster kubeadm

Les mises à niveau à l'échelle du cluster ou les changements de feature gates se font avec `kubeadm upgrade apply` ou `kubeadm upgrade node`. Ne modifiez pas la ConfigMap kubeadm-config à la main sauf en cas d'urgence, et même alors documentez pendant l'opération.

Pour prévisualiser une mise à niveau sans rien changer :

```bash
sudo kubeadm upgrade plan
```

La sortie attendue montre à la fois la version actuelle et la version cible de Kubernetes, par exemple :

```text
COMPONENT   CURRENT   TARGET
kubelet     v1.29.2   v1.30.1
kubeadm     v1.29.2   v1.30.1
kubectl     v1.29.2   v1.30.1
```

N'appliquez jamais une mise à niveau sans lire les notes de version pour les changements cassants spécifiques à votre environnement.

## Vérification et diagnostic

Une fois que vous comprenez l'état actuel, utilisez des diagnostics ciblés pour identifier le composant défaillant. Les sources les plus courantes de problèmes kubeadm sont le kubelet qui ne démarre pas, les pods statiques du plan de contrôle qui ne deviennent pas prêts et les problèmes de plugin réseau.

### Vérifier l'état et les journaux du kubelet

Le kubelet est la première chose à échouer lorsque les certificats expirent, que le runtime de conteneurs est indisponible ou que la configuration du kubelet est invalide.

```bash
sudo systemctl status kubelet --no-pager -l
```

Un kubelet sain affiche `active (running)`. S'il est dans l'état `failed`, consultez les journaux :

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

Messages d'erreur courants et leurs causes probables :

- `failed to run Kubelet: misconfiguration: kubelet cgroup driver: "systemd" is different from docker cgroup driver: "cgroupfs"` -- incompatibilité de pilote cgroup. Corrigez en définissant `cgroupDriver: systemd` dans la configuration du kubelet ou du runtime de conteneurs.
- `error: failed to start container "kubelet": Error response from daemon: ...` -- le runtime de conteneurs ne fonctionne pas ou est mal configuré. Vérifiez `sudo systemctl status containerd` ou `sudo systemctl status docker`.
- `x509: certificate signed by unknown authority` -- signifie souvent que le CA du cluster a changé ou que `kubelet.conf` est corrompu. Régénérez avec `kubeadm init phase kubeconfig kubelet` ou copiez depuis un autre nœud, mais soyez extrêmement prudent avec les secrets.

### Inspecter les pods statiques du plan de contrôle

Les composants du plan de contrôle s'exécutent comme pods statiques dans `/etc/kubernetes/manifests`. S'ils plantent, le cluster perd l'accès à l'API.

Listez les pods statiques :

```bash
ls -la /etc/kubernetes/manifests/
```

Fichiers attendus : `etcd.yaml`, `kube-apiserver.yaml`, `kube-controller-manager.yaml`, `kube-scheduler.yaml`.

Consultez les journaux du pod de l'API server même si kubectl ne répond pas :

```bash
# Utilisation de crictl car kubelet gère les pods statiques via CRI
sudo crictl ps -a | grep kube-apiserver
```

Obtenez l'ID du conteneur puis :

```bash
sudo crictl logs <container-id> --tail=100
```

Surveillez les erreurs comme `etcdserver: request timed out` (etcd en panne), `connection refused` (réseau) ou `failed to create listener: failed to listen on 0.0.0.0:6443` (port déjà utilisé).

### Vérifier le point de terminaison de l'API server

Depuis un nœud du plan de contrôle, testez l'API server local :

```bash
curl -k https://127.0.0.1:6443/healthz?verbose
```

La sortie attendue inclut `[+]ping ok` et `[+]log ok`. Si vous obtenez `connection refused`, l'API server ne fonctionne pas. Si vous obtenez une erreur de certificat, cela peut être un décalage d'heure ou de CA.

### Vérifier le réseau du cluster

Une défaillance du réseau des pods se manifeste par `CNI plugin not initialized` dans les journaux du kubelet ou des nœuds bloqués NotReady après la jointure. Confirmez que les pods de votre plugin CNI fonctionnent :

```bash
kubectl get pods -n kube-system | grep -E 'calico|flannel|weave|cilium'
```

Pour Flannel, les pods attendus sont `kube-flannel-ds-xxxxx` avec `1/1 Running`. S'ils sont en `CrashLoopBackOff`, consultez leurs journaux :

```bash
kubectl -n kube-system logs ds/kube-flannel-ds --tail=50
```

Erreur Flannel courante : `failed to find any valid interface to use` lorsque plusieurs interfaces existent. Corrigez en ajoutant `--iface=eth1` au daemonset flannel ou en définissant `--iface-regex`.

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

Lorsque quelque chose est déjà cassé, suivez un chemin de récupération structuré. L'ordre compte : rétablissez d'abord le service, puis enquêtez sur la cause profonde, puis appliquez la prévention.

### Mode de défaillance 1 : échec de la rotation des certificats du kubelet

**Symptôme** : Les nœuds deviennent NotReady, `kubectl get nodes` affiche `SchedulingDisabled` ou `NotReady` pour tous les workers, les journaux du kubelet montrent `certificate has expired or is not yet valid`.

**Cause** : Les certificats client du kubelet sont valables 1 an par défaut et peuvent échouer à tourner si l'approbation de la demande de signature de certificat (CSR) n'est pas automatique.

**Récupération** :

1. Vérifiez les CSR en attente :

```bash
kubectl get csr
```

La sortie attendue montre des CSR en attente avec le statut `Pending` pour chaque nœud.

2. Approuvez tous les CSR en attente :

```bash
kubectl get csr --no-headers | awk '{print $1}' | xargs -I {} kubectl certificate approve {}
```

3. Vérifiez que les nœuds reviennent à Ready :

```bash
kubectl get nodes
```

Si l'approbation automatique des CSR est absente, implémentez un contrôleur comme `kubelet-csr-approver` ou ajustez les drapeaux de votre `kube-controller-manager`.

### Mode de défaillance 2 : répertoire de données etcd corrompu

**Symptôme** : `kubectl get nodes` renvoie `The connection to the server 192.168.1.10:6443 was refused - did you specify the right host or port?` et les journaux du pod API server montrent `etcdmain: cannot access data directory`.

**Cause** : Disque plein sur le répertoire de données etcd ou suppression accidentelle de `/var/lib/etcd`.

**Récupération** :

1. Confirmez que le pod etcd est en boucle de crash :

```bash
sudo crictl ps -a | grep etcd
```

2. Vérifiez l'espace disque :

```bash
df -h /var/lib/etcd
```

3. Si le disque est plein, arrêtez le pod etcd en déplaçant temporairement le manifeste :

```bash
sudo mv /etc/kubernetes/manifests/etcd.yaml /tmp/etcd.yaml.bak
```

4. Libérez de l'espace ou restaurez à partir d'une sauvegarde etcd. Si aucune sauvegarde, vous devez reconstruire le cluster. C'est pourquoi les sauvegardes etcd sont non négociables.

5. Restaurez etcd à partir d'un instantané (seulement si vous en avez un) :

```bash
sudo ETCDCTL_API=3 etcdctl snapshot restore /backup/etcd-snapshot.db --data-dir /var/lib/etcd-restore
sudo mv /var/lib/etcd-restore /var/lib/etcd
sudo mv /tmp/etcd.yaml.bak /etc/kubernetes/manifests/etcd.yaml
```

6. Attendez que l'API server revienne, puis vérifiez avec `kubectl get nodes`.

### Mode de défaillance 3 : échec de la jointure d'un nœud worker avec erreur de jeton

**Symptôme** : Sur un nouveau worker, `kubeadm join` se termine par `error execution phase preflight: couldn't validate the identity of the API Server: expected a 32byte but got 0` ou `token not found`.

**Cause** : Le jeton de jointure a expiré (24 heures par défaut) ou le hachage CA ne correspond pas.

**Récupération** :

1. Sur le plan de contrôle, créez un nouveau jeton :

```bash
sudo kubeadm token create --print-join-command
```

Sortie attendue :

```text
kubeadm join 10.0.0.10:6443 --token abcdef.0123456789abcdef --discovery-token-ca-cert-hash sha256:1a2b3c...
```

2. Exécutez cette commande exacte sur le nouveau worker avec sudo.

3. Vérifiez que le nœud se joint :

```bash
kubectl get nodes
```

Si le hachage CA ne correspond toujours pas, vous pouvez avoir plusieurs clusters ou un `ca.crt` obsolète. Inspectez manuellement le hachage du certificat :

```bash
openssl x509 -pubkey -in /etc/kubernetes/pki/ca.crt | openssl rsa -pubin -outform der 2>/dev/null | openssl dgst -sha256 -hex | sed 's/^.* //'
```

Comparez avec le hachage dans la commande de jointure.

## Liste de contrôle opérationnelle

Utilisez cette liste de contrôle lors de toute session de dépannage kubeadm. Elle est ordonnée du moins invasif au plus invasif.

| Étape | Action | Commande | Résultat attendu | Récupération en cas d'échec |
| --- | --- | --- | --- | --- |
| 1 | Vérifier les versions | `kubeadm version -o json` | Sortie JSON de version | Installer ou mettre à niveau kubeadm |
| 2 | Vérifier l'état des nœuds | `kubectl get nodes` | Tous les nœuds Ready | Diagnostiquer kubelet, runtime ou réseau |
| 3 | Vérifier l'état du kubelet | `sudo systemctl status kubelet` | `active (running)` | Redémarrer kubelet, inspecter les journaux |
| 4 | Vérifier la santé de l'API | `curl -k https://127.0.0.1:6443/healthz?verbose` | `ok` pour tous les contrôles | Inspecter les journaux du pod API server |
| 5 | Vérifier les pods statiques | `sudo crictl ps -a \| grep -E 'etcd\|kube-api\|kube-controller\|kube-scheduler'` | Tous en cours d'exécution | Redémarrer kubelet pour re-tirer les manifestes |
| 6 | Vérifier les CSR en attente | `kubectl get csr` | Aucun CSR en attente | Approuver les CSR ou installer un approbateur |
| 7 | Vérifier l'espace disque | `df -h /var/lib/etcd /var/lib/kubelet` | > 20% libre | Nettoyer ou étendre le disque |
| 8 | Vérifier les journaux des pods | `kubectl -n kube-system logs <pod-name> --tail=50` | Aucune erreur répétée | Corriger le problème sous-jacent, puis redémarrer le pod |

Parcourez la liste de contrôle de haut en bas. Ne sautez pas à l'étape 8 sans faire les étapes 1 à 7 d'abord. La liste de contrôle est également utile comme document de revue post-incident.

## Conclusion

Le dépannage kubeadm n'est pratique que lorsque chaque étape est versionnée, observable et réversible là où la technologie le permet. Copier une commande d'un forum sans comprendre la sortie attendue et le chemin de récupération n'est pas une procédure opérationnelle ; c'est un pari.

Comme prochaine étape, choisissez un diagnostic à faible risque de cet article, comme vérifier `kubeadm version -o json` ou `kubectl get csr`. Enregistrez l'état actuel, exécutez la commande, comparez le résultat avec le signal attendu et notez ce que vous feriez en cas d'échec.

Un flux de travail fiable rend la défaillance visible, protège les valeurs sensibles, limite les changements à la ressource prévue et définit la vérification de récupération avant qu'un incident ne force la décision. Gardez ces habitudes en place, et vos clusters kubeadm passeront beaucoup moins de temps dans des états de défaillance mystérieux.