从Docker中的Docker进行卷绑定挂载?

16
我有一个Docker容器"A",它将启动另一个容器"B"(通过卷挂载/var/run/docker.sock)。现在,这些容器需要共享文件。
容器"B"期望文件被卷挂载,最简单的方法是从"A"到"B"绑定挂载,但是Docker不能做到这一点(绑定挂载始终来自主机文件系统)。
是否有一种简单的方法让容器与其创建的容器共享文件,而不依赖于主机文件系统,并且不需要从"A"构建"B"镜像?
我一直在尝试创建卷并将文件复制到其中,但解决方案往往很复杂且脆弱。理想情况下,我想将解决方案放在docker-compose文件中,在"A"内运行,但那似乎几乎不可能。
供参考,这是另一个完美解决我的问题的想法:https://github.com/docker/compose/issues/3593#issuecomment-272089143
2个回答

20
当您挂载Docker套接字时,实际上并不是使用Docker in Docker,而只是客户端通过API向主机上的守护程序发出请求,而该守护程序不知道请求来自何处。因此,您可以将这个问题简化为“您能否将一个容器中的文件挂载到另一个容器中”。不幸的是,如果没有使用两个容器都外部的卷,那么没有一个简单的答案。这是因为容器文件系统依赖于用于组装各种映像和容器层的图形驱动程序,因此即使对于overlay2可能有效的解决方案也会在其他驱动程序上失效,并且它将取决于Docker的内部机制可能会在没有警告的情况下更改。
一旦进入外部卷,我想到了几个可能的解决方案。
选项A: 共同的主机目录。我经常在我的笔记本电脑上使用这个技巧来隐藏我正在容器内运行命令的事实。我会在容器中挂载一个常见的目录,比如-v $HOME:$HOME。如果在每个容器中挂载相同的主机目录,则可以从容器“A”和“B”内部使用此技术。如果您针对容器“A”使用像上面那样的卷挂载,则这将与compose文件一起使用,因为容器内的路径与主机上的路径相同。
选项B: volumes_from。我犹豫是否将其作为选项之一提及,因为随着用户采用swarm模式,它正在被淘汰,但是有一种方法可以将容器“A”中的所有卷安装到容器“B”中。这仍然需要您在容器“A”中定义一个卷,但现在您不关心卷的来源,它可以是主机、命名或匿名卷。
选项C: 共享命名卷。命名卷使Docker管理数据存储,默认情况下存储在主机上的/var/lib/docker/volumes目录下。您可以在两个容器中运行相同的命名卷,从而允许您在容器之间传递数据。您需要在容器“A”中拥有卷名称,以便使用相同的名称运行容器“B”的命令。当您第一次使用命名卷时,它们还会从映像初始化命名卷的内容,因此这可能是有益的,特别是对于文件所有权和权限。只需注意,在下一次使用相同的命名卷时,它不会重新初始化任何现有数据,而是以前的数据将是持久的。使用compose文件时,您需要将命名卷定义为外部卷。
选项D: 手动创建的命名卷。如果您只是想从容器“A”注入一些文件到容器“B”中,有多种方法可以通过Docker API注入该文件。我看到过将文件保存在“A”上的环境变量中,然后将环境变量写回到“B”的entrypoint中作为文件的方式。对于较大的文件或避免更改“B”的entrypoint,您可以创建一个命名卷,并通过docker的stdin/stdout管道传递数据到正在运行的容器中,并使用tar打包/解包该数据以通过I/O管道发送。这将在容器“A”内部工作,因为tar命令的一半在该容器的文件系统内运行。然后容器“B”将挂载该命名卷。要将数据从容器“A”导入命名卷,应如下所示:
tar -cC source_dir . | \
  docker run --rm -i -v target_vol:/target busybox tar -xC /target

要从具名卷中获取数据,需要反向执行此过程:

docker run --rm -v source_vol:/source busybox tar -cC /source . | \
  tar -xC target_dir

与选项C类似,您需要在compose文件中将此命名卷定义为外部卷。


1
哇,非常好的答案!非常感谢!当你这样说时,我最初拒绝的选项A可能是目前给我带来最少痛苦的解决方案。 - Krumelur
选项E:将A中的文件复制到B中(docker cp path/on/A containerB:path/on/B),在B中进行所需的工作,然后将结果复制回来(docker cp containerB:path/on/B path/on/A)。如果您的文件流程很简单(例如CI工作者),则对于Docker-in-Docker(“dind”)可以正常工作,否则请参考其他选项。 - Lenormju

0
如果您不依赖于卷,共享文件的另一种方法是将它们从"A"传输到"B"。您需要容器B在STDIN上处理输入,例如使用自定义入口点。
在容器A中:
cat some_file_on_A.txt | docker run -i container_B

容器 B 中的示例入口点:

while read input_from_A; do
  echo "${input_from_A}" > some_file_on_B.txt
done

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接