如何在Dockerfile中复制文件夹到Docker镜像?

153
我在我的Dockerfile中尝试了以下命令:COPY * /,结果非常惊讶。看起来这个天真的docker代码遍历了通配符中的目录,然后将每个文件倾倒到目标目录中,而且还恭敬地忽略了我的目录结构。
至少这是我理解这个问题的方式,它确实对应于我得到的结果。
我猜唯一可能存在这种行为的原因是有其他方法可以完成这个任务。但对于一个智力有限的人来说,要理解这个方法并不容易,有没有人知道呢?
10个回答

102

使用 ADD (文档)

ADD 命令可以接受一个 <src> 参数:

  1. 构建文件夹中的一个子文件夹 (和你的 Dockerfile 文件在同一个文件夹内)。您可以在 Dockerfile 中添加一行来指定该文件夹:
ADD folder /path/inside/your/container

  1. 在您的主机文件系统中的任何位置都可以使用单个文件存档。 要创建存档,请使用以下命令:
tar -cvzf newArchive.tar.gz /path/to/your/folder

您需要在 Dockerfile 中添加以下代码:

ADD /path/to/archive/newArchive.tar.gz  /path/inside/your/container

备注:

  • ADD 命令将自动提取您的归档文件。
  • 末尾斜杠的有无非常重要,请参阅链接文档。

4
谢谢您先生。卓越的解决方案。 我通过试错发现的唯一奇怪的事情是容器路径应该反映文件夹名称。例如,ADD myFolder /container/myFolder,然后myFolder及其内容将在预期位置。 - Leo Ufimtsev
2
我无法强调第一点有多重要,即 ADD 的第一个参数 _必须在构建文件夹内_。 - András Aszódi
如果你正在Windows上构建东西并需要在Dockerfile文件夹内获取一个文件夹结构,那么有一个很棒的命令叫做“robocopy /mir”,它可以镜像目录的内容。当你完成构建后,只需使用“erase /s /q”和“rmdir /s /q”命令,就像从未存在过一样。Robocopy非常棒。 - Jason Hughes
1
如果我将归档文件放在 /tmp 目录下,然后尝试 ADD /tmp/file.tar.gz,它无法解析。也许归档文件还需要在构建目录中。 - Christopher King
1
第一部分目前是不正确的。Dockerfile参考文档(https://docs.docker.com/engine/reference/builder/#add)非常清楚,如果`<src>是一个目录,则会复制目录的内容,而明确不会复制目录本身。这对于COPYADD`都是一样的。 - mtalexan

40

就像 @Vonc 所说,目前没有可能添加类似的命令。唯一的解决方法是提及文件夹并创建它,并将内容添加到其中。

# add contents to folder
ADD src $HOME/src

会在您的目录中创建一个名为src的文件夹,并将您的src文件夹的内容添加到其中。


32

使用ADD代替COPY。假设您想将主机上目录src中的所有内容复制到容器中的目录dst中:

使用ADD代替COPY。假设您想将主机上目录src中的所有内容复制到容器中的目录dst中:

ADD src dst

注意:容器中将自动创建目录dst。


8
'b'是什么?https://github.com/docker/docker/issues/18396 表明ADD命令不会创建子文件夹。 - VonC
3
你的回答不是很清晰,但是我理解了你想要表达的意思并且找到了我的答案。这个方法非常有效。 - Balasubramani M
这似乎不正确,如果我这样做,只会得到一个空文件夹。 - Vincent Gerris
1
原始答案是来自2016年。现在https://docs.docker.com/engine/reference/builder/应该是关于使用的真相来源。这个线程的原始问题是`COPY * /没有按预期行事,人们希望它像bash中的cp或powershell中的copy一样行事,但它并不是。作者应该使用COPY . /ADD . /,这本身就是一个问题,因为/`指的是根目录,这不太可能是有意的。不应使用COPY,因为它会导致不同操作系统的复制功能混淆。 - Hin Fan Chan
至少在2021年,它现在可以工作并创建src下面树形结构中的所有子文件夹。 - blissweb
根据2023年的文档和测试,它不会创建子文件夹,除非在源文件夹中包含“.”。因此,“ADD src/. dst/”将复制src文件夹中的所有文件和文件夹到dst中,但不会复制src文件夹本身。使用src/而不是src/.将复制src文件夹中的所有文件到dst中,而不包括src文件夹内的任何目录结构。 - mtalexan

25
正如您在工单中提到的

您使用了COPY files/* /test/,它会被扩展为COPY files/dir files/file1 files/file2 files/file /test/
如果将其拆分成单个的COPY命令(例如COPY files/dir /test/),您将看到(无论好坏)COPY将会复制每个参数dir的内容到目标目录中。不是参数dir本身,而是它的内容。

我对COPY不能保留顶层目录并不满意,但这种情况已经存在一段时间了。

因此,为了保持向后兼容性,不可能使用COPY/ADD来复制目录结构。
唯一的解决方法是一系列的RUN mkdir -p /x/y/z来构建目标目录结构,然后是一系列的docker ADD(每个文件夹一个)。 (根据评论,使用ADD而不是COPY。)

4
解决方法是正确使用 ADD 命令。我已经在下面的答案中尝试澄清其用法。 - ryanrain
2
COPY无法实现,但ADD可以。这个答案是误导性的,因为建议手动创建结构而不是使用ADD。根据@ryanrain的说法,文件夹和内容可以添加。我已经成功地通过ADD实现了这一点(但不是COPY)。我建议更新这个答案或删除它。 - Leo Ufimtsev

17

COPY . <destination>

可以将当前目录下的文件复制到指定目标路径。在你的情况下,应该是

COPY . /


7
请勿使用COPY * /app,它不会像你期望的那样工作。 相反,请使用COPY . /app以保留目录结构。 - kadee

13

我不完全理解原帖的情况,但我可以证明使用Dockerfile中的COPY命令可以复制目录结构。

假设您有以下文件夹结构:

folder1
  file1.html
  file2.html
folder2
  file3.html
  file4.html
  subfolder
    file5.html
    file6.html

要将它复制到目标图像,您可以使用以下Dockerfile内容:

FROM nginx

COPY ./folder1/ /usr/share/nginx/html/folder1/
COPY ./folder2/ /usr/share/nginx/html/folder2/

RUN ls -laR /usr/share/nginx/html/*

docker build . 的输出如下:

$ docker build --no-cache .
Sending build context to Docker daemon  9.728kB
Step 1/4 : FROM nginx
 ---> 7042885a156a
Step 2/4 : COPY ./folder1/ /usr/share/nginx/html/folder1/
 ---> 6388fd58798b
Step 3/4 : COPY ./folder2/ /usr/share/nginx/html/folder2/
 ---> fb6c6eacf41e
Step 4/4 : RUN ls -laR /usr/share/nginx/html/*
 ---> Running in face3cbc0031
-rw-r--r-- 1 root root  494 Dec 25 09:56 /usr/share/nginx/html/50x.html
-rw-r--r-- 1 root root  612 Dec 25 09:56 /usr/share/nginx/html/index.html

/usr/share/nginx/html/folder1:
total 16
drwxr-xr-x 2 root root 4096 Jan 16 10:43 .
drwxr-xr-x 1 root root 4096 Jan 16 10:43 ..
-rwxr-xr-x 1 root root    7 Jan 16 10:32 file1.html
-rwxr-xr-x 1 root root    7 Jan 16 10:32 file2.html

/usr/share/nginx/html/folder2:
total 20
drwxr-xr-x 3 root root 4096 Jan 16 10:43 .
drwxr-xr-x 1 root root 4096 Jan 16 10:43 ..
-rwxr-xr-x 1 root root    7 Jan 16 10:32 file3.html
-rwxr-xr-x 1 root root    7 Jan 16 10:32 file4.html
drwxr-xr-x 2 root root 4096 Jan 16 10:33 subfolder

/usr/share/nginx/html/folder2/subfolder:
total 16
drwxr-xr-x 2 root root 4096 Jan 16 10:33 .
drwxr-xr-x 3 root root 4096 Jan 16 10:43 ..
-rwxr-xr-x 1 root root    7 Jan 16 10:32 file5.html
-rwxr-xr-x 1 root root    7 Jan 16 10:32 file6.html
Removing intermediate container face3cbc0031
 ---> 0e0062afab76
Successfully built 0e0062afab76

尝试在“folder2”中添加更多子文件夹。您可能会发现并非所有子文件夹都被复制了... - Nagev
有用的回答!然而需要注意的是,我发现在“.gitignore”(当然还有“.dockerignore”)中列出的文件/文件夹不会被复制过去。 - Jannie Theunissen

2
FROM openjdk:8-jdk-alpine
RUN apk update && apk add wget openssl lsof procps curl
RUN apk update
RUN mkdir -p /apps/agent
RUN mkdir -p /apps/lib
ADD ./app/agent /apps/agent
ADD ./app/lib /apps/lib
ADD ./app/* /apps/app/
RUN ls -lrt /apps/app/
CMD sh /apps/app/launch.sh

在我的 Dockerfile 中,我将 ./apps/agent./apps/lib 目录复制到 /apps/agent/apps/lib 目录中。

0

将 * 替换为 /

因此,不使用

COPY * <destination>

而是使用

COPY / <destination>


14
不好的主意。COPY /<destination>将会把你的根卷复制到Docker镜像中。 - jarlef
1
@jarlef 这种行为似乎在Windows和Linux之间有所不同。在Windows中,它可以很好地获取构建上下文。 - duct_tape_coder
12
在Linux上,COPY / <destination> 不会将您的根卷复制到Docker镜像中。它将复制构建上下文的根目录。您无法从构建上下文外部将任何内容复制到镜像中。 - dpwr

-2

假设您想将包含Docker文件的文件夹中的内容复制到容器中。 请使用ADD命令:

RUN mkdir /temp
ADD folder /temp/Newfolder  

它将添加到您的容器中,位于temp/newfolder文件夹中。该文件夹是您拥有dockerfile的文件夹/目录,更具体地说,是您放置内容并希望复制的位置。现在,您可以通过运行容器并使用ls查看内容来检查已复制/添加的文件夹。

-6

最简单的方法:

sudo docker cp path/on/your/machine adam_ubuntu:/root/path_in_container

如果您正在复制需要通过根目录使用~获取的内容,请将其放入路径中。


4
我希望你现在能够理解为什么这个回答不合适 :) 它既不是最简单的,也是最糟糕的实践方式,因为你不应该干扰实际的容器,并且它与OP的问题没有关系,因为OP询问的是Dockerfile,而不是一个shell命令。 - Eksapsy

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