En bref: non, votre VOLUME
instruction n'est pas correcte.
Les fichiers Dockerfile VOLUME
spécifient un ou plusieurs volumes en fonction des chemins côté conteneur. Mais cela ne permet pas à l'auteur de l'image de spécifier un chemin d'hôte. Du côté de l'hôte, les volumes sont créés avec un nom de type ID très long à l'intérieur de la racine Docker. Sur ma machine, c'est /var/lib/docker/volumes
.
Remarque: étant donné que le nom généré automatiquement est extrêmement long et n'a aucun sens du point de vue d'un humain, ces volumes sont souvent appelés «sans nom» ou «anonymes».
Votre exemple qui utilise un "." caractère ne fonctionnera même pas sur ma machine, peu importe si je fais du point le premier ou le deuxième argument. Je reçois ce message d'erreur:
docker: réponse d'erreur du démon: erreur d'exécution oci: container_linux.go: 265: démarrage du processus de conteneur causé "process_linux.go: 368: conteneur init causé \" open / dev / ptmx: aucun fichier ou répertoire de ce type \ "".
Je sais que ce qui a été dit à ce point est sans doute pas très utile pour quelqu'un qui essaie de comprendre VOLUME
et -v
et il ne est certainement pas à une solution pour ce que vous essayez d'accomplir. Nous espérons donc que les exemples suivants éclaireront davantage ces questions.
Minitutorial: Spécification des volumes
Compte tenu de ce Dockerfile:
FROM openjdk:8u131-jdk-alpine
VOLUME vol1 vol2
(Pour le résultat de ce minitutoriel, cela ne fait aucune différence si nous spécifions vol1 vol2
ou /vol1 /vol2
- ne me demandez pas pourquoi)
Construit le:
docker build -t my-openjdk
Courir:
docker run --rm -it my-openjdk
À l'intérieur du conteneur, exécutez ls
la ligne de commande et vous remarquerez que deux répertoires existent; /vol1
et /vol2
.
L'exécution du conteneur crée également deux répertoires, ou "volumes", du côté hôte.
Pendant que le conteneur est en cours d'exécution, exécutez docker volume ls
sur la machine hôte et vous verrez quelque chose comme ceci (j'ai remplacé la partie centrale du nom par trois points par souci de concision):
DRIVER VOLUME NAME
local c984...e4fc
local f670...49f0
De retour dans le conteneur , exécutez touch /vol1/weird-ass-file
(crée un fichier vierge à cet emplacement).
Ce fichier est maintenant disponible sur la machine hôte, dans l'un des volumes sans nom lol. Cela m'a pris deux essais car j'ai d'abord essayé le premier volume répertorié, mais j'ai finalement trouvé mon fichier dans le deuxième volume répertorié, en utilisant cette commande sur la machine hôte:
sudo ls /var/lib/docker/volumes/f670...49f0/_data
De même, vous pouvez essayer de supprimer ce fichier sur l'hôte et il sera également supprimé dans le conteneur.
Remarque: Le _data
dossier est également appelé "point de montage".
Quittez le conteneur et répertoriez les volumes sur l'hôte. Ils sont partis. Nous avons utilisé l' --rm
indicateur lors de l'exécution du conteneur et cette option efface efficacement non seulement le conteneur à la sortie, mais également les volumes.
Exécutez un nouveau conteneur, mais spécifiez un volume en utilisant -v
:
docker run --rm -it -v /vol3 my-openjdk
Cela ajoute un troisième volume et l'ensemble du système finit par avoir trois volumes sans nom. La commande se serait plantée si nous l'avions spécifié seulement -v vol3
. L'argument doit être un chemin absolu à l' intérieur du conteneur. Du côté hôte, le nouveau troisième volume est anonyme et réside avec les deux autres volumes dans /var/lib/docker/volumes/
.
Il a été indiqué précédemment que le Dockerfile
ne peut pas être mappé à un chemin d'hôte, ce qui pose un problème pour nous lorsque nous essayons d'apporter des fichiers de l'hôte au conteneur pendant l'exécution. Une -v
syntaxe différente résout ce problème.
Imaginez que j'ai un sous-dossier dans le répertoire de mon projet ./src
que je souhaite synchroniser à l' /src
intérieur du conteneur. Cette commande fait l'affaire:
docker run -it -v $(pwd)/src:/src my-openjdk
Les deux côtés du :
personnage attendent un chemin absolu. Le côté gauche étant un chemin absolu sur la machine hôte, le côté droit étant un chemin absolu à l'intérieur du conteneur. pwd
est une commande qui "imprime le répertoire courant / de travail". Mettre la commande dans $()
prend la commande entre parenthèses, l'exécute dans un sous-shell et renvoie le chemin absolu vers notre répertoire de projet.
En mettant tout cela ensemble, supposons que nous avons ./src/Hello.java
dans notre dossier de projet sur la machine hôte avec le contenu suivant:
public class Hello {
public static void main(String... ignored) {
System.out.println("Hello, World!");
}
}
Nous construisons ce Dockerfile:
FROM openjdk:8u131-jdk-alpine
WORKDIR /src
ENTRYPOINT javac Hello.java && java Hello
Nous exécutons cette commande:
docker run -v $(pwd)/src:/src my-openjdk
Cela imprime "Hello, World!".
La meilleure partie est que nous sommes totalement libres de modifier le fichier .java avec un nouveau message pour une autre sortie lors d'une deuxième exécution - sans avoir à reconstruire l'image =)
Remarques finales
Je suis assez nouveau dans Docker, et le "tutoriel" mentionné ci-dessus reflète les informations que j'ai recueillies à partir d'un hackathon en ligne de commande de 3 jours. J'ai presque honte de ne pas avoir été en mesure de fournir des liens vers une documentation claire de type anglais pour étayer mes déclarations, mais je pense honnêtement que cela est dû à un manque de documentation et non à un effort personnel. Je sais que les exemples fonctionnent comme annoncé en utilisant ma configuration actuelle qui est "Windows 10 -> Vagrant 2.0.0 -> Docker 17.09.0-ce".
Le tutoriel ne résout pas le problème "comment spécifier le chemin du conteneur dans le Dockerfile et laisser la commande run spécifier uniquement le chemin de l'hôte". Il y a peut-être un moyen, je ne l'ai tout simplement pas trouvé.
Enfin, j'ai le sentiment instinctif que la spécification VOLUME
dans le Dockerfile n'est pas seulement rare, mais c'est probablement une meilleure pratique à ne jamais utiliser VOLUME
. Pour deux raisons. La première raison que nous avons déjà identifiée: nous ne pouvons pas spécifier le chemin de l'hôte - ce qui est une bonne chose car Dockerfiles doit être très indépendant des spécificités d'une machine hôte. Mais la deuxième raison est que les gens peuvent oublier d'utiliser l' --rm
option lors de l'exécution du conteneur. On peut se rappeler de retirer le conteneur mais oublier de retirer le volume. De plus, même avec le meilleur de la mémoire humaine, il peut être difficile de déterminer quels volumes anonymes peuvent être supprimés en toute sécurité.
docker volume prune
peut être utilisé pour nettoyer les volumes restants qui ne sont pas attachés aux conteneurs en cours d'exécution. Pour ne pas dire qu'il sera facile de distinguer ceux potentiellement importants par id seul .../
, doncvol1
est relatif à/
, qui se résout en/vol1
. Si vous utilisezWORKDIR
pour spécifier un répertoire de travail autre que/
,vol1
et/vol1
ne pointerait plus vers le même répertoire.La spécification d'une
VOLUME
ligne dans un Dockerfile configure un peu de métadonnées sur votre image, mais la manière dont ces métadonnées sont utilisées est importante.Tout d'abord, qu'ont fait ces deux lignes:
La
WORKDIR
ligne crée le répertoire s'il n'existe pas, et met à jour certaines métadonnées d'image pour spécifier tous les chemins relatifs, ainsi que le répertoire actuel pour les commandes commeRUN
sera à cet emplacement. LaVOLUME
ligne y spécifie deux volumes , l'un est le chemin relatif.
et l'autre est/usr/src/app
, les deux sont simplement le même répertoire. Le plus souvent, laVOLUME
ligne ne contient qu'un seul répertoire, mais elle peut en contenir plusieurs comme vous l'avez fait, ou il peut s'agir d'un tableau au format json.Vous ne pouvez pas spécifier une source de volume dans le Dockerfile : une source courante de confusion lors de la spécification de volumes dans un Dockerfile essaie de faire correspondre la syntaxe d'exécution d'une source et d'une destination au moment de la création de l'image, cela ne fonctionnera pas . Le Dockerfile ne peut spécifier que la destination du volume. Ce serait un exploit de sécurité trivial si quelqu'un pouvait définir la source d'un volume car il pouvait mettre à jour une image commune sur le hub docker pour monter le répertoire racine dans le conteneur, puis lancer un processus d'arrière-plan à l'intérieur du conteneur dans le cadre d'un point d'entrée qui ajoute des connexions à / etc / passwd, configure systemd pour lancer un mineur Bitcoin au prochain redémarrage, ou recherche dans le système de fichiers des cartes de crédit, des SSN et des clés privées à envoyer vers un site distant.
Que fait la ligne VOLUME? Comme mentionné, il définit certaines métadonnées d'image pour dire qu'un répertoire à l'intérieur de l'image est un volume. Comment ces métadonnées sont-elles utilisées? Chaque fois que vous créez un conteneur à partir de cette image, docker forcera ce répertoire à être un volume. Si vous ne fournissez pas de volume dans votre commande d'exécution ou dans votre fichier de composition, la seule option pour le docker est de créer un volume anonyme. Il s'agit d'un volume nommé local avec un long identifiant unique pour le nom et aucune autre indication de la raison pour laquelle il a été créé ou des données qu'il contient (les volumes anonymes sont des données perdues). Si vous remplacez le volume, en pointant vers un volume nommé ou hôte, vos données y iront à la place.
VOLUME casse les choses: vous ne pouvez pas désactiver un volume une fois défini dans un Dockerfile. Et plus important encore, la
RUN
commande dans docker est implémentée avec des conteneurs temporaires. Ces conteneurs temporaires recevront un volume anonyme temporaire. Ce volume anonyme sera initialisé avec le contenu de votre image. Toutes les écritures à l'intérieur du conteneur à partir de votreRUN
commande seront effectuées sur ce volume. Une fois laRUN
commande terminée, les modifications apportées à l'image sont enregistrées et les modifications apportées au volume anonyme sont supprimées. Pour cette raison, je déconseille fortement de définir unVOLUME
à l'intérieur du Dockerfile. Il en résulte un comportement inattendu pour les utilisateurs en aval de votre image qui souhaitent étendre l'image avec les données initiales dans l'emplacement du volume.Comment devez-vous spécifier un volume? Pour spécifier où vous souhaitez inclure les volumes avec votre image, fournissez un
docker-compose.yml
. Les utilisateurs peuvent modifier cela pour ajuster l'emplacement du volume à leur environnement local, et il capture d'autres paramètres d'exécution tels que les ports de publication et la mise en réseau.Quelqu'un devrait documenter cela! Ils ont. Docker inclut des avertissements sur l'utilisation de VOLUME dans sa documentation sur le Dockerfile ainsi que des conseils pour spécifier la source au moment de l'exécution:
la source
La
VOLUME
commande dans aDockerfile
est tout à fait légitime, totalement conventionnelle, tout à fait correcte à utiliser et elle n'est de toute façon pas obsolète. Juste besoin de le comprendre.Nous l'utilisons pour pointer vers tous les répertoires dans lesquels l'application du conteneur écrira beaucoup. Nous n'utilisons pas
VOLUME
simplement parce que nous voulons partager entre l'hôte et le conteneur comme un fichier de configuration.La commande a simplement besoin d'un paramètre; un chemin d'accès à un dossier, relatif à
WORKDIR
si défini, à partir du conteneur. Ensuite, docker créera un volume dans son graphique (/ var / lib / docker) et le montera dans le dossier du conteneur. Maintenant, le conteneur aura un endroit où écrire avec des performances élevées. Sans laVOLUME
commande, la vitesse d'écriture dans le dossier spécifié sera très lente car le conteneur utilise désormais sacopy on write
stratégie dans le conteneur lui-même. Lacopy on write
stratégie est l'une des principales raisons pour lesquelles les volumes existent.Si vous montez sur le dossier spécifié par la
VOLUME
commande, la commande n'est jamais exécutée car elleVOLUME
n'est exécutée que lorsque le conteneur démarre, un peu commeENV
.Fondamentalement, avec la
VOLUME
commande, vous obtenez des performances sans monter aucun volume en externe. Les données seront également enregistrées dans les exécutions de conteneurs sans aucun montage externe. Ensuite, lorsque vous êtes prêt, montez simplement quelque chose dessus.Quelques bons exemples d'utilisation:
- journaux
- dossiers temporaires
Quelques mauvais cas d'utilisation:
- fichiers statiques
- configs
- code
la source
VOLUME
répertoires des configurations. Cependant, une fois que vous avez réellement monté une configuration, vous devrez monter sur ce répertoire et doncVOLUME
commande ne s'exécutera pas. Par conséquent, il est inutile d'utiliser laVOLUME
commande sur un répertoire spécifié pour une configuration. L'initialisation d'un graphe de volume avec un seul fichier statique en lecture seule est également une sérieuse surcharge. Donc je maintiens ce que j'ai dit, pas besoin deVOLUME
commande sur les configs.Pour mieux comprendre les
volume
instructions de dockerfile, apprenons l'utilisation typique du volume dans l'implémentation officielle du fichier docker mysql.Référence: https://github.com/docker-library/mysql/blob/3362baccb4352bcf0022014f67c1ec7e6808b8c5/8.0/Dockerfile
C'est
/var/lib/mysql
l'emplacement par défaut de MySQL qui stocke les fichiers de données.Lorsque vous exécutez le conteneur de test à des fins de test uniquement, vous ne pouvez pas spécifier son point de montage, par exemple
alors l'instance de conteneur mysql utilisera le chemin de montage par défaut qui est spécifié par l'
volume
instruction dans dockerfile. les volumes sont créés avec un nom de type ID très long à l'intérieur de la racine Docker, cela s'appelle volume "sans nom" ou "anonyme". Dans le dossier du système hôte sous-jacent / var / lib / docker / volumes.Ceci est très pratique à des fins de test rapide sans avoir besoin de spécifier le point de montage, mais peut toujours obtenir les meilleures performances en utilisant Volume pour le stockage de données, pas la couche conteneur.
Pour une utilisation formelle, vous devrez spécifier le chemin de montage en utilisant le volume nommé ou le montage de liaison, par exemple
La commande monte le répertoire / my / own / datadir à partir du système hôte sous-jacent en tant que / var / lib / mysql à l'intérieur du conteneur. Le répertoire de données / my / own / datadir ne sera pas automatiquement supprimé, même le conteneur est supprimé.
Utilisation de l'image officielle mysql (veuillez consulter la section «Où stocker les données»):
Référence: https://hub.docker.com/_/mysql/
la source
-v
utiliser sans régler le volume dans le DockerfileJe ne considère pas l'utilisation de VOLUME comme bonne en tout cas, sauf si vous créez une image pour vous-même et que personne d'autre ne l'utilisera.
J'ai été affecté négativement en raison du VOLUME exposé dans les images de base que j'ai étendues et je n'ai pris connaissance du problème qu'après que l'image était déjà en cours d'exécution, comme wordpress qui déclare le
/var/www/html
dossier comme un VOLUME , ce qui signifiait que tous les fichiers ajoutés ou modifiés pendant l'étape de construction n'est pas prise en compte et les changements en direct persistent, même si vous ne le savez pas. Il existe une solution de contournement moche pour définir un répertoire Web à un autre endroit, mais c'est juste une mauvaise solution à une solution plus simple: supprimez simplement la directive VOLUME.Vous pouvez facilement atteindre l'intention du volume en utilisant l'
-v
option, cela non seulement indique clairement quels seront les volumes du conteneur (sans avoir à jeter un coup d'œil au Dockerfile et aux Dockerfiles parent), mais cela donne également au consommateur la possibilité de utiliser le volume ou non.Il est fondamentalement mauvais d'utiliser VOLUMES pour les raisons suivantes, comme le dit cette réponse :
Avoir la possibilité d' annuler la déclaration d' un volume aiderait, mais seulement si vous connaissez les volumes définis dans le fichier docker qui a généré l'image (et les fichiers docker parent!). De plus, un VOLUME pourrait être ajouté dans les versions plus récentes d'un Dockerfile et casser des choses de manière inattendue pour les consommateurs de l'image.
Une autre bonne explication (à propos de l'image oracle ayant VOLUME , qui a été supprimée ): https://github.com/oracle/docker-images/issues/640#issuecomment-412647328
Plus de cas dans lesquels VOLUME a cassé des choses pour les gens:
Une pull request pour ajouter des options pour réinitialiser les propriétés de l'image parente (y compris VOLUME), a été fermée et est en cours de discussion ici (et vous pouvez voir plusieurs cas de personnes affectées négativement en raison des volumes définis dans les fichiers dockerfiles), qui a un commentaire avec un bon explication contre VOLUME:
Je considère également EXPOSE comme mauvais, mais il a moins d'effets secondaires. La seule bonne chose que je puisse voir à propos de VOLUME et EXPOSE concerne la documentation, et je les considérerais comme bonnes si elles ne servaient que pour cela (sans aucun effet secondaire).
TL; DR
Je considère que la meilleure utilisation de VOLUME est d'être obsolète.
la source