在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