Dockerfile中的“ COPY”和“ ADD”命令有什么区别?


2194

Dockerfile中的COPYADD命令之间有什么区别,何时可以在另一个之上使用?

COPY <src> <dest>

COPY指令将从以下位置复制新文件<src>并将其添加到容器的文件系统中:<dest>

ADD <src> <dest>

ADD指令将从复制新文件<src>并将其添加到位于path的容器的文件系统中<dest>



9
截至2018年6月,参考文献指出ADD添加到映像(即静态文件),而COPY添加到容器(即映像的运行时实例)。当然这意味着每次运行Docker映像时都会执行COPY,或者这仅仅是术语不一致的情况?
克里斯·罗宾逊

14
我认为这是不一致的术语
Daniel Stevens

6
@ChrisRobinson,它不可能在COPY每次运行时都执行,因为它不一定有权访问原始上下文来获取内容。
肯·威廉姆斯

Answers:


2165

您应该查看ADDCOPY文档以获取有关其行为的更详细描述,但简而言之,主要区别在于ADD可以做的还不止COPY

  • ADD允许<src>成为网址
  • 参考下面的评论,ADD 文档指出:

    如果是以公认的压缩格式(身份,gzip,bzip2或xz)的本地tar归档文件,则将其解压缩为目录。来自远程URL的资源不会被解压缩。

请注意,编写Dockerfile最佳实践建议COPYADD不需要魔术的地方使用。否则,当您打算复制到容器中时,您(由于必须查找此答案)可能有一天会感到惊讶keep_this_archive_intact.tar.gz,而是将内容喷洒到文件系统上。


65
只是想澄清一点:将ADD与URL指向.tar.gz一起使用不会将存档提取到文件系统中(我现在已经仔细检查了两次以确保得到确认)
Cecile

42
这是必不可少的信息,官方Dockerfile参考未以此方式澄清区别是犯罪。
Cheeso

1
不确定,如果这对于一张图像来说是不同的。我使用busybox图片和ADD作为压缩文件。它只是简单地出现在目标目录中而没有解压缩。我认为,提取仅发生在压缩包中,但是我现在还没有检查过。
Santosh Kumar Arjunan

4
@SantoshKumarArjunan:Docker文档陈述了有关ADD和自动tar提取的以下内容: If <src> is a local tar archive in a recognized compression format (identity, gzip, bzip2 or xz) then it is unpacked as a directory. Resources from remote URLs are not decompressed. Docker ADD
hmacias

1
COPY允许--from = <name | index>,在这里我找不到对ADD的相同支持
Brandon

474

COPY

与“ ADD”相同,但没有tar和远程URL处理。

直接从源代码中引用。


15
我是否正确看到了这一点:ADD创建了不存在的目录。因此,尽管不建议在整个线程中使用它,但它有一个优势COPY因为您不必运行mkdir并保存某些键入内容
eli

3
COPY也这样做@eli
bhordupur,

到目前为止最好的解释。为什么它不是公认的答案?
xdevx32

141

关于这一点,有一些官方文档:编写Dockerfile的最佳实践

由于图像大小很重要,ADD因此强烈建议不要使用从远程URL获取软件包的方法。您应该使用curlwget代替。这样,您可以在提取文件后删除不再需要的文件,而不必在图像中添加其他图层。

RUN mkdir -p /usr/src/things \
  && curl -SL http://example.com/big.tar.gz \
    | tar -xJC /usr/src/things \
  && make -C /usr/src/things all

对于不需要ADDtar自动提取功能的其他项目(文件,目录),应始终使用COPY



18
Docker表示更喜欢COPY,因为它更加透明。从Docker File Best Practices(2014-12-15): Although ADD and COPY are functionally similar, generally speaking, COPY is preferred. That’s because it’s more transparent than ADD. COPY only supports the basic copying of local files into the container, while ADD has some features that are not immediately obvious.
schemar 2014年

115

从Docker文档中:

添加或复制

尽管ADD和COPY在功能上相似,但通常来说COPY是首选。那是因为它比ADD更透明。COPY仅支持将本地文件基本复制到容器中,而ADD的某些功能(如仅本地tar提取和远程URL支持)并不立即显而易见。因此,与ADD rootfs.tar.xz /中一样,ADD的最佳用途是将本地tar文件自动提取到映像中。

更多:编写Dockerfile的最佳实践


46

如果要将xx.tar.gz添加到/usr/localin容器中,请解压缩它,然后删除无用的压缩包。

对于COPY:

COPY resources/jdk-7u79-linux-x64.tar.gz /tmp/
RUN tar -zxvf /tmp/jdk-7u79-linux-x64.tar.gz -C /usr/local
RUN rm /tmp/jdk-7u79-linux-x64.tar.gz

对于添加:

ADD resources/jdk-7u79-linux-x64.tar.gz /usr/local/

ADD支持仅本地的tar提取。除此之外,COPY将使用三层,而ADD仅使用一层。


3
为什么不只有两层呢?RUN tar -zxvf /tmp/jdk-7u79-linux-x64.tar.gz -C /usr/local && rm /tmp/jdk-7u79-linux-x64.tar.gz
斯蒂芬·C

24

COPY 将文件/目录从主机复制到图像。

ADD 将文件/目录从主机复制到图像,但也可以获取远程URL,提取TAR文件等。

使用COPY简单复制文件和/或目录到构建上下文。

使用ADD下载远程资源,提取TAR文件等。


4
对于像我这样的
菜鸟的

17

来自Docker文档:https : //docs.docker.com/engine/userguide/eng-image/dockerfile_best-practices/#add-or-copy

“尽管ADD和COPY在功能上相似,但通常来说COPY是首选。这是因为它比ADD更透明。COPY仅支持将本地文件基本复制到容器中,而ADD具有某些功能(例如仅本地tar提取和远程URL支持)并不是立即显而易见的,因此,ADD的最佳用途是将本地tar文件自动提取到映像中,如ADD rootfs.tar.xz /中所示。

如果您有多个使用不同上下文的文件的Dockerfile步骤,请单独复制而不是一次全部复制。这将确保仅在特别需要的文件发生更改时,才使每个步骤的构建缓存无效(强制重新运行该步骤)。

例如:

 COPY requirements.txt /tmp/
 RUN pip install --requirement /tmp/requirements.txt
 COPY . /tmp/

与放置COPY相比,导致RUN步骤的缓存失效更少。/ tmp /之前。

由于图像大小很重要,因此强烈建议不要使用ADD从远程URL获取软件包。您应该使用curl或wget代替。这样,您可以在提取文件后删除不再需要的文件,而不必在图像中添加另一层。例如,您应该避免做以下事情:

 ADD http://example.com/big.tar.xz /usr/src/things/
 RUN tar -xJf /usr/src/things/big.tar.xz -C /usr/src/things
 RUN make -C /usr/src/things all

相反,请执行以下操作:

 RUN mkdir -p /usr/src/things \
     && curl -SL htt,p://example.com/big.tar.xz \
     | tar -xJC /usr/src/things \
     && make -C /usr/src/things all

对于不需要ADD的tar自动提取功能的其他项目(文件,目录),应始终使用COPY。”


7

来源:https : //nickjanetakis.com/blog/docker-tip-2-the-difference-between-copy-and-add-in-a-dockerile

COPY和ADD都是具有相似用途的Dockerfile指令。它们使您可以将文件从特定位置复制到Docker映像中。

COPY接收src和目的地。它仅允许您从主机(构建Docker映像的机器)的本地文件或目录中复制到Docker映像本身。

ADD也允许您执行此操作,但是它还支持其他2个来源。首先,您可以使用URL代替本地文件/目录。其次,您可以将tar文件从源直接提取到目标中

当您想将本地tar文件提取到Docker映像中的特定目录中时,ADD的有效用例。

如果要将本地文件复制到Docker映像,请始终使用COPY,因为它更加明确。


7

创建Dockerfile时,有两个命令可用于将文件/目录复制到其中:ADDCOPY。尽管它们的功能范围略有不同,但它们实际上执行相同的任务。

那么,为什么我们有两个命令,又如何知道何时使用一个或另一个?

DOCKER ADD命令

首先,请注意该ADD命令早于COPY。自从Docker平台启动以来,该ADD指令一直是其命令列表的一部分。

该命令将文件/目录复制到指定容器的文件系统。

ADD命令的基本语法为:

ADD <src> … <dest>

它包括您要复制的源(<src>),然后是您要存储它的目标(<dest>)。如果源是目录,则ADD复制其中的所有内容(包括文件系统元数据)。

例如,如果文件在本地可用,并且您要将其添加到图像目录中,则键入:

ADD /source/file/path  /destination/path

ADD也可以从URL复制文件。它可以下载外部文件并将其复制到所需的目的地。例如:

ADD http://source.file/url  /destination/path

另一个功能是,它复制压缩文件,自动提取给定目标中的内容。此功能仅适用于本地存储的压缩文件/目录。

ADD source.file.tar.gz /temp

请记住,您无法从URL下载和解压缩文件/目录。将外部软件包复制到本地文件系统时,该命令不会解压缩它们。

DOCKER COPY命令

由于某些功能问题,Docker必须引入额外的命令来复制内容– COPY

与其密切相关的ADD命令不同,它COPY仅具有一个分配的功能。它的作用是按照现有格式在指定位置复制文件/目录。这意味着它不处理提取压缩文件,而是按原样复制它。

该指令只能用于本地存储的文件。因此,您不能将其与URL一起使用来将外部文件复制到您的容器中。

要使用该COPY指令,请遵循基本命令格式:

输入源以及您希望命令将内容提取到的位置,如下所示:

COPY <src> … <dest> 

例如:

COPY /source/file/path  /destination/path 

使用哪个命令?(最佳做法)

考虑到COPY引入命令的环境,很明显,保留ADD是必要的。Docker发布了一份正式文档,概述了编写Dockerfile的最佳实践,明确建议不要使用该ADD命令。

Docker的官方文档指出,这COPY应该始终是入门指南,因为它比更加透明ADD

如果您需要从本地构建上下文复制到容器中,请坚持使用COPY

Docker团队还强烈建议您不要使用ADD从URL下载和复制软件包的方法。相反,在RUN命令中使用wget或curl更安全,更有效。这样可以避免创建额外的图像层并节省空间。


4

重要的提示

我不得不COPY在我的docker映像中解压缩java包。当我比较使用ADD创建的docker映像大小时,它比使用COPY,tar -xzf * .tar.gz和rm * .tar.gz创建的映像大180MB。

这意味着尽管ADD删除了tar文件,但仍保留在某个位置。并使其图像更大!


对于最新版本的Docker仍然如此吗?
纳文

3

由于Docker 17.05 在多阶段构建中COPY与该--from标志一起使用可将工件从先前的构建阶段复制到当前的构建阶段。

文档中

(可选)COPY接受一个标志--from=<name|index>,该标志可用于将源位置设置为将使用的先前构建阶段(使用FROM .. AS创建),而不是用户发送的构建上下文。


0
docker build -t {image name} -v {host directory}:{temp build directory} .

这是将文件复制到图像中的另一种方法。-v选项临时创建一个在构建过程中使用的卷。

这与其他卷不同,因为它仅装载用于构建的主机目录。可以使用标准cp命令复制文件。

同样,与curl和wget一样,它可以在命令堆栈中运行(在单个容器中运行),而不会乘以图像大小。ADD和COPY无法堆叠,因为它们在独立容器中运行,并且在其他容器中执行的那些文件上的后续命令将使图像大小成倍增加:

这样设置选项:

-v /opt/mysql-staging:/tvol

以下内容将在一个容器中执行:

RUN cp -r /tvol/mysql-5.7.15-linux-glibc2.5-x86_64 /u1 && \
    mv /u1/mysql-5.7.15-linux-glibc2.5-x86_64 /u1/mysql && \

    mkdir /u1/mysql/mysql-files && \
    mkdir /u1/mysql/innodb && \
    mkdir /u1/mysql/innodb/libdata && \
    mkdir /u1/mysql/innodb/innologs && \
    mkdir /u1/mysql/tmp && \

    chmod 750 /u1/mysql/mysql-files && \
    chown -R mysql /u1/mysql && \
    chgrp -R mysql /u1/mysql

1
您在哪个选项中看到的是哪个Docker版本?它没有记录,并且不能在我的1.12.1客户端上运行。
BMitch

2
实际上,此功能仍未包含在主版本中,并且对此主题仍有很多讨论,因此我们不希望在很长时间之前就使用此功能。有关更多信息,请参见错误报告:github.com/ docker / docker / issues / 14080
jwatkins '16

1
是的,没有这样的选项(已在最新版本17.06中选中)。这个答案是误导的。unknown shorthand flag: 'v' in -v
Kirby

确实有误导性评论
Guido van Steen

Docker卷在此处与答案无关,如果可以,请回答直接的问题:),这很容易被否决。
Majid Ali Khan
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.