## Introduction

Lorsque vous devez mettre à niveau Docker Compose et migrer un projet existant qui utilise les profils Compose, le travail ne consiste pas seulement à remplacer des binaires ou à modifier du YAML. C'est un changement contrôlé de la façon dont les services sont sélectionnés, démarrés et connectés. Un profil est un ensemble nommé de services dans un fichier `docker-compose.yml` qui n'est activé que lorsque vous le ciblez explicitement via l'option `--profile` ou la variable d'environnement `COMPOSE_PROFILES`. Les mises à niveau et les migrations deviennent risquées lorsque vous supposez que la nouvelle version de Compose analyse les profils de la même manière, lorsque les données résident sur la mauvaise couche de stockage, ou lorsque personne n'a défini à quoi ressemble un basculement réussi.

Ce guide s'adresse aux développeurs, ingénieurs DevOps et équipes techniques de startups qui doivent mettre à niveau les profils Docker Compose sans interrompre les services en cours d'exécution. Il couvre l'inventaire des versions, les modifications de configuration sécurisées, la vérification et le diagnostic, les modes de défaillance et la récupération, ainsi qu'une liste de contrôle opérationnelle avant basculement. Chaque procédure inclut des commandes concrètes, les sorties attendues, les signaux de défaillance et un point de décision afin que vous puissiez observer avant de modifier, limiter la zone d'impact et récupérer rapidement si le résultat n'est pas celui attendu.

L'objectif opérationnel est simple : séparer l'observation de l'intervention, capturer d'abord l'état actuel et les horodatages, protéger les informations d'identification et le matériel privé, ne modifier qu'un élément délimité uniquement lorsque sa zone d'impact et son chemin de récupération sont compris, et vérifier le résultat. Un simple test de redémarrage peut révéler des problèmes de persistance des données avant qu'ils ne deviennent des incidents de production.

## Inventaire des versions et de l'environnement

Avant de toucher à un fichier de configuration, enregistrez exactement ce que vous avez. Cet inventaire vous protège des incompatibilités cachées et vous donne une cible de retour arrière. Exécutez les commandes en lecture seule suivantes en tant qu'utilisateur disposant des autorisations Docker minimales nécessaires ; évitez `sudo` jusqu'à ce que vous sachiez que vous en avez besoin.

Commencez par les versions de Docker et de Compose :

```bash
docker --version
# Exemple de sortie : Docker version 24.0.7, build afdd53b

docker compose version
# Exemple de sortie : Docker Compose version v2.23.0-desktop.1

docker compose config --profiles
# Liste tous les profils définis dans le(s) fichier(s) compose du répertoire de travail actuel.
```

Si `docker compose config --profiles` échoue avec une erreur d'analyse YAML, corrigez la syntaxe YAML avant de changer quoi que ce soit d'autre. Confirmez que le projet actuel possède bien des profils :

```bash
docker compose config --profiles
# Exemple de sortie si des profils existent :
#   - dev
#   - debug
```

Si la sortie est vide, votre projet n'utilise pas encore de profils ; la migration les introduira. Si la commande n'est pas reconnue, votre version de Compose est trop ancienne (les profils ont été introduits dans Compose v2.2.0, mais des versions correctives ultérieures ont corrigé des bogues de résolution de profils). Mettez d'abord à niveau le plugin Compose. Sous Linux avec Docker Desktop, vous pouvez mettre à jour le plugin via l'interface Desktop, ou sur une installation Docker Engine autonome, vous pouvez télécharger le dernier binaire du plugin dans `~/.docker/cli-plugins/`.

Ensuite, voyez ce qui est réellement en cours d'exécution dans le contexte du projet :

```bash
docker compose ps
# Exemple de sortie :
# NAME                COMMAND                  SERVICE             STATUS              PORTS
# web-1               "python app.py"          web                 running             0.0.0.0:8000->8000/tcp
```

Si vous voyez des conteneurs auxquels vous ne vous attendiez pas, arrêtez-vous et enquêtez. Un profil a peut-être été activé précédemment via une variable d'environnement, ou un développeur a peut-être démarré le service manuellement.

Vérifiez la source d'activation du profil actuel. Dans votre shell ou dans un fichier `.env` à côté du fichier compose :

```bash
printenv | grep COMPOSE_PROFILES
# Exemple de sortie si défini : COMPOSE_PROFILES=dev
```

Si `COMPOSE_PROFILES` est défini globalement dans le profil de votre shell, il peut activer des profils pour chaque projet compose dans ce terminal, ce qui crée de la confusion. Préférez les fichiers `.env` locaux au projet ou les indicateurs `--profile` explicites pour la reproductibilité.

Enfin, inspectez le stockage des données. Listez les volumes nommés et les montages de type bind utilisés par le projet :

```bash
docker compose ps -q | xargs -I{} sh -c 'docker inspect -f "{{.Name}} {{range .Mounts}}{{.Type}}:{{.Source}}->{{.Destination}} {{end}}" {}'
# Exemple de sortie pour un volume nommé et un montage bind :
# web-1 volume:app_data->/var/lib/app
# web-1 bind:/home/user/app/html->/usr/share/nginx/html
```

Un volume nommé comme `app_data` est géré par Docker et est plus facile à préserver lors de la recréation des conteneurs. Un montage bind mappe un chemin de l'hôte et est utile pour le développement local, mais il peut échouer sur un autre hôte ou contexte de conteneur si le chemin n'existe pas ou si les permissions diffèrent. Notez quels services utilisent des montages bind ; si un répertoire monté en bind contient des fichiers spécifiques à une version ou des fichiers de verrouillage, assurez-vous qu'il est accessible en écriture par l'utilisateur du conteneur après la mise à niveau.

**Test de persistance avant tout changement :** Créez ou identifiez un fichier de test dans chaque répertoire de données, recréez le conteneur et vérifiez si le fichier survit.

```bash
# Pour un conteneur en cours d'exécution nommé app, écrivez un marqueur
docker compose exec app sh -c 'echo canary-$(date +%s) > /var/lib/app/canary.txt'
docker compose stop app
docker compose up -d app
docker compose exec app cat /var/lib/app/canary.txt
# Si le contenu du fichier est renvoyé, les données sont sur un volume ou un montage bind.
# Si le fichier est absent, le service a écrit dans la couche inscriptible du conteneur, qui est perdue lors de la recréation.
```

Si les données disparaissent après la recréation, mettez à jour la définition du service pour utiliser un volume nommé ou un montage bind avant de tenter toute migration de profil. Ne mettez pas à niveau les profils d'un service qui perd des données lors d'un simple redémarrage.

## Chemin de configuration sécurisé

Après l'inventaire, vous êtes prêt à modifier les définitions de profils. Travaillez sur une copie du fichier compose, pas sur le fichier en production. Utilisez `git diff` ou une sauvegarde de fichier pour rendre le retour arrière trivial. Suivez cette séquence :

1. Geler le fichier compose actuel sous le nom `docker-compose.yml.backup` dans le même répertoire.
2. Apportez une modification à la fois : ajoutez un profil, modifiez l'affectation d'un profil ou mettez à niveau l'en-tête de version du fichier Compose.
3. Validez avec `docker compose config` avant d'appliquer.
4. Appliquez d'abord dans un environnement de test si possible.

### Exemple de migration d'un service dans un profil

Supposons que votre `docker-compose.yml` comporte les services `web`, `db` et `adminer`, et que vous souhaitiez placer `adminer` derrière un profil `debug` tout en gardant `web` et `db` toujours actifs :

Extrait original :

```yaml
services:
  web:
    image: nginx:alpine
    ports:
      - "8000:80"
  db:
    image: postgres:16-alpine
    volumes:
      - db_data:/var/lib/postgresql/data
  adminer:
    image: adminer:4.8.1
    ports:
      - "8080:8080"
```

Extrait modifié :

```yaml
services:
  web:
    image: nginx:alpine
    ports:
      - "8000:80"
  db:
    image: postgres:16-alpine
    volumes:
      - db_data:/var/lib/postgresql/data
  adminer:
    image: adminer:4.8.1
    profiles: ["debug"]
    ports:
      - "8080:8080"
```

Après la modification, exécutez :

```bash
docker compose config --profiles
# Sortie attendue :
#   - debug
```

Testez maintenant le comportement prévu :

```bash
# Sans le profil debug, adminer ne doit PAS démarrer
docker compose up -d
docker compose ps
# Attendu : seuls web et db sont en cours d'exécution.
# adminer-1 ne doit pas apparaître.

# Avec le profil debug, adminer démarre
docker compose --profile debug up -d adminer
docker compose ps
# Attendu : adminer-1 est maintenant en cours d'exécution et écoute sur le port 8080.
```

Si `docker compose up -d` démarre encore adminer sans le profil, vérifiez la présence d'une variable d'environnement globale `COMPOSE_PROFILES` dans votre shell ou votre fichier `.env`. Supprimez-la ou désactivez-la pour cette session de terminal afin d'éviter une activation accidentelle.

### Exemple de mise à niveau de la version du fichier Compose et de la syntaxe des profils

Les fichiers Compose utilisaient historiquement une clé de niveau supérieur `version:` comme `version: "3.9"`. Les versions plus récentes de Compose v2 n'exigent plus cette clé ; le fichier est traité comme sans version et la CLI infère le schéma. Si votre fichier contient encore `version: "3.7"`, les profils fonctionnent mais avec une sémantique de résolution plus ancienne. La migration vers le format sans version est facultative, mais si vous le faites, supprimez la ligne `version:` et assurez-vous que tous les attributs de service obsolètes sont nettoyés (par exemple, `links`, `depends_on` avec l'ancien format). Validez immédiatement :

```bash
docker compose config
# La sortie sera la configuration développée et normalisée. Elle ne doit pas générer d'erreur.
```

Si vous voyez un avertissement comme `the attribute 'version' is obsolete, it will be ignored, please remove it to avoid potential confusion`, supprimez la ligne dans le cadre de la migration.

### Principe de changement minimal

Pour chaque modification de profil, demandez-vous : « Si cela échoue, puis-je restaurer l'état précédent en moins de cinq minutes ? » Si la réponse est non, divisez le changement en étapes plus petites. Par exemple, ajoutez d'abord un profil à un service sans modifier les options d'exécution, vérifiez avec `docker compose config`, puis modifiez l'image ou l'environnement du service dans une deuxième étape. Cela isole la cause en cas de problème.

## Vérification et diagnostic

Après une modification de profil, vérifiez non seulement que les services attendus fonctionnent, mais qu'ils fonctionnent correctement avec le bon réseau et les bonnes données.

### Vérifications au niveau du service

```bash
# Liste tous les services avec leur statut de profil
docker compose config --services
# Exemple de sortie :
#   web
#   db
#   adminer

docker compose config --profile debug --services
# Sortie attendue :
#   web
#   db
#   adminer  # inclus car le profil debug l'active

# Vérifie quels services sont réellement en cours d'exécution
docker compose ps --services
# Exemple de sortie si le profil debug n'est pas actif :
#   web
#   db
```

L'écart entre `config --services` et `ps --services` révèle quels services sont définis mais pas démarrés. Si un service doit être démarré mais est absent, le profil n'a pas été activé. Utilisez `docker compose --profile debug up -d adminer` pour le démarrer, ou définissez `COMPOSE_PROFILES=debug` dans le fichier `.env` pour une activation à l'échelle du projet.

### Vérifications du réseau et des dépendances

Vérifiez qu'un service activé par profil peut atteindre ses dépendances. Par exemple, si un service `worker` dans le profil `background` a besoin du service `db`, et que `db` n'est dans aucun profil (toujours actif), alors `docker compose --profile background up -d` démarrera à la fois `db` et `worker`. Mais si `db` est dans un autre profil, `worker` peut ne pas résoudre `db`. Pour éviter les surprises, utilisez `depends_on` avec condition ou au moins l'ordre :

```yaml
services:
  db:
    image: postgres:16-alpine
    profiles: ["data"]
  worker:
    image: python:3.11-alpine
    command: ["python", "worker.py"]
    profiles: ["background"]
    depends_on:
      - db
```

Démarrez ensuite avec les deux profils : `docker compose --profile data --profile background up -d`. Si vous en oubliez un, la dépendance isolée peut échouer ; vérifiez les journaux :

```bash
docker compose logs worker
# Recherchez une erreur de connexion refusée à db:5432 ou une erreur de résolution DNS.
```

C'est un signal de défaillance courant qui indique un problème de dépendance de profil. La solution est soit de s'assurer que tous les profils nécessaires sont activés ensemble, soit de retirer le profil de la dépendance afin qu'elle s'exécute toujours.

### Diagnostics au niveau du conteneur

```bash
# Inspecte les variables d'environnement à l'intérieur d'un conteneur en cours d'exécution, y compris COMPOSE_PROFILES si transmise
docker compose exec web env | grep -i compose
# Exemple de sortie si COMPOSE_PROFILES est transmise :
# COMPOSE_PROFILES=debug

# Vérifie l'état de santé d'un conteneur spécifique
docker inspect --format='{{.State.Health.Status}}' <container_id>
# Exemple de sortie : healthy
```

Si un conteneur est en mauvaise santé après la modification du profil, inspectez ses journaux :

```bash
docker logs <container_id> --tail 50
# Exemple de ligne de défaillance :
# Error: unable to connect to database at db:5432
```

Utilisez des conteneurs nommés lorsque c'est possible pour un débogage stable :

```bash
docker compose up -d --scale web=2
# Vérifiez ensuite les deux conteneurs
docker compose ps
```

N'oubliez pas que la mise à l'échelle d'un service avec un montage bind échoue souvent ou provoque des conflits de permissions de fichiers ; assurez-vous que les services de données ne sont pas mis à l'échelle accidentellement.

### Test fonctionnel du point de terminaison

Pour un service web, après la migration, exécutez un curl local :

```bash
curl -s -o /dev/null -w "%{http_code}\n" http://localhost:8000
# Exemple de sortie attendue : 200
```

Si le service est dans un profil et que vous obtenez une connexion refusée, il n'est probablement pas en cours d'exécution. Activez le profil et refaites le test.

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

Même avec une planification minutieuse, des défaillances surviennent. Voici les modes de défaillance les plus probables, comment les détecter et les étapes de récupération immédiates.

### Mode de défaillance 1 : Profil silencieusement non appliqué en raison d'une variable d'environnement

**Symptômes :** `docker compose up -d` démarre plus ou moins de services que prévu. `docker compose config --profiles` affiche des profils, mais `docker compose ps` ne correspond pas.

**Cause :** Une variable `COMPOSE_PROFILES` définie dans `.env` ou le shell remplace l'option `--profile` de la ligne de commande ou ajoute des profils inattendus.

**Récupération :** Vérifiez et désactivez la variable :

```bash
unset COMPOSE_PROFILES
# Ou supprimez la ligne COMPOSE_PROFILES du fichier .env
docker compose --profile debug up -d
```

Vérifiez ensuite la liste des services. Si le fichier `.env` est utilisé pour d'autres variables, conservez le fichier mais supprimez uniquement la ligne `COMPOSE_PROFILES`.

### Mode de défaillance 2 : Erreur de syntaxe YAML après modification du profil

**Symptômes :** `docker compose up -d` échoue avec une erreur d'analyse YAML ou une clé inattendue.

**Cause :** Erreur d'indentation ou placement de la clé `profiles` au mauvais niveau.

**Récupération :** Validez avant d'appliquer :

```bash
docker compose config
# La commande peut afficher l'emplacement de l'erreur, par exemple :
# services.adminer.profiles: unexpected element
```

Corrigez le YAML et revalidez. Si vous avez une sauvegarde, vous pouvez la restaurer instantanément :

```bash
cp docker-compose.yml.backup docker-compose.yml
docker compose config
```

### Mode de défaillance 3 : Perte de données après recréation du service

**Symptômes :** Après `docker compose up -d --force-recreate`, un service démarre mais les fichiers de données sont absents ou par défaut.

**Cause :** Le service a écrit dans la couche du conteneur au lieu d'un volume ou d'un montage bind.

**Récupération :** Si les données existent encore dans l'ancien conteneur, essayez de les extraire avant de détruire le conteneur :

```bash
# Si l'ancien conteneur est encore présent mais arrêté, trouvez son ID
docker ps -a --filter "name=app_1"
# Copiez le répertoire de données vers l'extérieur
docker cp app_1:/var/lib/app ./data-backup
# Recréez ensuite avec un volume approprié
```

Mettez à jour le fichier compose avec un volume, puis ramenez le service et restaurez les données.

### Mode de défaillance 4 : Conflit réseau après migration de profil

**Symptômes :** Les services de différents profils ne peuvent pas communiquer ; les journaux montrent des erreurs DNS ou de connexion refusée.

**Cause :** Les deux services sont sur le même réseau mais l'un n'est pas démarré lorsque l'autre l'attend, ou l'isolation par profil n'était pas intentionnelle.

**Récupération :** Assurez-vous que le service dépendant est dans un profil activé en même temps, ou retirez la dépendance d'un profil. Vous pouvez ajouter un réseau `external: true` si nécessaire, mais évitez les modifications réseau pendant la migration sauf si nécessaire.

### Procédures de retour arrière

Pour toute migration, ayez un plan de retour arrière. Si vous utilisez git, validez le fichier compose connu comme bon avant les modifications. Sinon, conservez une sauvegarde horodatée :

```bash
cp docker-compose.yml docker-compose.yml.$(date +%Y%m%d%H%M%S)
```

Pour annuler une modification de profil, restaurez le fichier et recréez les services :

```bash
cp docker-compose.yml.backup docker-compose.yml
docker compose down
docker compose up -d
# Vérifiez l'ancien comportement
docker compose ps
```

Si vous avez mis à niveau la version de la CLI Compose elle-même et rencontré un bogue, vous pouvez rétrograder le plugin Docker Compose. Sous Linux, remplacez le binaire du plugin dans `~/.docker/cli-plugins/docker-compose` par une version plus ancienne, en vous assurant qu'il est exécutable (`chmod +x`). Redémarrez votre terminal ou votre shell après le remplacement.

## Pièges courants et comment les éviter

1. **Utiliser le même fichier `.env` pour plusieurs projets sans délimitation.** `COMPOSE_PROFILES` s'applique au projet dans le répertoire actuel. Si vous exécutez compose depuis différents répertoires, chaque projet peut utiliser le même `.env` s'il se trouve dans le répertoire personnel. Stockez les fichiers `.env` spécifiques au projet dans le répertoire du projet.
2. **Oublier que l'activation de profil est par commande, pas persistante.** Sauf si vous définissez `COMPOSE_PROFILES`, chaque commande `docker compose` doit inclure `--profile`. Cela conduit à l'arrêt des services lorsqu'un développeur exécute `docker compose up -d` sans l'option, créant des pannes intermittentes. Documentez le profil requis dans le README ou une cible `Makefile`.
3. **Ne pas mettre à niveau la CLI Compose avant d'utiliser des fonctionnalités de profil plus récentes.** Certains comportements de profil comme `depends_on` avec des profils ont eu des bogues corrigés dans des versions ultérieures. Exécutez toujours la dernière version stable de Docker Compose v2 lors d'une migration.
4. **Profils qui se chevauchent et font s'exécuter un service deux fois.** Si un service a plusieurs profils, il est activé lorsque l'un de ces profils est sélectionné. Assurez-vous que vos sélections de profils ne provoquent pas d'instances de service en double, sauf si c'est intentionnel (par exemple, en utilisant `--scale` séparément).
5. **Tester uniquement `docker compose up` sans vérifier la persistance des données.** Un service peut démarrer et paraître sain, mais une recréation ultérieure perd des données. Le test du fichier canari décrit précédemment est le moyen le moins coûteux de vérifier.
6. **Ignorer les conditions `depends_on` dans les migrations de profils.** Si un service utilise `depends_on: condition: service_healthy`, la dépendance doit être dans le même ensemble de profils ou toujours active ; sinon, le service dépendant peut échouer à démarrer. Préférez des ensembles de profils qui incluent toutes les dépendances ou évitez l'isolation par profil pour les dépendances avec vérification de santé.

## Liste de contrôle opérationnelle

Utilisez cette liste de contrôle avant et après la migration. Attribuez un responsable unique pour chaque élément (pour une petite équipe, cela peut être la même personne, mais chaque élément doit avoir un responsable nommé).

| # | Étape | Commande ou action | Résultat attendu | Responsable | Fréquence de révision |
|---|-------|-------------------|------------------|-------------|----------------------|
| 1 | Enregistrer les versions actuelles de Docker et Compose | `docker --version && docker compose version` | Versions consignées (par ex., Docker 24.0.7, Compose v2.23.0) | Priya Shah, ingénieure DevOps | Une fois avant le changement |
| 2 | Lister les profils actuels | `docker compose config --profiles` | Noms de profils existants ou vide | Priya Shah | Une fois avant le changement |
| 3 | Capturer l'état des services en cours d'exécution | `docker compose ps` | Liste des services et statut | Priya Shah | Une fois avant le changement |
| 4 | Sauvegarder le fichier compose | `cp docker-compose.yml docker-compose.yml.backup` | Le fichier de sauvegarde existe avec la même somme de contrôle | Alex Johnson, développeur | Une fois avant le changement |
| 5 | Effectuer un test de persistance canari sur les services de données critiques | Voir le test canari dans la section inventaire | Le fichier canari survit à la recréation | Alex Johnson | Avant le changement et après la migration |
| 6 | Appliquer la modification de configuration de profil | Modifier `docker-compose.yml`, ajouter la clé `profiles` ou changer l'affectation de profil | Fichier enregistré avec un seul changement logique | Alex Johnson | Une fois par changement |
| 7 | Valider la configuration | `docker compose config` | Aucune erreur d'analyse, sortie correcte | Priya Shah | Après chaque modification |
| 8 | Recréer les services sans le profil cible | `docker compose up -d` | Le service cible n'est pas en cours d'exécution ; les services de base fonctionnent | Alex Johnson | Après le changement, avant le basculement |
| 9 | Activer le profil cible et vérifier | `docker compose --profile debug up -d` | Le service cible démarre, port ouvert, journaux propres | Priya Shah | Après le changement, avant le basculement |
| 10 | Redémarrer le service et vérifier la persistance des données | `docker compose restart <service>` | Le service récupère, les données sont intactes | Alex Johnson | Après le changement, avant le basculement |
| 11 | Documenter les étapes de retour arrière et le responsable | Écrire la commande de retour arrière dans le runbook, par ex., `cp backup docker-compose.yml && docker compose up -d` | Procédure de retour arrière testée | Priya Shah | Une fois avant le basculement |
| 12 | Approbation finale | Examiner que tous les éléments de la liste de contrôle sont complets ; mettre à jour la documentation avec le nouveau comportement du profil | Le responsable technique approuve le basculement | Maria Garcia, responsable technique | Avant le basculement en production |

Revenez sur la liste de contrôle après tout échec ou incident : attribuez la cause racine, mettez à jour la liste de contrôle pour éviter la récurrence et réexécutez les étapes de vérification concernées.

## Conclusion

Les profils Docker Compose sont un moyen pratique de gérer les services optionnels, mais leur mise à niveau ou leur migration en toute sécurité exige une séquence disciplinée : inventorier l'état actuel, apporter une modification minimale à la fois, vérifier l'effet avec des commandes concrètes et disposer d'un chemin de retour arrière testé. Cet article a parcouru le cycle de vie complet avec des exemples pratiques : vérification des versions et de l'activation des profils, déplacement d'un service dans un profil, validation de la configuration, diagnostic des défaillances courantes, évitement des pièges et utilisation d'une liste de contrôle opérationnelle avec des responsables attribués.

Les habitudes les plus précieuses sont le test de persistance canari et l'utilisation de `docker compose config` comme garde-fou. Si vous n'adoptez que deux pratiques de ce guide, adoptez celles-là. Avant votre prochain changement en production, choisissez une modification de profil à faible risque, exécutez les vérifications de bout en bout dans un environnement de développement et comparez la sortie réelle aux signaux attendus. Documentez le résultat et le responsable. Ensuite, et seulement ensuite, appliquez le même changement en production avec confiance.