Recherche de site Web

Synchronisation de fichiers avec Docker-Compose Watch


Lorsque nous utilisons Docker pour créer des environnements de développement portables et facilement reproductibles, nous devons trouver un moyen d’apporter des modifications à notre base de code, immédiatement à l’intérieur des conteneurs, sans avoir besoin de les reconstruire à chaque fois. Une solution possible consiste à monter les répertoires hôtes directement à l’intérieur des conteneurs ; Cependant, cela nécessite de rompre l’isolation et la portabilité des conteneurs, car ils deviennent dépendants de la structure du répertoire hôte. Pour résoudre ce problème, nous pouvons utiliser docker-compose watch.

Dans ce tutoriel, nous allons apprendre à utiliser docker-compose watch pour garder les fichiers hôtes synchronisés à l’intérieur des conteneurs.

Dans ce tutoriel, vous allez apprendre :

  • Quelle est la différence entre les volumes et les montages de liaison
  • Comment utiliser docker-compose watch pour synchroniser les fichiers entre le système hôte et les conteneurs

CatégorieExigences, conventions ou version du logiciel utilisé
SystèmeIndépendant de la distribution
Logicieldocker-compose
AutreÊtre familier avec Docker et docker-compose
Conventions# – nécessite que les commandes linux données soient exécutées avec les privilèges root, soit directement en tant qu’utilisateur root, soit en utilisant la commande sudo
$– nécessite que les commandes linux données soient exécutées en tant qu’utilisateur régulier non privilégié

Volumes Docker et montages de liaison

Les données à l’intérieur des conteneurs ne sont pas persistantes : cela signifie que si un conteneur est détruit, toutes les données qu’il contient sont perdues. Pour obtenir la persistance des données lors de l’utilisation de Docker, nous avons essentiellement deux façons : nous pouvons soit utiliser des volumes, soit lier des montages. Chaque solution a ses avantages et ses inconvénients : résumons-les.

Volumes Docker

On peut distinguer les volumes anonymes des volumes nommés. Leur comportement est presque identique, à l’exception d’un cas que nous verrons dans un instant.

Les volumes anonymes ou nommés de manière aléatoire sont généralement créés lorsque l’instruction VOLUME est utilisée dans un fichier Dockerfile ou lorsque la commande docker volume create est appelée sans fournir de nom de volume comme argument. Les volumes nommés, en revanche, comme leur nom l’indique, sont des volumes auxquels nous attribuons explicitement un nom.

Dans l’exemple ci-dessous, nous exécutons un conteneur basé sur l’image officielle « httpd », et nous spécifions que nous voulons créer un volume nommé (nous l’appelons « httpd_data »), pour conserver les données dans le répertoire du conteneur /usr/local/apache2/htdocs/ :

docker run -v httpd_data:/usr/local/apache2/htdocs httpd

Lorsqu’un conteneur est démarré, si un volume est vide, les données existantes dans le répertoire cible du conteneur (/usr/local/apache2/htdocs, dans ce cas) sont copiées à l’intérieur du volume. Si le volume n’est pas vide, les données à l’intérieur du conteneur obscurcissent le contenu du répertoire cible, tout comme lorsque nous montons un système de fichiers à l’intérieur d’un répertoire existant avec la commande mnt.

Normalement, lorsqu’un conteneur est retiré, les volumes qu’il utilise sont conservés. Une exception est lorsqu’un conteneur est créé en exécutant la commande docker run avec l’option --rm, ce qui entraîne la suppression automatique du conteneur lorsqu’il existe. Dans ce cas particulier, contrairement aux volumes nommés, les volumes anonymes créés de manière contextuelle sont automatiquement supprimés avec le conteneur.

Les volumes sont généralement la solution privilégiée pour obtenir la persistance des données lors de la distribution d’une application avec Docker, car ils sont créés et gérés par Docker lui-même. Ils ne sont cependant pas une solution idéale lors du développement, puisque toute modification d’une base de code, pour être reflétée à l’intérieur d’un conteneur, nécessiterait une reconstruction de ce dernier.

Supports de liaison

Les supports de liaison sont souvent déconseillés, mais ils représentent une solution possible au problème mentionné ci-dessus. En utilisant des montages bind, nous pouvons rendre un répertoire hôte directement accessible à l’intérieur d’un conteneur. Dans l’exemple ci-dessous, nous lions mount le répertoire src sur l’hôte, sur le répertoire /usr/local/apache2/htdocs à l’intérieur du conteneur httpd :

sudo docker run -v $(pwd)/src:/usr/local/apache2/htdocs httpd

Cette stratégie avait l’avantage de fournir un moyen de refléter immédiatement les modifications de code à l’intérieur des conteneurs, mais présente deux inconvénients principaux :

  1. Il rompt l’isolation et la portabilité du conteneur (le conteneur devient dépendant de la structure du répertoire hôte)
  2. Une gestion plus difficile des autorisations, surtout si des mesures de sécurité supplémentaires telles que SELinux sont utilisées sur le système hôte

Voici un exemple typique : supposons que, sur le système hôte, nous ayons nos fichiers sources stockés dans le répertoire src, appartenant à notre utilisateur et à son propre groupe d’utilisateurs, et non accessibles en écriture par d’autres utilisateurs. Si nous montons ce répertoire à l’intérieur du conteneur, nous devons nous assurer que le service correspondant s’exécute avec le même UID que notre utilisateur a sur la machine hôte, sinon il ne pourra pas créer ou supprimer de fichiers dans le répertoire. Si ledit service s’exécute en tant que root, il sera capable de créer des fichiers à l’intérieur du répertoire, mais nous ne pourrons pas les modifier avec notre utilisateur non privilégié sur la machine hôte. Cela peut même devenir plus compliqué lors de l’utilisation de docker sans racine, car les UID sont déplacés à l’intérieur des conteneurs.

Utilisation de la montre docker-compose

La veille Docker-compose n’est pas une fonctionnalité du moteur Docker, ce n’est donc pas, à proprement parler, une nouvelle façon d’obtenir la persistance des données ou de partager des données entre le système hôte et les conteneurs : c’est une fonctionnalité disponible depuis la version 2.22 de docker-compose, un outil utilisé pour exécuter facilement des applications multi-conteneurs.

En utilisant docker-compose watch, nous pouvons préserver l’isolation des conteneurs et, en même temps, voir les modifications que nous apportons à notre base de code immédiatement répercutées à l’intérieur d’un conteneur. Ceci est réalisé en monocutant les chemins spécifiés sur l’hôte pour les modifications : lorsqu’une modification est repérée dans un fichier, par exemple, ce fichier est automatiquement et de manière transparente synchronisé à l’intérieur du conteneur.

Voyons un exemple. Supposons que nous ayons un fichier « index.html » à l’intérieur du répertoire src sur l’hôte. Le contenu du fichier est le suivant :

<h1>Hello world!</h1>

Pour copier le contenu du répertoire src dans le répertoire cible à l’intérieur du conteneur lors de la création de ce dernier, nous devons étendre le Dockerfile d’origine du service que nous voulons exécuter (httpd, dans ce cas) :

FROM httpd:latest
COPY src/ /usr/local/apache2/htdocs

Maintenant, dans le même répertoire, créons le fichier docker-compose contenant des instructions sur la construction du conteneur et surveillons le contenu du répertoire src pour les modifications :

 services:
   httpd:
     build:
       dockerfile: Dockerfile
     ports:
       - 8080:80
     develop:
       watch:
         - action: sync
           path: ./src
           target: /usr/local/apache2/htdocs

Nous fournissons des instructions « watch » via l’attribut watch à l’intérieur du fichier docker-compose. Nous spécifions une action (sync, dans ce cas), un chemin d’accès relatif au fichier docker-compose à surveiller pour les modifications (./src) et une cible, qui contrôle où les modifications sont reflétées à l’intérieur du conteneur. De plus, nous pouvons utiliser le champ ignore pour exclure certains fichiers de la surveillance : les modèles d’exclusion sont considérés comme relatifs au chemin. Supposons, par exemple, que nous voulions exclure le répertoire ./src/node-modules, nous écririons :

 services:
   httpd:
     build:
       dockerfile: Dockerfile
     ports:
       - 127.0.0.1:8080:80
     develop:
       watch:
         - action: sync
           path: ./src
           target: /usr/local/apache2/htdocs
           ignore:
             - node_modules/

Maintenant, nous pouvons exécuter la commande docker-compose up. Pour activer la surveillance des fichiers, nous utilisons l’option --watch :

docker-compose up --watch

Une fois que notre image personnalisée est générée et que le conteneur est démarré, nous pouvons jeter un coup d’œil aux journaux pour confirmer que la surveillance est établie :

STEP 1/3: FROM httpd:latest
STEP 2/3: COPY src/ /usr/local/apache2/htdocs
--> a03c7ea1c705
STEP 3/3: LABEL "com.docker.compose.image.builder"="classic"
COMMIT docker.io/library/test-httpd
--> 01c475f85dc8
Successfully tagged docker.io/library/test-httpd:latest
01c475f85dc840ceb98e9257142257c27037fe8c4df31db423c90bf6430e0eb6
Successfully built 01c475f85dc8
Successfully tagged test-httpd
[+] Running 2/1
✔ Network test_default    Created                                                                                                                                                           0.0s 
✔ Container test-httpd-1  Created                                                                                                                                                           0.1s 
       ⦿ Watch enabled
Attaching to httpd-1
httpd-1  | AH00558: httpd: Could not reliably determine the server's fully qualified domain name, using 10.89.4.2. Set the 'ServerName' directive globally to suppress this message
httpd-1  | AH00558: httpd: Could not reliably determine the server's fully qualified domain name, using 10.89.4.2. Set the 'ServerName' directive globally to suppress this message
httpd-1  | [Fri Feb 28 14:15:07.472766 2025] [mpm_event:notice] [pid 1:tid 1] AH00489: Apache/2.4.63 (Unix) configured -- resuming normal operations
httpd-1  | [Fri Feb 28 14:15:07.473037 2025] [core:notice] [pid 1:tid 1] AH00094: Command line: 'httpd -D FOREGROUND'

Comme nous avons lié le port 8080 sur l’hôte au port 80 à l’intérieur du conteneur, en naviguant vers http://localhost:8080, nous devrions être en mesure de visualiser le contenu du fichier index.html à l’intérieur du répertoire source :

Maintenant, modifions le contenu du fichier index.html comme suit :

<h1>Hello linuxconfig.org!</h1>

Dès que nous enregistrons les modifications, en lisant les journaux, nous pouvons confirmer que le fichier a été synchronisé :

         ⦿ Syncing service "httpd" after 1 changes were detected

En effet, si nous naviguons vers http://localhost:8080, encore une fois, nous devrions voir les modifications reflétées à l’intérieur du conteneur :

Il se dit tout seul que, pour que docker-compose watch fonctionne, l’UTILISATEUR à l’intérieur du conteneur doit être capable d’écrire dans le répertoire cible.

Comme vous pouvez le déduire du contenu du fichier docker-compose, « sync » n’est pas la seule action de surveillance disponible : les autres sont sync+restart et rebuild. Lorsque le premier est utilisé, les modifications apportées aux fichiers spécifiés sur l’hôte sont synchronisées avec le répertoire cible à l’intérieur du conteneur, et en outre, le conteneur de service est redémarré. C’est utile, par exemple, lors de la synchronisation de fichiers de configuration, qui nécessite un redémarrage d’un service pour être reflétée. Lorsque l’action de reconstruction est utilisée, les modifications apportées aux fichiers spécifiés déclenchent une reconstruction de l’image et le remplacement du conteneur de service en cours d’exécution.

Conclusions

Dans ce tutoriel, nous avons appris à utiliser docker-compose watch pour synchroniser automatiquement les fichiers hôtes à l’intérieur d’un conteneur. Bien que docker-compose watch ne soit pas destiné à remplacer les bind-mounts dans toutes les situations, c’est une solution idéale pendant le développement, car elle nous permet de voir les modifications que nous effectuons sur notre base de code immédiatement reflétées à l’intérieur du conteneur, sans qu’il soit nécessaire de briser l’isolation et la portabilité des conteneurs.