这篇博文将帮助您理解两个类似的Dockerfile指令(ADD和COPY)之间的区别,以及它们如何成为现在的样子,以及我们对您应该使用哪条指令的建议。 (提示:不是ADD) 从Dockerfile构建Docker镜像时,您可以选择两个指令将目录/文件添加到镜像:ADD和COPY。两条指令都遵循相同的基本形式,并完成了几乎相同的事情:

ADD <src>... <dest>
COPY <src>... <dest>
复制代码

在这两种情况下,都会复制目录或文件()并将其添加到指定的路径的容器的文件系统中。

因此,如果两条指令都是相同的,为什么它们都存在,您应该使用哪一条?请仔细阅读,找出答案。

如果您对ADD和COPY的细微差别不感兴趣,只想回答“我应该使用哪一个?”,您只需要知道:使用COPY。

故事起源

与COPY指令不同的是,ADD从一开始就是Docker的一部分,除了从构建上下文中复制文件之外,还支持一些其他技巧。

ADD指令允许您使用URL作为参数。提供URL时,将从URL下载文件并将其复制到。

ADD http://foo.com/bar.go /tmp/main.go
复制代码

上面的文件将从指定的URL下载并添加到容器的文件系统/tmp/main.go中。另一种形式可以让你简单地为下载的文件指定目标目录:

ADD http://foo.com/bar.go /tmp/
复制代码

由于参数以尾部"/"结尾,因此Docker会从URL中推断出文件名并将其添加到指定的目录中。在这种情况下,一个名为/tmp/bar.go的文件将被添加到容器的文件系统中。

ADD的另一个功能是能够自动解压缩压缩文件。如果参数是一个识别压缩格式(tar,gzip,bzip2等)的本地文件,那么它将被解压到容器文件系统中的指定处。

ADD /foo.tar.gz /tmp/
复制代码

上面的命令会导致foo.tar.gz归档文件的内容被解压到容器的/ tmp目录中。 有趣的是,URL下载和解压功能不能一起使用。任何通过URL复制的压缩文件都不会自动解压缩。

魔法

显然,简单的ADD指令后面有很多功能。虽然这使得ADD非常灵活,但并没有使它具有特别的可预测性。以下是2013年12月针对ADD命令记录的问题的一段引文:

在我看来,当前的ADD指令非常的神奇。它可以添加本地和远程文件。它有时会解压一个文件,它有时不会解压文件。如果某个文件是要复制的tar包,则意外解压缩它。如果该文件是一个压缩格式的tar包,需要解压,则意外复制它。 - amluto

这个共识似乎是ADD试图做得太多而且让用户感到困惑。显然,没有人想要破坏与ADD现有用法的向后兼容性,所以决定添加一个更具可预测性的新指令。

类ADD,当更精简

当版本1.0的Docker发布时,包含了新的COPY指令。与ADD不同的是,COPY直接将文件和文件夹从构建上下文复制到容器中。

COPY不支持URL作为参数,因此它不能用于从远程位置下载文件。任何想要复制到容器中的东西都必须存在于本地构建上下文中。

另外,COPY对压缩文件没有特别的处理。如果您复制归档文件,它将完全按照出现在构建上下文中的方式落入容器中,而不会尝试解压缩它。

COPY实际上只是ADD的精简版本,旨在满足大部分“复制文件到容器”的使用案例而没有任何副作用。

使用哪个?

如果现在还不明显,Docker团队的建议是在几乎所有情况下都使用COPY。

真的,使用ADD的唯一原因是当你有一个压缩文件,你一定想自动解压到镜像中。理想情况下,ADD将被重新命名为EXTRACT之类的内容,以真正将这一点引入Docker生态(同样,出于向后兼容的原因,这不太可能发生)。 好的,但是从远程URL获取软件包的方法不是仍然有用吗?技术上,是的,但在大多数情况下,您可能会更好地运行curl或wget。考虑下面的例子:

ADD http://foo.com/package.tar.bz2 /tmp/
RUN tar -xjf /tmp/package.tar.bz2 \
  && make -C /tmp/package \
  && rm /tmp/package.tar.bz2
复制代码

这里我们有一条ADD指令,它从一个URL中检索一个包,然后是一条RUN指令,它将它解包,构建它,然后尝试清理下载的存档。

不幸的是,由于软件包检索和rm命令在单独的镜像层中,我们实际上并没有在最终镜像中节省任何空间(有关此现象的更详细的解释,请参阅我的优化docker镜像文章)。

在这种情况下,你最好像下面这样做:

RUN curl http://foo.com/package.tar.bz2 \
  | tar -xjC /tmp/package \
  && make -C /tmp/package

复制代码

这里我们使用curl命令下载压缩包,然后通过管道传递给tar命令解压。这样我们就不会在我们需要清理的文件系统上留下压缩文件。

将远程文件添加到镜像中可能仍有正当理由,但这应该是明确的决定,而不是您的默认选择。

最终,规则是这样的:使用COPY(除非你确定你需要ADD)。