以下是我的Dockerfile的内容

FROM node:boron

# Create app directory
RUN mkdir -p /usr/src/app

# Change working dir to /usr/src/app
WORKDIR /usr/src/app

VOLUME . /usr/src/app

RUN npm install

EXPOSE 8080

CMD ["node" , "server" ]

在这个文件中,我期待VOLUME。/usr/src/app命令挂载 将主机当前工作目录的内容挂载到/usr/src/app上 容器文件夹。

请告诉我这是正确的方式吗?


当前回答

Dockerfile中的VOLUME命令是非常合法的,完全常规的,绝对可以使用,无论如何它都没有被弃用。只需要理解它。

我们使用它来指向容器中应用程序将经常写入的任何目录。我们不使用VOLUME只是因为我们想要像配置文件一样在主机和容器之间共享。

The command simply needs one param; a path to a folder, relative to WORKDIR if set, from within the container. Then docker will create a volume in its graph(/var/lib/docker) and mount it to the folder in the container. Now the container will have somewhere to write to with high performance. Without the VOLUME command the write speed to the specified folder will be very slow because now the container is using it's copy on write strategy in the container itself. The copy on write strategy is a main reason why volumes exist.

如果挂载在VOLUME命令指定的文件夹上,则该命令永远不会运行,因为VOLUME只在容器启动时执行,有点像ENV。

基本上,使用VOLUME命令,无需从外部挂载任何卷即可获得性能。数据将保存跨容器运行也没有任何外部挂载。然后当准备好时,简单地安装在它上面的东西。

一些好的用例: - - - - - -日志 -临时文件夹

一些糟糕的用例: -静态文件 ——配置 ——代码

其他回答

官方docker教程说:

A data volume is a specially-designated directory within one or more containers that bypasses the Union File System. Data volumes provide several useful features for persistent or shared data: Volumes are initialized when a container is created. If the container’s base image contains data at the specified mount point, that existing data is copied into the new volume upon volume initialization. (Note that this does not apply when mounting a host directory.) Data volumes can be shared and reused among containers. Changes to a data volume are made directly. Changes to a data volume will not be included when you update an image. Data volumes persist even if the container itself is deleted.

在Dockerfile中,你只能在容器中指定卷的目的地。例如/usr/src/app.

当你运行一个容器时,例如docker run——volume=/opt:/usr/src/app my_image,你可以但不必指定它在主机上的挂载点(/opt)。如果你没有指定——volume参数,那么挂载点将自动选择,通常在/var/lib/docker/volumes/下。

虽然这是一个非常老的帖子,我仍然希望你可以检查最新的docker官方文档,如果你在卷和绑定挂载之间有一些困惑

绑定挂载从Docker早期就已经存在了,我认为它也不应该是一个完美的设计,比如“绑定挂载允许访问敏感文件”, 你可以得到docker官方更喜欢你使用VOLUME而不是bind mount的信息。

您可以从这里获得卷的良好用例

参考

Docker卷文档 Docker存储概述

在Dockerfile中指定VOLUME行会在映像上配置一些元数据,但是如何使用这些元数据是很重要的。

首先,这两行做了什么:

WORKDIR /usr/src/app
VOLUME . /usr/src/app

如果目录不存在,那么WORKDIR行将创建该目录,并更新一些图像元数据以指定所有相对路径,同时,RUN等命令的当前目录将位于该位置。这里的VOLUME行指定了两个卷,一个是相对路径,另一个是/usr/src/app,两者恰好是相同的目录。大多数情况下,VOLUME行只包含一个目录,但它可以像您所做的那样包含多个目录,或者它可以是一个json格式的数组。

You cannot specify a volume source in the Dockerfile: A common source of confusion when specifying volumes in a Dockerfile is trying to match the runtime syntax of a source and destination at image build time, this will not work. The Dockerfile can only specify the destination of the volume. It would be a trivial security exploit if someone could define the source of a volume since they could update a common image on the docker hub to mount the root directory into the container and then launch a background process inside the container as part of an entrypoint that adds logins to /etc/passwd, configures systemd to launch a bitcoin miner on next reboot, or searches the filesystem for credit cards, SSNs, and private keys to send off to a remote site.

What does the VOLUME line do? As mentioned, it sets some image metadata to say a directory inside the image is a volume. How is this metadata used? Every time you create a container from this image, docker will force that directory to be a volume. If you do not provide a volume in your run command, or compose file, the only option for docker is to create an anonymous volume. This is a local named volume with a long unique id for the name and no other indication for why it was created or what data it contains (anonymous volumes are were data goes to get lost). If you override the volume, pointing to a named or host volume, your data will go there instead.

VOLUME breaks things: You cannot disable a volume once defined in a Dockerfile. And more importantly, the RUN command in docker is implemented with temporary containers with the classic builder. Those temporary containers will get a temporary anonymous volume. That anonymous volume will be initialized with the contents of your image. Any writes inside the container from your RUN command will be made to that volume. When the RUN command finishes, changes to the image are saved, and changes to the anonymous volume are discarded. Because of this, I strongly recommend against defining a VOLUME inside the Dockerfile. It results in unexpected behavior for downstream users of your image that wish to extend the image with initial data in volume location.

如何指定卷?要指定在映像中包含卷的位置,请提供docker-compose.yml。用户可以对其进行修改,以根据本地环境调整卷的位置,它还可以捕获其他运行时设置,如发布端口和网络。

应该有人把这个记录下来!他们有。Docker在Dockerfile文档中包含了关于VOLUME使用的警告,以及在运行时指定源文件的建议:

Changing the volume from within the Dockerfile: If any build steps change the data within the volume after it has been declared, those changes will be discarded. ... The host directory is declared at container run-time: The host directory (the mountpoint) is, by its nature, host-dependent. This is to preserve image portability, since a given host directory can’t be guaranteed to be available on all hosts. For this reason, you can’t mount a host directory from within the Dockerfile. The VOLUME instruction does not support specifying a host-dir parameter. You must specify the mountpoint when you create or run the container.


随着buildkit的引入,Dockerfile中定义VOLUME和RUN步骤的行为发生了变化。这里有两个例子。首先是Dockerfile:

$ cat df.vol-run 
FROM busybox

WORKDIR /test
VOLUME /test
RUN echo "hello" >/test/hello.txt \
 && chown -R nobody:nobody /test

其次,不使用buildkit进行构建。注意RUN步骤的更改是如何丢失的:

$ DOCKER_BUILDKIT=0 docker build -t test-vol-run -f df.vol-run .
Sending build context to Docker daemon  23.04kB
Step 1/4 : FROM busybox
 ---> beae173ccac6
Step 2/4 : WORKDIR /test
 ---> Running in aaf2c2920ebd
Removing intermediate container aaf2c2920ebd
 ---> 7960bec5b546
Step 3/4 : VOLUME /test
 ---> Running in 9e2fbe3e594b
Removing intermediate container 9e2fbe3e594b
 ---> 5895ddaede1f
Step 4/4 : RUN echo "hello" >/test/hello.txt  && chown -R nobody:nobody /test
 ---> Running in 2c6adff98c70
Removing intermediate container 2c6adff98c70
 ---> ef2c30f207b6
Successfully built ef2c30f207b6
Successfully tagged test-vol-run:latest

$ docker run -it test-vol-run /bin/sh
/test # ls -al 
total 8
drwxr-xr-x    2 root     root          4096 Mar  6 14:35 .
drwxr-xr-x    1 root     root          4096 Mar  6 14:35 ..
/test # exit

然后用buildkit进行构建。注意RUN步骤的更改是如何保存的:

$ docker build -t test-vol-run -f df.vol-run .
[+] Building 0.5s (7/7) FINISHED                                                                         
 => [internal] load build definition from df.vol-run                                                0.0s
 => => transferring dockerfile: 154B                                                                0.0s
 => [internal] load .dockerignore                                                                   0.0s
 => => transferring context: 34B                                                                    0.0s
 => [internal] load metadata for docker.io/library/busybox:latest                                   0.0s
 => CACHED [1/3] FROM docker.io/library/busybox                                                     0.0s
 => [2/3] WORKDIR /test                                                                             0.0s
 => [3/3] RUN echo "hello" >/test/hello.txt  && chown -R nobody:nobody /test                        0.4s
 => exporting to image                                                                              0.0s
 => => exporting layers                                                                             0.0s
 => => writing image sha256:8cb3220e3593b033778f47e7a3cb7581235e4c6fa921c5d8ce1ab329ebd446b6        0.0s
 => => naming to docker.io/library/test-vol-run                                                     0.0s

$ docker run -it test-vol-run /bin/sh
/test # ls -al
total 12
drwxr-xr-x    2 nobody   nobody        4096 Mar  6 14:34 .
drwxr-xr-x    1 root     root          4096 Mar  6 14:34 ..
-rw-r--r--    1 nobody   nobody           6 Mar  6 14:34 hello.txt
/test # exit

为了更好地理解dockerfile中的volume指令,让我们学习一下mysql官方docker文件实现中典型的volume用法。

VOLUME /var/lib/mysql

参考: https://github.com/docker-library/mysql/blob/3362baccb4352bcf0022014f67c1ec7e6808b8c5/8.0/Dockerfile

/var/lib/mysql是MySQL存储数据文件的默认位置。

当您仅为测试目的运行测试容器时,您可以不指定它的挂载点,例如。

docker run mysql:8

然后mysql容器实例将使用dockerfile中的volume指令指定的默认挂载路径。卷是在Docker根目录中使用一个非常长的类似id的名称创建的,这被称为“未命名”或“匿名”卷。在底层主机系统/var/lib/docker/volumes文件夹下

/var/lib/docker/volumes/320752e0e70d1590e905b02d484c22689e69adcbd764a69e39b17bc330b984e4

这对于快速测试非常方便,不需要指定挂载点,但仍然可以通过将Volume用于数据存储而不是容器层来获得最佳性能。

对于正式的使用,您将需要通过使用命名卷或绑定挂载来指定挂载路径。

docker run  -v /my/own/datadir:/var/lib/mysql mysql:8

该命令从底层主机系统中将/my/own/datadir目录作为/var/lib/mysql挂载到容器中。数据目录/my/own/datadir不会被自动删除,甚至容器也会被删除。

mysql官方镜像的使用(请检查“Where to storage Data”部分):

参考: https://hub.docker.com/_/mysql/

Dockerfile中的VOLUME命令是非常合法的,完全常规的,绝对可以使用,无论如何它都没有被弃用。只需要理解它。

我们使用它来指向容器中应用程序将经常写入的任何目录。我们不使用VOLUME只是因为我们想要像配置文件一样在主机和容器之间共享。

The command simply needs one param; a path to a folder, relative to WORKDIR if set, from within the container. Then docker will create a volume in its graph(/var/lib/docker) and mount it to the folder in the container. Now the container will have somewhere to write to with high performance. Without the VOLUME command the write speed to the specified folder will be very slow because now the container is using it's copy on write strategy in the container itself. The copy on write strategy is a main reason why volumes exist.

如果挂载在VOLUME命令指定的文件夹上,则该命令永远不会运行,因为VOLUME只在容器启动时执行,有点像ENV。

基本上,使用VOLUME命令,无需从外部挂载任何卷即可获得性能。数据将保存跨容器运行也没有任何外部挂载。然后当准备好时,简单地安装在它上面的东西。

一些好的用例: - - - - - -日志 -临时文件夹

一些糟糕的用例: -静态文件 ——配置 ——代码