如何从Dockerfile复制文件到主机?

59

我希望在dockerfile成功构建时获取一些文件,但它没有将文件从容器复制到主机。

这意味着当我构建dockerfile时,文件已经在主机上了。


Docker - 从容器复制文件到主机 - LF00
6个回答

77

自2019年7月Docker 19.03.0引入了“自定义构建输出”功能,现在已经成为可能。有关自定义构建输出的官方文档请查看。

要在构建过程中将构建镜像中的自定义构建输出导出到主机上,您需要启用BuildKit。这是引擎进行构建阶段的新推荐向后兼容的方法。有关启用BuildKit的官方文档,请查看。

这可以通过以下两种方式完成:

  1. 设置环境变量DOCKER_BUILDKIT=1,或
  2. 通过将"features": { "buildkit": true }添加到配置json文件的根目录中,将其默认设置为docker engine。

有关自定义构建输出的官方文档如下所示:

自定义导出器允许您将构建产物导出为本地文件系统上的文件,而不是Docker镜像,这对于生成本地二进制文件、代码生成等非常有用。
本地导出器将生成的构建文件写入客户端侧的目录。tar导出器类似,但将文件作为单个tarball(.tar)写入。
如果未指定类型,则该值默认为本地导出器的输出目录。
--output选项将从目标阶段导出所有文件。仅导出特定文件的常见模式是进行多阶段构建,并使用COPY --from将所需文件复制到新的scratch阶段中。
例如一个Dockerfile示例。
FROM alpine:latest AS stage1
WORKDIR /app
RUN echo "hello world" > output.txt

FROM scratch AS export-stage
COPY --from=stage1 /app/output.txt .

运行中

DOCKER_BUILDKIT=1 docker build --file Dockerfile --output out .

输出的尾部是:

 => [export-stage 1/1] COPY --from=stage1 /app/output.txt .
0.0s
 => exporting to client
0.1s
 => => copying files 45B
0.1s

这将生成一个本地文件out/output.txt,该文件是由RUN命令创建的。

$ cat out/output.txt
hello world

所有文件均从目标阶段输出

--output选项将从目标阶段导出所有文件。因此,如果使用非临时阶段和COPY --from,会导致多余的文件复制到输出中。建议使用临时阶段和COPY --from


目前不支持Windows操作系统:


4
2020年,这应该是被选中的答案!这就是方法。Ben T => 谢谢! - Xavi Montero
它仍在运行。 - PHP Avenger
1
如何只获取精确的文件,而不是在 / 中获取所有文件?@Vaccano @Ben T - gfan
@gfan 上面的示例已经复制了单个文件。你看到了什么? - Ben T
1
@gfan 这是已记录的行为。文档中指出,“--output”选项会导出目标阶段中的所有文件。 - Ben T
显示剩余6条评论

20
将文件从Dockerfile复制到主机是不被支持的。Dockerfile只是指定如何构建镜像的配方。
当您构建时,可以使用COPY指令ADD从主机向正在构建的镜像复制文件。
您还可以使用docker cp容器(已经docker run过的镜像)向主机复制文件(实际上,cp也可以从主机向容器复制)。

如果你想将在构建过程中生成的一些文件(例如调用脚本生成SSL)返回到主机,你可以运行一个容器,挂载来自主机的文件夹并执行cp命令。

例如,请参见getcrt脚本

docker run -u root --entrypoint=/bin/sh --rm -i -v ${HOME}/b2d/apache:/apache apache << COMMANDS
pwd
cp crt /apache
cp key /apache
echo Changing owner from \$(id -u):\$(id -g) to $(id -u):$(id -u)
chown -R $(id -u):$(id -u) /apache/crt
chown -R $(id -u):$(id -u) /apache/key
COMMANDS

"我是一个有用的助手,可以为您翻译文本。以下是需要翻译的内容:

COMMANDS之间的所有内容都是在容器上执行的命令,包括那些在主机${HOME}/b2d/apache文件夹上进行复制的cp命令,并通过-v ${HOME}/b2d/apache:/apache挂载到容器中作为/apache

这意味着每次您在容器中复制任何内容到/apache时,实际上是在主机上的${HOME}/b2d/apache上进行复制!

"

1
谢谢您的回复。非常具体,我会尝试一下。 - Allen
我们如何从dockerfile中实现这个? - SudarP
@SudarP 我认为这个2015年的答案是说它无法完成?你有什么具体的用例吗? - VonC
1
可能有这样的用例,您需要在构建时显式地将数据复制回主机,而不是在稍后的容器运行时。例如,假设您有一个多阶段构建,在其中一个构建阶段中执行某些单元测试并编写测试覆盖报告。您需要在特定的构建阶段通过此操作 - 失败的测试应该导致构建失败,并且不应在稍后的容器运行中出现。但是您也可能需要重新训练代码覆盖报告文件,以供在任何镜像或容器之外的其他用途中使用。 - ely
1
除此之外,整个过程可能在 CI/CD 系统中运行,在这种情况下,为了复制文件而拉取 Docker 容器并在全新的 CI/CD 步骤中运行它是很昂贵的 - 您确实需要在构建发生的同一 CI/CD 过程中完成它,而不是在稍后进行容器拉取 + 运行。 - ely
@ely 很好的观点。当我写这个答案时,多阶段构建还不存在。 - VonC

9

虽然不能通过 Dockerfile 功能直接支持,但是可以从已构建的 Docker 镜像中拷贝文件。

containerId=$(docker create example:latest)
docker cp "$containerId":/source/path /destination/path
docker rm "$containerId"

这正是我正在寻找的,谢谢。 - Nazar

1
也许您可以将主机的文件夹作为卷挂载,并将文件放置在那里?通常情况下,挂载的卷可以从容器内部写入,除非您在挂载时指定了只读选项。
docker run -v ${PWD}:/<somewritablepath> -w <somewritablepath> <image> <action>

0

您也可以使用网络。从此答案的脚本开始:https://dev59.com/AlkS5IYBdhLWcg3wem36#58255859

  • 我在主机上运行此脚本,将输出放在我想要的文件夹中

  • 在我的 Docker 中:

    RUN apt-get install -y curl RUN curl -F 'file=@/path/to/file' http://172.17.0.1:44444/

请注意,至少对我来说,172.17.0.1 在构建 dockerfile 时始终是主机的 IP。


0
完成使用Dockerfile的Docker Build后,以下内容适用于我:
其中/var/lib/docker/是我设置的Docker根目录
sudo find /var/lib/docker/ -name DESIRED_FILE -type f | xargs sudo ls -hrt | tail -1 | grep tgz | xargs  -i  sudo cp -v  '{}' PATH_ON_HOST

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