使用Docker进行COPY操作,但带有排除功能。

584

在Dockerfile中,我有以下内容

COPY . .
我想排除一个完整的目录,例如在我的情况下是node_modules目录。
类似于这样:
   COPY [all but **/node_modules/**] .

使用Docker可以实现这个吗?


71
.dockerignore 文件怎么样? - anthony sottile
我正在寻找与此相关的问题的答案:https://dev59.com/PVUL5IYBdhLWcg3wUWit - Alexander Mills
3
@AnthonySottile dockerignore 可以防止文件成为构建上下文的一部分。 - Marcello Romani
1
我在 .dockerignore 文件中加入了 .venv 目录(.venv 和 **/.venv),但是在 Dockerfile 中使用 COPY . . 命令时,该目录仍然被复制。 - ierdna
2
我的项目有50个目录,编译需要大约20分钟。构建脚本的一部分还包括其他小的编译过程(由其他脚本执行),目前这些过程失败了。我基本上只想将这两个编译过程分开,并调试那些额外的脚本。而不用编写50多个COPY语句,指定除这些脚本之外的所有内容。关键点在于希望缓存第一次编译的结果。 - Beolap
7个回答

887

16
谢谢,所以我猜“COPY . .”命令不需要更改。 - Alexander Mills
20
在子目录中仅仅使用node_modules是无效的。 - rustyx
103
如果你想将它COPY,但不希望在那一层中出现,这并不能真正解决问题。 - Wes
4
这个回答的根本错误在于.dockerignore可以防止文件被发送到Docker服务器,从而将它们从构建上下文中排除。相反,更有用的是能够复制一个文件夹,但排除掉其中某些内容。 - Marcello Romani
6
如果是这样的话,您能否解释一下为什么.dockerignore文档中第一段说:“这有助于避免向守护程序发送不必要的大型或敏感文件和目录,并可能使用ADD或COPY将它们添加到映像中。”? - ceejayoz
显示剩余6条评论

150

对于那些无法使用.dockerignore文件的人(例如,如果您需要在一个COPY中使用该文件但在另一个COPY中不需要):

是的,但您需要多个COPY指令。具体来说,您需要为要排除的文件名中的每个字母都需要一个COPY。

COPY [^n]*    # All files that don't start with 'n'
COPY n[^o]*   # All files that start with 'n', but not 'no'
COPY no[^d]*  # All files that start with 'no', but not 'nod'

继续查找,直到您获得完整的文件名或仅获得您合理确定不会有其他文件的前缀。

5
因为没有以'no'开头的文件,所以会出现COPY failed: no source files were specified错误提示,但如果有以'nod'开头的文件则不会。 - mheck
2
或者,您可以使用两个不同的子目录(用于不同的复制目标),例如在我的nginx设置中,我将src/复制到/var/www,并将/conf/nginx.conf复制到/etc/nginx/conf.d/default.conf。此外,您可以复制所有内容,稍后再运行RUN rm -r conf(以保持源树不变)。如果有其他建议,我很乐意听取。 - Yuval
2
如果我这样做,针对特定的文件类型或文件结尾,有什么解决方案? - Lea Reimann
17
这种方法唯一的缺点是您的文件夹结构会被压平。 - Islam
1
如果你只需要复制几个目录(比如只有 '/app' 或者 '/src'),更简单的方法是逐个复制目录(保持它们的结构),然后你只需要一个 COPY [^node_modules]* 就可以复制所有顶层文件。 - smdufb

15

一行代码解决方案:在项目根目录下的命令提示符终端中输入以下命令:

echo node_modules >> .dockerignore

此命令将在.dockerignore文件中追加“node_modules”。如果.dockerignore文件不存在,它将创建一个新文件。请将node_modules替换为您要排除的文件夹。

警告:如果您是 Docker 生态系统的新手和/或已经在项目中有.dockerignore文件,请在执行操作之前备份。

奖励:(由 Joey Baruch 指出)

(To CREATE/OVERWRITE the .dockerignore file via PowerShell, which can be handled by Docker):
>> echo node_modules | Out-File -Encoding UTF8 .dockerignore

3
不要在PowerShell中这样做,它默认输出UTF-16,我认为docker无法处理。 - Joey Baruch
18
你真的想让人们丢失他们的 .dockerignore 文件内容吗?如果是这样,就执行以下命令:echo node_modules >> .dockerignore。 - webcitron
4
我不知道为什么这个答案有那么多赞,如果人们在已经有.dockerignore文件的项目中直接复制粘贴它,会很危险。此外,如果没有解释.dockerignore文件的功能,有人可能会认为这是告诉Docker下次构建时不要使用该文件夹的方法。请注意,你正在回答一些不熟悉Docker工作原理的人。 - Daniel Campos Olivares
1
@JoeyBaruch 感谢您提到它。我也更新了PowerShell的答案。 :) - Naveen Kumar V
2
@NaveenKumarV 更正:如果您使用卷映射,它将映射所有内容,而不管您在.dockerignore文件中放置了什么。如果您不使用卷映射,则可以使用.dockerignore将主机文件排除在镜像之外。 - Peter Kionga-Kamau
显示剩余6条评论

14

从当前目录中排除 node_modules

node_modules

在任何直接子目录中排除node_modules。
*/node_modules

这是官方文档链接,其中涉及到Docker的技术相关内容。

11
你可以使用 **/node_modules 来同时执行这两个操作。 - bfontaine
1
@PeterKionga-Kamau,它们确实有影响。ADD和COPY命令明确提到受dockerignore影响:https://docs.docker.com/engine/reference/builder/#dockerignore-file - Leon Lucardie
2
@PeterKionga-Kamau 我尝试了多次。今天我已经使用 Docker v20.10.16 进行了重新验证,任何与 .dockerignore 条目匹配的文件都不会被添加到我在 Dockerfile 中添加的 COPY 或 ADD 命令中。 - Leon Lucardie
2
@PeterKionga-Kamau,截至5月16日,v20.10.16是最新版本。我刚刚使用全新的dockerfile再次尝试了一遍,但是所有我在.dockerignore中设置的模式/文件都没有被COPY或ADD命令捕获。你有没有一个它无法工作的例子? - Leon Lucardie
2
@PeterKionga-Kamau 这是链接:https://github.com/leonluc-dev/dockerignoreexample 构建并运行 Docker 镜像后,检查镜像应该显示 dockerignore 文件中的条目(在本例中为 testfile1.txt)未被复制到 Docker 镜像中。 - Leon Lucardie
显示剩余3条评论

8

我采用了多阶段构建方法,因为我需要一个阶段能够访问该文件,而另一个阶段不能,所以 .dockerignore 无法解决这个问题:

FROM ruby AS builder

COPY app/ app/

# Do stuff with app

# remove the stuff you don't want
RUN rm -Rf app/assets

FROM ruby AS publish

# In my real version I needed the absolute path to builder WORKDIR.
# Since I'm copying from the builder stage, app/assets won't exist
# and neither will it be part of the publish image.
COPY --from=builder app app

5
这是错误的。app/assets仍然在图像中,尽管在一个不可见的层中。 - Andreas Wittig
2
@AndreasWittig,在多阶段构建中,只有在最终阶段复制的内容才会被保留。在我的例子中,我们有一个构建器阶段,它删除了一些文件。这些文件仍然存在于构建器镜像的一个层中,但正如你所说,它们不再是该层的一部分。当我们复制到发布阶段时,由于这些文件不可见,它们将不会包含在来自发布阶段的镜像中。 - moger777
只有使用多阶段构建才能真正地清理掉。否则,@AndreasWittig 是正确的:被忽略的文件仍然在镜像的一层中。 - Paul Rey
2
根据https://docs.docker.com/build/building/multi-stage/,使用多阶段构建可以从另一个阶段复制所需内容,并丢弃其余部分。构建了两个镜像,构建器镜像包含了所有的冗余内容,但是发布镜像,也就是你要部署的镜像,不会包含这些冗余内容,因为你只复制了未被删除的部分。 - undefined
1
根据 https://docs.docker.com/build/building/multi-stage/,使用多阶段构建允许您从另一个阶段复制所需内容,并且会丢弃其余部分。共构建了两个镜像,构建器镜像具有所有冗余内容,但发布镜像(即您要部署的镜像)不包含冗余内容,因为您只复制了未被删除的部分。 - moger777
显示剩余4条评论

6

对于使用gcloud build的用户:

gcloud build会忽略.dockerignore,而寻找.gcloudignore

请使用:

cp .dockerignore .gcloudignore

Source


1
这两者之间的文件格式是否相同? - edjm
1
".gcloudignore的语法大量借鉴了.gitignore的语法;请参阅https://git-scm.com/docs/gitignore或man gitignore以获取完整的参考信息。" - https://cloud.google.com/sdk/gcloud/reference/topic/gcloudignore @edjm - JesusIniesta

4

对于我来说,添加 .dockerignore 文件是有效的。

还有一个额外的提示,如果你在 Windows 上尝试此解决方案Windows 不会让你创建 .dockerignore 文件(因为默认情况下不允许创建以.开头的文件)。

要在 Windows 上创建以 . 开头的文件也包括结尾的句点,比如:.dockerignore.并按回车键(前提是你已经从文件夹选项中启用了查看扩展名选项)。


11
在Windows中,您可以通过在文件名末尾添加一个点来创建以点开头的文件。 - Jim Pedid

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