## Introduction

Déboguer un conteneur Docker mal configuré consiste rarement à trouver une commande magique. Il s'agit de construire une séquence reproductible : observer l'état actuel, identifier le plus petit changement possible, l'appliquer de manière contrôlée, vérifier le résultat et savoir revenir en arrière si la situation empire. Ce guide passe en revue les erreurs de configuration courantes — réseau, montages, variables d'environnement, contrôles de santé et limites de ressources — en utilisant des commandes réelles et des sorties attendues pour passer rapidement du symptôme à la solution.

Nous nous concentrerons sur un débogage pratique, orienté production. L'objectif est la sécurité opérationnelle : ne jamais modifier un conteneur en cours d'exécution à l'aveugle, protéger les secrets, limiter le rayon d'impact et toujours avoir un plan de récupération documenté avant d'en avoir besoin.

## Inventaire de la version et de l'environnement

Avant de toucher à quoi que ce soit, sachez exactement avec quoi vous travaillez. Exécutez ces commandes en lecture seule et capturez la sortie dans vos notes d'incident avec un horodatage.

**Vérifiez la version de Docker et les informations du démon :**
```bash
docker version
```
La sortie attendue inclut les versions client et serveur. Par exemple :
```
Client: Docker Engine - Community
 Version:           24.0.7
 API version:       1.43
 Go version:        go1.20.10
 Git commit:        311b9ff
 Built:             Thu Oct 26 09:08:15 2023
 OS/Arch:           linux/amd64
 Context:           default

Server: Docker Engine - Community
 Engine:
  Version:          24.0.7
```
Si les versions client et serveur diffèrent de manière significative, certaines fonctionnalités peuvent se comporter de manière inattendue. Mettez à niveau ou rétrogradez pour correspondre au standard de l'équipe.

**Listez les conteneurs en cours d'exécution avec les métadonnées clés :**
```bash
docker ps --format "table {{.Names}}\t{{.Status}}\t{{.Ports}}\t{{.Image}}"
```
Exemple de résultat :
```
NAMES                STATUS                    PORTS                     IMAGE
web-app              Up 2 hours (unhealthy)   0.0.0.0:8080->80/tcp      nginx:1.25
postgres-db          Up 5 hours                0.0.0.0:5432->5432/tcp    postgres:15
```
Notez l'état de santé. Un conteneur « unhealthy » est un indice, pas un verdict — il peut encore servir du trafic tout en échouant à son contrôle de santé.

**Inspectez un conteneur spécifique pour les montages, réseaux, variables d'environnement et configuration de santé :**
```bash
docker inspect web-app
```
Cela produit un gros objet JSON. Filtrez les parties dont vous avez besoin :
```bash
docker inspect web-app --format '{{json .Mounts}}' | jq
docker inspect web-app --format '{{json .NetworkSettings.Networks}}' | jq
docker inspect web-app --format '{{json .Config.Env}}' | jq
docker inspect web-app --format '{{json .Config.Healthcheck}}' | jq
```
Chaque commande renvoie des données structurées. Par exemple, la sortie des montages pour un conteneur avec un volume nommé et un montage bind pourrait ressembler à :
```json
[
  {
    "Type": "volume",
    "Name": "web-app-data",
    "Source": "/var/lib/docker/volumes/web-app-data/_data",
    "Destination": "/var/www/html",
    "Mode": "",
    "RW": true,
    "Propagation": "rprivate"
  },
  {
    "Type": "bind",
    "Source": "/home/user/config",
    "Destination": "/etc/app/config",
    "Mode": "ro",
    "RW": false,
    "Propagation": "rprivate"
  }
]
```
Portez une attention particulière au champ `RW` : un montage bind en lecture seule fera échouer les écritures avec une erreur de permission si l'application s'attend à modifier ces fichiers.

**Pour les projets Compose**, obtenez une vue globale du projet :
```bash
docker compose ps
docker compose config --services
docker compose config --volumes
```
Ces commandes confirment les services définis dans votre fichier compose et si Docker voit les volumes que vous attendez.

### Persistance des données avant tout changement

L'une des erreurs de configuration les plus courantes est la perte de données lors de la recréation d'un conteneur. Avant de poursuivre le débogage, confirmez où vivent les données persistantes :

- Volume nommé : géré par Docker, facile à réutiliser. Exemple d'extrait Compose :
```yaml
services:
  app:
    image: myapp:1.2
    volumes:
      - app_data:/var/lib/app
volumes:
  app_data:
```
- Montage bind : mappe directement un chemin de l'hôte. Utile pour le développement mais peut échouer en production si le chemin hôte n'existe pas sur toutes les machines.

Effectuez un test de redémarrage dans un environnement de staging :
1. Arrêtez le conteneur : `docker stop web-app`
2. Supprimez-le : `docker rm web-app`
3. Recréez-le à partir du même fichier compose ou de la même commande run.
4. Vérifiez si les données sont toujours présentes : `docker exec web-app ls /var/lib/app`

Si les fichiers manquent, l'application écrivait probablement dans la couche inscriptible du conteneur au lieu d'un volume ou d'un montage. Corrigez le Dockerfile ou le fichier compose pour monter un volume à ce chemin.

## Chemin de configuration sûr

Lorsque vous avez identifié une erreur de configuration probable, suivez un processus de changement chirurgical. Ne modifiez jamais les fichiers à l'intérieur d'un conteneur en cours d'exécution avec `docker exec vi` — ce changement disparaît à la recréation du conteneur et n'est pas suivi.

### Utilisez correctement les variables d'environnement

Une erreur fréquente est de passer des secrets via des variables d'environnement en clair dans une commande `docker run` ou un fichier compose, où ils sont visibles dans `docker inspect` et les listes de processus.

**Mauvaise pratique :**
```bash
docker run -e DATABASE_PASSWORD=supersecret myapp
```
`docker inspect` affichera ce mot de passe en clair.

**Mieux : utilisez les secrets Docker (pour Swarm) ou un fichier de secrets monté en volume en lecture seule.**
Exemple de fichier compose avec montage d'un fichier de secret :
```yaml
services:
  app:
    image: myapp:1.2
    secrets:
      - db_password
secrets:
  db_password:
    file: ./secrets/db_password.txt
```
À l'intérieur du conteneur, le secret apparaît à `/run/secrets/db_password`. L'application lit à partir de ce chemin.

### Changez une variable à la fois

Si vous soupçonnez qu'une variable d'environnement est incorrecte, modifiez uniquement cette variable et redémarrez le conteneur. Ne regroupez pas de modifications sans rapport.

Pour un conteneur unique :
```bash
docker stop web-app
docker rm web-app
docker run -d --name web-app \
  -e APP_ENV=production \
  -e DATABASE_URL=postgres://db:5432/app \
  -p 8080:80 \
  myapp:1.2
```
Ensuite, vérifiez les journaux et la santé :
```bash
docker logs web-app --tail 50
docker inspect web-app --format '{{.State.Health.Status}}'
```
Si l'état de santé n'est pas « healthy » en une minute, vous savez que la nouvelle variable d'environnement a contribué au problème.

Pour Compose, modifiez `docker-compose.yml`, puis recréez uniquement le service affecté :
```bash
docker compose up -d --force-recreate app
```
`--force-recreate` garantit que le conteneur est reconstruit avec la nouvelle configuration au lieu de réutiliser l'ancienne.

### Plan de retour en arrière

Avant d'appliquer un changement, écrivez la commande exacte de retour en arrière dans vos notes. Pour un service Compose, la configuration précédente est dans votre contrôle de version — il suffit de récupérer le commit précédent et de relancer `docker compose up -d`. Pour un `docker run` manuel, vous pouvez inspecter la configuration actuelle du conteneur avant suppression avec :
```bash
docker inspect web-app --format '{{json .Config}}' > web-app-config-backup.json
```
Ensuite, si nécessaire, reconstruisez la commande run à partir de ce JSON. Mieux encore, conservez toutes les définitions de conteneurs dans un fichier compose ou un script sous contrôle de version afin que le retour en arrière soit fiable.

## Vérification et diagnostics

Après tout changement, vérifiez que le conteneur se comporte comme prévu. Ne vous fiez pas à « il a démarré » — vérifiez la fonctionnalité réelle et l'utilisation des ressources.

### Vérifiez les journaux pour les motifs d'erreur

```bash
docker logs web-app --tail 200
```
Cherchez des exceptions récurrentes, des messages de connexion refusée ou des erreurs de configuration invalide. Pour un conteneur qui plante immédiatement après le démarrage, utilisez :
```bash
docker logs web-app
```
Si le conteneur a redémarré plusieurs fois, ajoutez des horodatages :
```bash
docker logs --timestamps web-app
```

Pour un conteneur qui se termine immédiatement, exécutez-le au premier plan avec un shell de substitution pour déboguer les problèmes de démarrage :
```bash
docker run --rm -it myapp:1.2 /bin/sh
```
Ensuite, exécutez manuellement la commande de démarrage de l'application pour voir l'erreur.

### Testez la connectivité réseau

Depuis l'intérieur d'un conteneur en cours d'exécution, testez la résolution DNS et la connectivité TCP :
```bash
docker exec web-app ping -c 3 database
```
Sortie attendue si le DNS fonctionne :
```
PING database (172.18.0.2) 56(84) bytes of data.
64 bytes from database (172.18.0.2): icmp_seq=1 ttl=64 time=0.089 ms
```
Si le nom ne se résout pas, le conteneur n'est peut-être pas sur le même réseau défini par l'utilisateur. Vérifiez les réseaux :
```bash
docker network ls
docker network inspect bridge
```
Pour un conteneur attaché à un réseau spécifique, assurez-vous que les services Compose partagent ce réseau.

Vérifiez les conflits de liaison de port :
```bash
docker ps --format "table {{.Names}}\t{{.Ports}}"
```
Si un autre conteneur utilise déjà le port hôte que vous voulez, vous verrez une erreur de liaison de port dans les journaux.

### Contrôles de santé et disponibilité

Un contrôle de santé mal configuré peut marquer un conteneur sain comme malsain, déclenchant des redémarrages inutiles. Inspectez la définition du contrôle de santé :
```bash
docker inspect web-app --format '{{json .Config.Healthcheck}}' | jq
```
Exemple de sortie :
```json
{
  "Test": ["CMD-SHELL", "curl -f http://localhost/ || exit 1"],
  "Interval": 30000000000,
  "Timeout": 5000000000,
  "Retries": 3,
  "StartPeriod": 60000000000
}
```
Les intervalles sont en nanosecondes. Une période de démarrage de 60 secondes (60 000 000 000 ns) peut être trop courte si votre application met plus de temps à s'initialiser. Ajustez dans votre Dockerfile ou fichier compose :
```yaml
healthcheck:
  test: ["CMD", "curl", "-f", "http://localhost/"]
  interval: 30s
  timeout: 5s
  retries: 3
  start_period: 120s
```
Après la mise à jour, recréez le conteneur et surveillez l'état de santé au fil du temps :
```bash
watch -n 5 'docker inspect web-app --format "{{.State.Health.Status}}"'
```

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

Examinons des erreurs de configuration spécifiques et comment récupérer de chacune.

### Erreur 1 : Montage de volume manquant entraînant une perte de données

**Symptôme :** Après la recréation du conteneur, tous les fichiers téléchargés par les utilisateurs ont disparu.
**Pourquoi cela arrive :** L'application a écrit dans un répertoire à l'intérieur du système de fichiers du conteneur, pas dans un volume monté.
**Récupération :** Vérifiez le système de fichiers de l'ancien conteneur (s'il est encore disponible) avec `docker cp` pour récupérer les fichiers avant de le supprimer. Ensuite, mettez à jour votre fichier compose pour ajouter un volume nommé au chemin de données de l'application. Recréez le conteneur et vérifiez la persistance avec un test de redémarrage.

### Erreur 2 : Fautes de frappe dans les variables d'environnement causant des échecs de connexion

**Symptôme :** L'application ne peut pas se connecter à la base de données, mais le conteneur de base de données est en cours d'exécution.
**Pourquoi cela arrive :** Une faute de frappe dans le nom de la variable d'environnement, par exemple, `DATABSE_URL` au lieu de `DATABASE_URL`.
**Récupération :** Inspectez l'environnement du conteneur :
```bash
docker exec web-app env | sort
```
Comparez avec ce que l'application attend. Corrigez le fichier compose ou le Dockerfile, puis recréez le conteneur. Utilisez une bibliothèque de configuration qui valide les variables d'environnement requises au démarrage pour échouer rapidement.

### Erreur 3 : Mappage de port incorrect empêchant l'accès externe

**Symptôme :** Le service fonctionne mais ne peut pas être atteint depuis l'hôte.
**Pourquoi cela arrive :** Soit le mauvais port du conteneur est mappé, soit le port hôte est déjà utilisé.
**Récupération :** Vérifiez les mappages de port réels :
```bash
docker port web-app
```
Comparez avec le port d'écoute de l'application (vérifiez le `EXPOSE` du Dockerfile ou les journaux de démarrage). Mettez à jour le mappage de port et recréez. Vérifiez avec `curl http://localhost:8080`.

### Erreur 4 : Limites de ressources non définies, le conteneur tue des processus

**Symptôme :** Le conteneur est tué avec le code de sortie 137 (SIGKILL) sous charge.
**Pourquoi cela arrive :** Le conteneur a dépassé la limite de mémoire (si définie) ou l'hôte a manqué de mémoire.
**Récupération :** Inspectez l'utilisation actuelle des ressources :
```bash
docker stats --no-stream
```
Définissez des limites de mémoire et de CPU appropriées dans votre fichier compose :
```yaml
services:
  app:
    image: myapp:1.2
    deploy:
      resources:
        limits:
          cpus: '0.50'
          memory: 512M
        reservations:
          cpus: '0.25'
          memory: 256M
```
Recréez et exécutez des tests de charge pour confirmer la stabilité.

### Erreur 5 : Utilisation d'une mauvaise image de base ou balise

**Symptôme :** L'application se comporte différemment en production par rapport au développement, ou des dépendances manquent.
**Pourquoi cela arrive :** Le Dockerfile utilise une balise mutable comme `latest`, ou l'image de base diffère entre les environnements.
**Récupération :** Épinglez l'image de base à un digest ou une balise de version spécifique dans votre Dockerfile. Reconstruisez l'image, poussez vers le registre et redéployez. Utilisez `docker image inspect` pour vérifier que l'ID de l'image correspond entre les environnements.

## Pièges courants et comment les éviter

Au-delà des erreurs spécifiques ci-dessus, voici des pièges généraux qui font trébucher les équipes.

### Modifier des fichiers à l'intérieur du conteneur

Exécuter `docker exec -it web-app bash` et modifier un fichier de configuration avec `vi` ou `sed` est tentant pour une solution rapide. Mais le changement est perdu à la recréation du conteneur et n'est pas visible pour les autres membres de l'équipe. À la place, montez un fichier de configuration ou utilisez des variables d'environnement, puis recréez.

### Ignorer les journaux jusqu'à ce que quelque chose casse

Les journaux sont le premier endroit à regarder, mais de nombreuses équipes ne les collectent pas de manière centralisée. Utilisez les pilotes de journalisation Docker pour transférer les journaux vers un service d'agrégation. Exemple d'extrait compose :
```yaml
services:
  app:
    image: myapp:1.2
    logging:
      driver: json-file
      options:
        max-size: "10m"
        max-file: "3"
```
Pour un serveur syslog central, passez le pilote à `syslog` et spécifiez l'adresse. Cela garantit que les journaux sont disponibles même si le conteneur est supprimé.

### Ne pas utiliser de fichier `.dockerignore`

Sans fichier `.dockerignore`, le contexte de construction peut inclure le répertoire local `.git`, `node_modules` ou des secrets, rendant l'image inutilement volumineuse et pouvant fuiter des données sensibles. Créez un fichier `.dockerignore` à la racine de votre contexte de construction :
```
.git
node_modules
*.log
.env
secrets
```

### Utilisation excessive de la balise `latest`

Comme mentionné, `latest` rend les constructions non reproductibles. Utilisez toujours des balises de version sémantique ou des digests dans les fichiers de déploiement en production. Pour le développement local, `latest` est acceptable mais évitez-le dans les pipelines CI/CD.

### Omettre les contrôles de santé

Sans contrôle de santé, Docker ne peut pas savoir si votre application est vraiment prête. Définissez un contrôle de santé qui teste un point de terminaison critique, pas seulement que le processus est en cours d'exécution. Utilisez `start_period` pour laisser suffisamment de temps aux applications à démarrage lent.

## Liste de contrôle des opérations

Utilisez cette liste de contrôle avant et après tout changement de configuration d'un service conteneurisé. Attribuez chaque élément à une seule personne responsable (pas une équipe) et révisez la liste lors de chaque publication ou revue d'incident.

| Étape | Action | Responsable | Fréquence |
|-------|--------|-------------|-----------|
| 1 | Enregistrer l'état actuel : sortie de `docker ps`, `docker logs`, `docker inspect` | Ingénieur d'astreinte | Au début de l'incident |
| 2 | Identifier le changement nécessaire et son rayon d'impact | Propriétaire de l'application (par ex., Priya Shah, responsable ingénierie) | Pendant la planification du changement |
| 3 | Sauvegarder les données critiques : `docker cp <conteneur>:/chemin /sauvegarde/` si nécessaire | Administrateur de base de données | Avant un changement destructif |
| 4 | Appliquer le plus petit changement possible (une variable d'environnement, un montage, un port) | Ingénieur d'astreinte | Au moment du changement |
| 5 | Vérifier la fonctionnalité : journaux, état de santé et réponse réelle du point de terminaison | Ingénieur QA | Immédiatement après le changement |
| 6 | Documenter le changement et la procédure de retour en arrière dans le runbook | Propriétaire du service | Dans les 24 heures |
| 7 | Exécuter un test de redémarrage en staging pour confirmer la persistance des données et la configuration | Propriétaire de l'application | Avant le prochain déploiement |
| 8 | Revoir la chronologie de l'incident et mettre à jour le runbook si nécessaire | Responsable ingénierie | Revue hebdomadaire des incidents |

Cette liste de contrôle garantit qu'aucune étape n'est sautée sous pression. Les responsables sont illustratifs — remplacez-les par les noms et rôles réels de votre organisation.

## Conclusion

Déboguer les erreurs de configuration des conteneurs Docker est un processus discipliné, pas un jeu de devinettes. Commencez par un inventaire complet de l'environnement, comprenez l'état actuel, faites un changement minimal à la fois et vérifiez avec des commandes concrètes et des sorties attendues. Ayez toujours un plan de retour en arrière avant de changer quoi que ce soit.

Les flux de travail les plus fiables rendent les défaillances visibles grâce aux journaux et aux contrôles de santé, protègent les valeurs sensibles avec la gestion des secrets, limitent les changements à la ressource prévue et définissent la vérification de la récupération avant qu'un incident ne force une décision. Appliquez la liste de contrôle et les pièges de ce guide pour construire des déploiements de conteneurs plus sûrs et plus résilients. Comme prochaine étape, choisissez un service que vous gérez, parcourez la liste de contrôle des opérations et documentez au moins une procédure de récupération dans le runbook de votre équipe.

Un flux de travail technique fiable rend les défaillances visibles, protège les valeurs sensibles, limite les changements à la ressource prévue et définit la vérification de la récupération avant que l'incident ne force la décision.