Docker-如何将文件从映像复制到主机?


117

我的问题是有关这个问题的复制从容器主机的文件; 我有一个Dockerfile可以获取依赖项,从源代码编译构建工件,并运行可执行文件。我还想复制构建工件(在我的情况下,它是.zipsbt dist'../ target /`产生的,但是我认为这个问题也适用于jar,二进制文件等。

docker cp适用于容器,而不是图像;我需要启动一个容器只是为了从中获取文件吗?在脚本中,我尝试/bin/bash在后台以交互模式运行,将文件复制出来,然后杀死容器,但这似乎很麻烦。有没有更好的办法?

另一方面,我想避免只是为了取出一个文件而.tar在运行后解压缩docker save $IMAGENAME文件(但是,这似乎是目前最简单的选择,即使是最慢的选择)。

我会使用docker卷,例如:

docker run -v hostdir:out $IMAGENAME /bin/cp/../blah.zip /out

但是我正在boot2dockerOSX上运行,我不知道如何直接写到我的Mac主机文件系统(boot2docker虚拟机中安装了读写卷,这意味着我无法轻松地共享脚本以blah.zip从映像中提取内容)其他人,有什么想法吗?

Answers:


172

要从图像复制文件,请创建一个临时容器,从中复制文件,然后将其删除:

id=$(docker create image-name)
docker cp $id:path - > local-tar-file
docker rm -v $id

create添加/删除了命令的哪个版本的
docker

2
@ThorSummoner docker createdocker
Igor Bukanov

1
我不知道为什么没有选择这个作为正确答案。
CentAu

1
绝对正确的答案!!!不依赖容器内的任何东西...对于Golang暂存图像,这是唯一可行的方法!
Marcello de Sales

2
有什么理由要复制到stdout,然后将其定向到本地文件?当我这样做时,它在文件内容之前和之后转储了一堆控制字符。直接运行它就像docker cp $id:path > local-tar-file完美的工作。
Yonatan

62

不幸的是,似乎没有一种直接从Docker映像复制文件的方法。您需要先创建一个容器,然后从该容器复制文件。

但是,如果您的映像包含一个cat命令(在许多情况下会执行),则可以使用单个命令来执行此操作:

docker run --rm --entrypoint cat yourimage  /path/to/file > path/to/destination

如果您的图片不包含cat,只需创建一个容器并docker cp按照Igor的答案中的建议使用命令即可。


1
很棒的解决方案。无法启动我的容器,因为它在启动后崩溃了一秒钟,但需要在其中抓取一个文件。这工作得很好。
Mirodinho '17

23

更快的选择是将文件从正在运行的容器复制到安装的卷:

docker run -v $PWD:/opt/mount --rm --entrypoint cp image:version /data/libraries.tgz /opt/mount/libraries.tgz

真正的0m0.446s

** VS **

docker run --rm --entrypoint cat image:version /data/libraries.tgz > libraries.tgz

真正的0m9.014s


与第二个示例中实际复制文件的字节相比,这与第一个示例中的基础文件系统执行文件的懒/浅复制(想写时复制)有更多关系。一个有用的测试将是查看cat a >bvs cp a b是否具有类似此处所示的时间。另外,如果源路径和目标路径位于不同的文件系统上,则这两个示例都将导致完整的逐字节复制。
KevinOrr

14

家长的评论已经显示了如何使用cat。您也可以以类似方式使用tar

docker run yourimage tar -c -C /my/directory subfolder | tar x

1
这个答案是按照原始问题询问复制目录而不是文件。但是,+ 1是因为它也可以与文件一起使用,并且具有额外的功能:权限和所有者保留。大!
caligari17年

5
实际上,我使用docker run --rm --entrypoint tar _image_ cC _img_directory_ . | tar xvC _host_directory_
caligari's

6
docker cp $(docker create registry.example.com/ansible-base:latest):/home/ansible/.ssh/id_rsa ./hacked_ssh_key

想提供一线解决方案

编辑:容器甚至不必在此解决方案中运行


1

此问题的另一个(简短)答案:

docker run -v $PWD:/opt/mount --rm -ti image:version bash -c "cp /source/file /opt/mount/"

0

我在MacOS上使用boot2docker。我可以向您保证,基于“ docker cp”的脚本是可移植的。因为任何命令都在boot2docker内部进行中继,但是二进制流随后又中继回到在Mac上运行的docker命令行客户端。因此,来自docker客户端的写入操作在服务器内部执行,并写回到正在执行的客户端实例中!

我正在与我提供的任何Docker容器共享Docker卷的备份脚本,并且我的备份脚本已在Linux和MacOS上使用boot2docker进行了测试。可以在平台之间轻松交换备份。基本上,我在脚本中执行以下命令:

docker run --name=bckp_for_volume --rm --volumes-from jenkins_jenkins_1 -v /Users/github/jenkins/backups:/backup busybox tar cf /backup/JenkinsBackup-2015-07-09-14-26-15.tar /jenkins

运行一个新的busybox容器,并使用名称jenkins_jenkins_1装载我的jenkins容器的体积。整个卷都写入文件backups / JenkinsBackup-2015-07-09-14-26-15.tar

我已经在Linux容器和mac容器之间移动了归档文件,而没有对备份或还原脚本进行任何调整。如果这是您想要的内容,请在此处找到整个脚本的教程:blacklabelops / jenkins


0

您可以主机上的本地路径绑定到容器上的路径,然后cp在脚本末尾将所需文件绑定到该路径。

$ docker run -d \
  -it \
  --name devtest \
  --mount type=bind,source="$(pwd)"/target,target=/app \
  nginx:latest

然后,无需再进行复制。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.