在Dockerfile中指定一个
VOLUME
行会配置一些元数据到你的镜像上,但这些元数据的使用方式非常重要。
首先,这两行代码做了什么?
WORKDIR /usr/src/app
VOLUME . /usr/src/app
WORKDIR
这一行会在目录不存在时创建该目录,并更新一些镜像元数据以指定所有相对路径,同时为像RUN
这样的命令指定当前目录位置。 VOLUME
这一行指定了两个卷,一个是相对路径.
,另一个是/usr/src/app
,它们恰好是同一个目录。通常VOLUME
只包含单个目录,但也可以像你所做的那样包含多个目录,或者是JSON格式化的数组。
无法在Dockerfile中指定卷源:在Dockerfile中指定卷时经常出现的一个混淆点是,试图在构建镜像时匹配源和目标的运行时语法,这是不起作用的。 Dockerfile只能指定卷的目标。如果有人可以定义卷的源,那么这将是一个微不足道的安全漏洞,因为他们可以更新Docker Hub上的公共镜像,将根目录挂载到容器中,然后作为入口点的一部分启动容器中的后台进程,添加登录到/ etc / passwd,配置systemd在下次重启时启动比特币矿工,或搜索文件系统以将信用卡、社会保险号码和私钥发送到远程站点。
{{VOLUME行的作用是什么?}}正如所述,它设置了一些图像元数据,以表明镜像内的目录是一个卷。这些元数据如何使用?每次从该镜像创建容器时,Docker都会强制该目录成为一个卷。如果您在运行命令或组合文件中未提供卷,则Docker的唯一选择是创建匿名卷。这是一个本地命名卷,其名称具有长唯一ID,并且没有其他指示说明它被创建的原因或包含的数据(匿名卷是数据消失的地方)。如果覆盖卷,指向命名或主机卷,您的数据将进入那里。
{{VOLUME破坏了一些东西:}}在Dockerfile中定义卷后,无法禁用卷。更重要的是,Docker中的
RUN
命令是使用经典构建器的临时容器实现的。这些临时容器将获得一个临时的匿名卷。该匿名卷将用您镜像的内容初始化。来自
RUN
命令的容器内写操作将针对该卷进行。当
RUN
命令完成时,将保存对镜像的更改,但是会
丢弃对匿名卷的更改。因此,我强烈建议不要在Dockerfile中定义
VOLUME
。这会导致下游用户的镜像出现意外行为,他们希望通过初始数据扩展镜像以达到卷位置。
{{应该如何指定卷?}}要指定要与镜像一起使用的卷,请提供一个
docker-compose.yml
文件。用户可以修改该文件以调整卷位置以适应其本地环境,并捕获其他运行时设置,例如发布端口和网络设置。
有人应该记录这个!他们已经做到了。Docker在
Dockerfile文档中包含有关VOLUME使用的警告,并建议在运行时指定源:
- 在Dockerfile内更改卷:如果任何构建步骤在声明卷后更改卷内的数据,则这些更改将被丢弃。
...
- 主机目录在容器运行时声明:主机目录(挂载点)本质上是与主机相关的。这是为了保留镜像的可移植性,因为给定的主机目录不能保证在所有主机上都可用。出于这个原因,您不能在Dockerfile内挂载主机目录。
VOLUME
指令不支持指定host-dir
参数。您必须在创建或运行容器时指定挂载点。
随着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
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
然后使用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
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
docker run -it -v /host/path:/container/path containername bash
命令即可。 - Roland Puntaier