## Introduction Docker Compose est un outil puissant pour définir et exécuter des applications multi-conteneurs. Avec un seul fichier YAML, vous pouvez configurer des services, des réseaux et des volumes, et tout démarrer avec une seule commande. Mais lorsque les variables d'environnement sont mal configurées, les résultats peuvent être déroutants : les conteneurs ne démarrent pas, les services se connectent à la mauvaise base de données ou des secrets fuient dans les journaux. Cet article se concentre sur les erreurs concrètes et réalistes que rencontrent les développeurs et les opérateurs lorsqu'ils utilisent des variables d'environnement dans Docker Compose. Plutôt que de la théorie abstraite, vous trouverez des commandes précises, des extraits de configuration et des sorties attendues. Chaque section cible un mode de défaillance courant, explique pourquoi il se produit et montre comment le corriger en toute sécurité. À la fin, vous disposerez d'un flux de travail de diagnostic reproductible qui va de l'observation à la résolution vérifiée, sans suppositions. Nous aborderons l'inventaire des versions et de l'environnement, les chemins de configuration sûrs, la vérification et le diagnostic, les modes de défaillance et la récupération, une liste de contrôle opérationnelle complète et les pièges courants. Tout au long, l'accent est mis sur la sécurité opérationnelle : observer avant de modifier, limiter le rayon d'impact, protéger les secrets, vérifier les résultats et documenter les chemins de récupération. ## Inventaire des versions et de l'environnement Avant de modifier quoi que ce soit, sachez exactement ce que vous exécutez. La syntaxe et le comportement de Docker Compose ont évolué ; une variable qui fonctionne dans une version peut se comporter différemment dans une autre. Commencez par enregistrer les versions exactes et inspecter l'état actuel de votre projet. ### Vérifier les versions de Docker et Compose Exécutez : ```bash docker --version docker compose version ``` Sortie attendue : ``` Docker version 24.0.7, build afdd53b Docker Compose version v2.23.0-desktop.1 ``` Les anciennes versions de Compose (le binaire autonome `docker-compose`) utilisent une syntaxe de commande différente et peuvent ne pas prendre en charge les fonctionnalités plus récentes comme `env_file` avec interpolation ou certaines options de vérification de l'état de santé. Si vous êtes en version 1.x, envisagez de migrer vers le plugin Compose v2 (`docker compose`) pour une meilleure compatibilité et des performances accrues. ### Inventorier l'environnement actuel des conteneurs Pour voir ce qui est réellement en cours d'exécution, utilisez : ```bash docker ps --format "table {{.Names}}\t{{.Status}}\t{{.Ports}}" ``` Exemple de sortie : ``` NAMES STATUS PORTS web Up 2 minutes 0.0.0.0:8080->80/tcp db Up 2 minutes 5432/tcp ``` Pour un examen plus approfondi des variables d'environnement d'un conteneur spécifique, exécutez : ```bash docker inspect --format '{{range .Config.Env}}{{println .}}{{end}}' ``` Cela affiche les variables exactement telles que Docker les voit, y compris celles qui ont été substituées par Compose. Si vous avez un projet Compose en cours d'exécution, utilisez : ```bash docker compose ps docker compose logs -f docker compose exec sh ``` La commande `exec` vous donne un shell à l'intérieur du conteneur pour inspecter directement les variables, par exemple `echo $DATABASE_URL`. ### Persistance des données : volumes vs montages liés Une erreur courante consiste à perdre des données parce qu'elles ont été écrites dans la couche inscriptible du conteneur au lieu d'un volume. Avant de modifier la configuration, confirmez où résident les données. - Un volume nommé, par exemple `app_data:/var/lib/app`, est géré par Docker et persiste lors de la recréation des conteneurs. - Un montage lié, par exemple `./data:/var/lib/app`, mappe directement un répertoire hôte et est utile pour le développement, mais peut souffrir de problèmes de permissions lorsque le chemin hôte n'existe pas ou a un mauvais propriétaire. Exécutez un test de redémarrage pour vérifier la persistance : ```bash docker compose stop docker compose up -d # Vérifiez si les données sont toujours là docker compose exec app ls /var/lib/app ``` Si les données sont manquantes, le service écrivait dans la couche du conteneur ; définissez un volume dans votre fichier Compose et refaites le test. ### Rayon d'impact et récupération Documentez la modification envisagée dans un endroit visible par vos collègues (un ticket, une branche git, un commentaire dans le fichier Compose). Effectuez toujours une modification ciblée à la fois et vérifiez avant de continuer. Par exemple, si vous soupçonnez qu'une variable est incorrecte, modifiez uniquement cette variable, puis exécutez `docker compose up -d` ; ne modifiez pas simultanément les ports, les réseaux et les politiques de redémarrage. ## Chemin de configuration sûr De nombreuses erreurs liées aux variables proviennent d'un placement incorrect des valeurs ou d'une mauvaise syntaxe. Cette section présente la manière sûre de définir et d'utiliser les variables d'environnement dans Compose. ### Où les variables peuvent être définies Docker Compose prend en charge plusieurs sources pour les variables d'environnement, avec une priorité spécifique (de la plus élevée à la plus basse) : 1. Variables d'environnement du shell (définies dans le terminal avant d'exécuter `docker compose`) 2. La section `environment` du fichier Compose 3. L'option `env_file` 4. Un fichier `.env` dans le répertoire du projet 5. Les valeurs par défaut de `docker-compose.yml` (par exemple `${VAR:-défaut}`) Une erreur courante est de s'attendre à ce qu'une valeur d'une source soit visible alors qu'une autre source la remplace. Par exemple, si vous définissez `POSTGRES_PASSWORD` dans votre shell et le définissez également dans la section `environment`, la valeur du shell ne l'emporte que si la section `environment` ne le définit pas également. En réalité, Compose v2 résout les variables au moment de l'analyse en utilisant l'environnement du shell et le fichier `.env` ; les valeurs de `environment` sont transmises au conteneur telles quelles après interpolation. Soyons précis : les entrées `environment:` peuvent référencer `${VAR}` et seront substituées en utilisant l'environnement du shell ou le fichier `.env`. ### Bonnes pratiques pour une configuration sûre - **Utilisez un fichier `.env`** pour le développement local et l'intégration continue (CI). Cela garde les variables hors du fichier Compose principal et peut être ignoré par git s'il contient des données sensibles. - **Ne committez jamais de véritables identifiants**. Utilisez des espaces réservés dans les fichiers d'exemple (par exemple `DB_PASSWORD=change-me`) et documentez comment les définir. - **Utilisez `env_file` pour de grands ensembles de variables** partagées entre services, mais sachez que les variables de `env_file` remplacent celles de `environment` (l'opposé des variables du shell). - **Préférez le mappage explicite dans `environment`** pour les paramètres critiques afin de les rendre visibles dans le fichier Compose. ### Un exemple de configuration sûre Supposons que vous ayez un service web qui a besoin d'une chaîne de connexion à une base de données. Une configuration fragile ressemble à ceci : ```yaml services: web: image: myapp:latest environment: DATABASE_URL: postgres://user:secret@db:5432/mydb ``` Coder en dur des identifiants dans YAML est dangereux et empêche la réutilisation. Une meilleure approche : À la racine du projet, créez un fichier `.env` : ``` DB_USER=appuser DB_PASSWORD=changeme DB_HOST=db DB_PORT=5432 DB_NAME=mydb ``` Dans votre `docker-compose.yml`, utilisez l'interpolation : ```yaml services: web: image: myapp:latest environment: DATABASE_URL: postgres://${DB_USER}:${DB_PASSWORD}@${DB_HOST}:${DB_PORT}/${DB_NAME} db: image: postgres:15 environment: POSTGRES_USER: ${DB_USER} POSTGRES_PASSWORD: ${DB_PASSWORD} POSTGRES_DB: ${DB_NAME} volumes: - db_data:/var/lib/postgresql/data volumes: db_data: ``` Exécutez `docker compose config` pour prévisualiser la configuration entièrement rendue : ```bash docker compose config ``` Cela devrait afficher le YAML avec les valeurs réelles substituées. Vérifiez que `DATABASE_URL` montre la bonne chaîne. Si une variable est manquante, Compose générera une erreur ou substituera une chaîne vide, ce qui peut entraîner des échecs subtils. ### Variables d'environnement et secrets Pour les valeurs sensibles comme les mots de passe de base de données ou les clés API, Docker recommande d'utiliser des secrets. Dans Compose, vous pouvez définir des secrets dans la section `secrets` et les référencer dans le conteneur sous forme de fichiers sous `/run/secrets/`. Par exemple : ```yaml services: db: image: postgres:15 secrets: - db_password environment: POSTGRES_PASSWORD_FILE: /run/secrets/db_password secrets: db_password: file: ./db_password.txt ``` Cela évite de mettre le mot de passe directement dans l'environnement ou le fichier Compose. L'image Postgres prend en charge la variante `_FILE` pour plusieurs variables, ce qui est plus sécurisé. ## Vérification et diagnostic Après toute modification, vous devez vérifier que la variable a l'effet escompté à l'intérieur du conteneur en cours d'exécution. Cette section présente des commandes de diagnostic concrètes et comment interpréter leur sortie. ### Inspecter les variables dans un conteneur en cours d'exécution Le contrôle le plus direct est d'exécuter `env` dans le conteneur : ```bash docker compose exec web env | sort ``` Recherchez la variable que vous avez définie. Par exemple, vous devriez voir `DATABASE_URL=postgres://appuser:changeme@db:5432/mydb`. Si vous voyez une valeur vide ou le littéral `${DB_USER}`, l'interpolation a échoué car la variable n'était pas définie dans le shell ou le fichier `.env`. ### Consulter les journaux pour les erreurs de configuration Les conteneurs échouent souvent au démarrage lorsque des variables requises sont manquantes. Utilisez : ```bash docker compose logs --tail=50 web ``` Messages d'erreur courants : - `Error: Database is uninitialized and superuser password is not specified.` (Postgres) - `panic: environment variable DATABASE_URL must be set` (applications Go) - `KeyError: 'SECRET_KEY'` (applications Python) Si vous voyez de telles erreurs, le conteneur s'est probablement arrêté. Vérifiez son état avec `docker compose ps` ; un conteneur arrêté affiche `Exited (1)` dans la colonne d'état. ### Utiliser docker inspect pour un diagnostic approfondi `docker inspect` vous donne le tableau d'environnement exact : ```bash docker inspect --format '{{range .Config.Env}}{{println .}}{{end}}' ``` Cela est utile lorsque vous soupçonnez que Compose ne substitue pas les variables comme prévu. La sortie est une liste plate de paires `KEY=value` ; notez que les valeurs ne sont pas masquées, alors faites attention à ne pas exposer de secrets dans les journaux ou les captures d'écran. ### Vérifications de l'état de santé et de l'état de préparation Un conteneur peut démarrer mais échouer à se connecter à une dépendance parce que celle-ci n'est pas prête. Utilisez des vérifications de l'état de santé pour rendre vos services résilients. Exemple pour un service web : ```yaml services: web: image: myapp:latest depends_on: db: condition: service_healthy healthcheck: test: ["CMD", "curl", "-f", "http://localhost/health"] interval: 30s timeout: 10s retries: 5 ``` Ensuite, vérifiez l'état de santé : ```bash docker compose ps ``` Un service sain affiche `Up (healthy)` dans la colonne d'état. ### Test de redémarrage pour la persistance Comme mentionné précédemment, redémarrez le conteneur pour vous assurer que la configuration persiste : ```bash docker compose restart ``` Vérifiez ensuite que l'application voit toujours les fichiers et variables attendus. Si une variable est définie uniquement dans le shell et que vous redémarrez sans elle, la valeur sera perdue. Définissez toujours les variables dans le fichier Compose ou `.env` pour des redémarrages cohérents. ## Modes de défaillance et récupération Cette section décrit des modes de défaillance spécifiques liés aux variables d'environnement, comment les reconnaître et comment les récupérer. ### Défaillance 1 : Variable non substituée - `${VAR}` littéral apparaît **Symptôme :** Les journaux de l'application montrent littéralement `DATABASE_URL=postgres://${DB_USER}:...`, ou le conteneur échoue avec « chaîne de connexion invalide ». **Cause :** La variable `DB_USER` n'est pas définie dans le shell ou le fichier `.env`, et aucune valeur par défaut n'a été fournie dans le fichier Compose. Compose laisse la chaîne intacte. **Récupération :** 1. Assurez-vous que le fichier `.env` existe et contient la variable. 2. Exécutez `docker compose config` pour vérifier l'interpolation. Si vous voyez `${DB_USER}` dans la sortie, la variable n'est pas définie. 3. Exportez la variable dans le shell ou ajoutez-la au `.env`, puis exécutez à nouveau `docker compose up -d`. ### Défaillance 2 : Mauvaise valeur en raison de la priorité **Symptôme :** Une variable a une valeur différente de celle attendue, provoquant des échecs de connexion ou un comportement erroné. **Cause :** La même variable est définie à plusieurs endroits (shell, `.env`, `environment`, `env_file`), et la priorité est mal comprise. **Récupération :** 1. Exécutez `docker compose config` pour voir les valeurs résolues. 2. Tracez la source de la valeur en utilisant la chaîne de priorité. Supprimez les définitions en double pour simplifier. 3. Documentez la source prévue pour chaque variable dans un README. ### Défaillance 3 : Secrets exposés dans le fichier Compose ou l'environnement **Symptôme :** Les identifiants sont visibles dans `docker inspect` ou dans le fichier Compose, qui est commité dans git. **Cause :** Secrets codés en dur. **Récupération :** 1. Déplacez les secrets vers un fichier séparé (par exemple `.env` ou les secrets Docker). 2. Référencez-les via l'interpolation ou les variables `_FILE`. 3. Faites pivoter immédiatement les identifiants exposés. 4. Ajoutez `.env` au `.gitignore` s'il contient des secrets, et fournissez un `sample.env` avec des espaces réservés. ### Défaillance 4 : Variable requise manquante - le conteneur se termine immédiatement **Symptôme :** `docker compose up` démarre, mais le service se termine avec le code 1 et les journaux montrent une erreur de variable manquante. **Cause :** L'application attend une variable d'environnement qui n'est pas fournie. **Récupération :** 1. Vérifiez `docker compose logs ` pour le message d'erreur. 2. Ajoutez la variable à la section `environment` ou au fichier `.env`. 3. Redémarrez : `docker compose up -d`. 4. Vérifiez avec `docker compose exec env`. ### Défaillance 5 : Variable définie mais le service ne parvient toujours pas à se connecter **Symptôme :** L'application ne peut pas atteindre une base de données ou un autre service même si la variable semble correcte. **Cause :** Problèmes de réseau, service non prêt ou mauvais nom d'hôte. **Récupération :** 1. Assurez-vous que le nom du service dans le fichier Compose correspond au nom d'hôte utilisé dans la chaîne de connexion. Les noms de service Compose sont résolubles par DNS. 2. Ajoutez `depends_on` avec des vérifications de l'état de santé pour imposer l'ordre de démarrage. 3. Vérifiez l'isolation réseau : les services sur le réseau par défaut devraient pouvoir se joindre par nom. Si vous avez défini des réseaux personnalisés, assurez-vous que les deux services sont sur le même réseau. 4. Depuis l'intérieur du conteneur, testez la connectivité : `docker compose exec web ping db` (si ping est disponible) ou utilisez `nc -zv db 5432`. ## Pièges courants et comment les éviter Au-delà des défaillances spécifiques, plusieurs erreurs récurrentes trompent même les utilisateurs expérimentés. Comprendre ces pièges fait gagner du temps et prévient les incidents en production. ### Piège 1 : Mélanger les variables `.env` et du shell de manière incohérente **Pourquoi cela arrive :** Les développeurs définissent des variables dans leur shell pour une session et oublient de mettre à jour `.env` pour les autres. Lorsque la personne suivante exécute le projet, les variables sont manquantes. **Comment éviter :** Utilisez `.env` comme source unique de vérité pour le développement local. Documentez que les développeurs doivent exécuter `source .env` si nécessaire, mais mieux vaut s'appuyer sur le chargement automatique de `.env` par Compose depuis le répertoire du projet. Pour la CI, définissez les variables dans l'environnement de la CI. ### Piège 2 : Utiliser `env_file` de manière incorrecte **Pourquoi cela arrive :** Les utilisateurs pensent que `env_file` est identique à `.env`, mais ils ont des rôles différents. `env_file` injecte des variables dans le conteneur au moment de l'exécution ; `.env` est utilisé par Compose pour l'interpolation dans le fichier Compose lui-même. **Comment éviter :** Utilisez `env_file` uniquement lorsque vous devez passer un ensemble de variables à un conteneur depuis un fichier. Utilisez `.env` pour les valeurs qui doivent être interpolées dans le fichier Compose. Gardez-les séparés pour éviter toute confusion. Si vous avez besoin des deux, vous pouvez spécifier plusieurs entrées `env_file`. ### Piège 3 : Oublier de citer les valeurs avec des caractères spéciaux **Pourquoi cela arrive :** YAML est sensible aux caractères spéciaux comme `#`, `:`, `{}`, `[]`, `*`, `&`, etc. Une valeur non citée peut être analysée de manière incorrecte. **Exemple :** ```yaml environment: - PASSWORD=pa$$word # faux, $$ peut être interprété - PASSWORD: 'pa$$word' # correct ``` **Comment éviter :** Citez toujours les valeurs qui contiennent des caractères spéciaux. Utilisez des guillemets simples pour empêcher toute expansion de variable dans YAML, ou des guillemets doubles si vous avez besoin de séquences d'échappement. Mieux encore, utilisez un fichier `.env` où les guillemets ne sont pas nécessaires. ### Piège 4 : Dépendre des variables d'environnement de l'hôte en production **Pourquoi cela arrive :** Les développeurs testent avec des variables exportées dans leur shell, puis déploient en production sans les définir, provoquant des échecs. **Comment éviter :** En production, définissez toutes les variables requises dans l'orchestrateur (par exemple, fichier Docker Compose, secrets Kubernetes ou système de gestion de configuration). Ne dépendez jamais des exports du shell dans un environnement de production. Utilisez un pipeline CI/CD qui injecte les variables depuis un magasin de secrets. ### Piège 5 : Ne pas valider le fichier Compose avant de l'appliquer **Pourquoi cela arrive :** Les développeurs modifient le YAML et exécutent `docker compose up`, pour voir une erreur de syntaxe ou un comportement inattendu. **Comment éviter :** Exécutez toujours `docker compose config` après avoir modifié le fichier Compose. Cela valide la syntaxe et montre le résultat interpolé. S'il y a une erreur, elle sera signalée immédiatement. De plus, vous pouvez utiliser `docker compose config --quiet` pour supprimer la sortie et vérifier uniquement les erreurs. ### Piège 6 : Ignorer la politique de redémarrage et les changements d'environnement **Pourquoi cela arrive :** Modifier les variables d'environnement dans le fichier Compose ne recrée pas automatiquement les conteneurs s'ils sont déjà en cours d'exécution ; vous devez exécuter `docker compose up -d` pour appliquer les changements, et même là, Compose peut ne pas recréer le conteneur s'il pense que rien n'a changé. Les changements d'environnement déclenchent la recréation, mais si vous utilisez `docker compose start` à la place, les variables ne sont pas appliquées. **Comment éviter :** Après avoir modifié les variables, exécutez `docker compose up -d` et vérifiez que le conteneur a été recréé (regardez l'ID du conteneur ou la durée de fonctionnement). Sinon, utilisez `docker compose up --force-recreate -d` pour forcer la recréation. ## Liste de contrôle opérationnelle Utilisez cette liste de contrôle comme procédure reproductible pour diagnostiquer les problèmes de variables d'environnement dans Docker Compose. Chaque élément est une action concrète avec une commande de vérification. ### Phase 1 : Observer et inventorier - [ ] Exécutez `docker --version` et `docker compose version` et enregistrez la sortie. - [ ] Exécutez `docker compose ps` pour voir l'état des services. - [ ] Pour un conteneur spécifique, exécutez `docker inspect --format '{{range .Config.Env}}{{println .}}{{end}}'` pour lister les variables d'environnement. (Faites attention aux secrets ; masquez-les si vous les journalisez.) - [ ] Vérifiez le fichier Compose et tous les fichiers `.env` dans le répertoire du projet. Exécutez `docker compose config` pour voir la configuration complète rendue. ### Phase 2 : Identifier le problème - [ ] Comparez la valeur attendue de la variable avec la valeur réelle de `docker compose config` ou `docker inspect`. - [ ] Vérifiez les journaux pour les erreurs de variables manquantes : `docker compose logs --tail=100 `. - [ ] Déterminez si la variable est définie à plusieurs endroits et quelle source a la priorité. ### Phase 3 : Effectuer une modification ciblée - [ ] Modifiez uniquement la variable en question. Utilisez la source la plus sûre : `.env` pour l'interpolation, `environment` pour les valeurs directes du conteneur, ou les secrets pour les identifiants. - [ ] Si vous utilisez des secrets, assurez-vous que le fichier de secret existe et est référencé correctement. - [ ] Validez la modification avec `docker compose config`. ### Phase 4 : Appliquer et vérifier - [ ] Appliquez la modification : `docker compose up -d` (ou `--force-recreate` si nécessaire). - [ ] Vérifiez l'état du service : `docker compose ps`. - [ ] Vérifiez la variable à l'intérieur du conteneur : `docker compose exec printenv `. - [ ] Testez la fonctionnalité de l'application qui dépend de la variable (par exemple, se connecter à la base de données). ### Phase 5 : Documenter et récupérer - [ ] Enregistrez la modification dans un fichier sous contrôle de version (par exemple, mettez à jour `.env.example` si applicable). - [ ] Mettez à jour les procédures ou la documentation avec le bon paramètre de variable. - [ ] Si la modification casse quelque chose, revenez en arrière : restaurez la valeur précédente, exécutez `docker compose up -d` et vérifiez le comportement d'origine. - [ ] Informez l'équipe de la modification et des actions requises (par exemple, ils peuvent avoir besoin de récupérer le dernier `.env`). Chaque étape doit être suivie dans l'ordre, et aucune étape ne doit être sautée. Cette liste de contrôle peut être adaptée au flux de travail de votre équipe, mais le principe de base reste : observer, modifier une chose, vérifier, documenter. ## Conclusion Les variables d'environnement sont une partie petite mais critique de la configuration de Docker Compose. Une seule variable manquante ou incorrecte peut faire tomber toute une pile, pourtant ces problèmes résultent souvent d'erreurs simples et évitables. En suivant les procédures de diagnostic et de récupération décrites ici, vous pouvez résoudre rapidement et en toute sécurité les problèmes liés aux variables. Les points clés à retenir : - Commencez toujours par un inventaire des versions et de l'état : sachez ce qui s'exécute et d'où viennent les variables. - Utilisez un chemin de configuration sûr : préférez `.env` pour l'interpolation et les secrets pour les identifiants. - Vérifiez chaque modification avec `docker compose config`, les journaux et `printenv` à l'intérieur du conteneur. - Comprenez les modes de défaillance : variable non substituée, mauvaise priorité, secrets exposés, variables manquantes et problèmes de connectivité. - Évitez les pièges courants : sources incohérentes, citation YAML, dépendance à l'hôte et saut de validation. - Suivez la liste de contrôle opérationnelle pour standardiser votre réponse. N'oubliez pas que la sécurité opérationnelle est primordiale : ne faites pas plusieurs modifications à la fois, protégez les valeurs sensibles et ayez toujours un chemin de récupération documenté. Avec de la pratique, vous serez capable de diagnostiquer et de corriger les erreurs de variables d'environnement dans Docker Compose avec confiance, réduisant les temps d'arrêt et la frustration. Comme prochaine étape, choisissez l'un de vos projets Compose existants et parcourez la liste de contrôle. Comparez la configuration rendue avec vos attentes et corrigez toute divergence. Ensuite, mettez en place une simple vérification de l'état de santé et un test de redémarrage pour vous assurer que vos services sont robustes. Enfin, examinez votre approche de gestion des secrets et sortez les identifiants des variables d'environnement en clair lorsque c'est possible. Ce faisant, vous renforcerez votre configuration Compose et prendrez l'habitude de vérifier avant de faire confiance.