Docker镜像拉取与Volume、Bind Mount行为疑问及验证
问题背景
我正在理解Docker镜像的工作机制,参考了一个包含VOLUME指令且有RUN命令修改该路径内容的Containerfile,有以下疑问:
- 从Docker Hub拉取该镜像时,Volume中的数据会如何处理?是否包含在拉取的镜像中?
RUN命令仅在启动容器时执行吗? - 运行容器时,将本地目录绑定挂载到
VOLUME指令指定的挂载点上会发生什么?
实验与行为观察
实际测试发现,绑定挂载会覆盖容器内原有数据,导致RUN命令生成的数据丢失;但使用命名卷时数据会保留。同时我有疑问:若拉取更新版本的容器镜像,命名卷中的原有数据会不会被镜像里Volume的新内容修改?
查阅Docker文档得知:
使用容器填充卷
若启动创建新卷的容器,且容器中待挂载目录存在文件或目录,该目录内容会被复制到卷中。容器随后挂载并使用该卷,其他使用该卷的容器也能访问预填充的内容。
但该机制在绑定挂载已存在目录时不生效。我做了以下对比实验:
测试用Dockerfile
FROM alpine RUN mkdir /test \ && echo 'echo "start `date`" >>/test/log' >/runtest \ && echo 'echo "/log:"' >>/runtest \ && echo 'cat /log' >>/runtest \ && echo 'echo "/test/log:"' >>/runtest \ && echo 'cat /test/log' >>/runtest \ && echo 'sleep 99999999' >>/runtest \ && chmod +x /runtest \ && echo "build `date`" >/test/log \ && echo "build `date`" >/log \ && cat /runtest \ && cat /log \ && cat /test/log CMD /runtest VOLUME "/test"
该Dockerfile通过RUN命令创建/test/log并写入构建日期,CMD命令会在每次容器启动时向该文件追加启动日期。需注意VOLUME指令要放在RUN之后,因为VOLUME会将目录当前内容复制到卷中,之后的RUN命令仅影响容器镜像内的原目录,不会同步到卷中。
命名卷方式测试(docker-compose配置)
version: "3" services: tester: build: . volumes: - test:/test volumes: test:
首次运行结果
tester_1 | /log: tester_1 | build Mon Feb 20 08:53:29 UTC 2023 tester_1 | /test/log: tester_1 | build Mon Feb 20 08:53:29 UTC 2023 tester_1 | start Mon Feb 20 08:53:45 UTC 2023
停止后再次运行结果
tester_1 | /log: tester_1 | build Mon Feb 20 08:54:28 UTC 2023 tester_1 | /test/log: tester_1 | build Mon Feb 20 08:53:29 UTC 2023 tester_1 | start Mon Feb 20 08:53:45 UTC 2023 tester_1 | start Mon Feb 20 08:54:33 UTC 2023
可以看到,不在卷中的/log每次启动都会被镜像内的新内容覆盖,而卷中的/test/log会保留历史内容,每次启动时追加新的日志。
绑定挂载方式测试
将docker-compose中的卷替换为绑定挂载配置- /test:/test,首次运行报错:Service "tester" is using volume "/test" from the previous container.。删除原有Docker卷后再次运行,结果如下:
tester_1 | /log: tester_1 | build Mon Feb 20 09:00:50 UTC 2023 tester_1 | /test/log: tester_1 | start Mon Feb 20 09:01:03 UTC 2023
可见RUN命令生成的构建日期数据没有被复制到绑定挂载的本地目录中。
结论
- 拉取镜像时,
VOLUME指定路径下的内容是包含在镜像中的;RUN命令是在镜像构建阶段执行的,并非容器启动时执行。首次使用命名卷挂载该路径时,镜像里的内容会被复制到卷中;后续启动容器时,卷的内容会覆盖容器内该路径的内容,镜像里的对应内容不再生效。 - 使用绑定挂载时,本地目录的内容会直接覆盖容器内
VOLUME指定的路径,镜像构建阶段RUN生成的该路径下的内容不会被复制到本地目录中。 - 对于已存在的命名卷,拉取更新版本的镜像后启动容器,镜像里
VOLUME路径的新内容不会覆盖原有卷的数据——只有首次创建卷时才会执行从镜像到卷的内容复制。
内容的提问来源于stack exchange,提问作者Nikki Locke

