Dockerfile 中的 COPY 与 ADD 命令

本文详细解析了Dockerfile中的COPY和ADD命令,对比了它们的功能、应用场景及区别,包括如何利用COPY命令在多阶段构建中拷贝文件,以及ADD命令的额外功能如解压压缩文件和从URL拷贝文件。

Dockerfile 中提供了两个非常相似的命令 COPY 和 ADD,本文尝试解释这两个命令的基本功能,以及其异同点,然后总结其各自适合的应用场景。

Build 上下文的概念

在使用 docker build 命令通过 Dockerfile 创建镜像时,会产生一个 build 上下文(context)。所谓的 build 上下文就是 docker build 命令的 PATH 或 URL 指定的路径中的文件的集合。在镜像 build 过程中可以引用上下文中的任何文件,比如我们要介绍的 COPY 和 ADD 命令,就可以引用上下文中的文件。

默认情况下 docker build -t testx . 命令中的 . 表示 build 上下文为当前目录。当然我们可以指定一个目录作为上下文,比如下面的命令:

$ docker build -t testx /home/nick/hc

我们指定 /home/nick/hc 目录为 build 上下文,默认情况下 docker 会使用在上下文的根目录下找到的 Dockerfile 文件。

COPY 和 ADD 命令不能拷贝上下文之外的本地文件
对于 COPY 和 ADD 命令来说,如果要把本地的文件拷贝到镜像中,那么本地的文件必须是在上下文目录中的文件。其实这一点很好解释,因为在执行 build 命令时,docker 客户端会把上下文中的所有文件发送给 docker daemon。考虑 docker 客户端和 docker daemon 不在同一台机器上的情况,build 命令只能从上下文中获取文件。如果我们在 Dockerfile 的 COPY 和 ADD 命令中引用了上下文中没有的文件,就会收到类似下面的错误:

与 WORKDIR 协同工作

WORKDIR 命令为后续的 RUN、CMD、COPY、ADD 等命令配置工作目录。在设置了 WORKDIR 命令后,接下来的 COPY 和 ADD 命令中的相对路径就是相对于 WORKDIR 指定的路径。比如我们在 Dockerfile 中添加下面的命令:

WORKDIR /app
COPY checkredis.py .

然后构建名称为 testx 的容器镜像,并运行一个容器查看文件路径:

checkredis.py 文件就是被复制到了 WORKDIR /app 目录下。

COPY 命令的简单性

如果仅仅是把本地的文件拷贝到容器镜像中,COPY 命令是最合适不过的。其命令的格式为:
COPY <src> <dest>

除了指定完整的文件名外,COPY 命令还支持 Go 风格的通配符,比如:

COPY check* /testdir/           # 拷贝所有 check 开头的文件
COPY check?.log /testdir/       # ? 是单个字符的占位符,比如匹配文件 check1.log

对于目录而言,COPY 和 ADD 命令具有相同的特点:只复制目录中的内容而不包含目录自身。比如我们在 Dockerfile 中添加下面的命令:

WORKDIR /app
COPY nickdir .

其中 nickdir 目录的结构如下:

重新构建镜像 testx,运行一个容器并查看 /app 目录下的内容:

这里只有 file1 和 file2,少了一层目录 nickdir。如果想让 file1 和 file2 还保存在 nickdir 目录中,需要在目标路径中指定这个目录的名称,比如:

WORKDIR /app
COPY nickdir ./nickdir

COPY 命令区别于 ADD 命令的一个用法是在 multistage 场景下。关于 multistage 的介绍和用法请参考笔者的《Dockerfile 中的 multi-stage》一文。在 multistage 的用法中,可以使用 COPY 命令把前一阶段构建的产物拷贝到另一个镜像中,比如:

复制代码
FROM golang:1.7.3
WORKDIR /go/src/github.com/sparkdevo/href-counter/
RUN go get -d -v golang.org/x/net/html
COPY app.go    .
RUN CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -o app .

FROM alpine:latest
RUN apk --no-cache add ca-certificates
WORKDIR /root/
COPY --from=0 /go/src/github.com/sparkdevo/href-counter/app .
CMD ["./app"]
复制代码

这段代码引用自《Dockerfile 中的 multi-stage》一文,其中的 COPY 命令通过指定 --from=0 参数,把前一阶段构建的产物拷贝到了当前的镜像中。

ADD 命令还可以干其它事情

ADD 命令的格式和 COPY 命令相同,也是:
ADD <src> <dest>

除了不能用在 multistage 的场景下,ADD 命令可以完成 COPY 命令的所有功能,并且还可以完成两类超酷的功能:

  • 解压压缩文件并把它们添加到镜像中
  • 从 url 拷贝文件到镜像中

当然,这些功能也让 ADD 命令用起来复杂一些,不如 COPY 命令那么直观。

解压压缩文件并把它们添加到镜像中
如果我们有一个压缩文件包,并且需要把这个压缩包中的文件添加到镜像中。需不需要先解开压缩包然后执行 COPY 命令呢?当然不需要!我们可以通过 ADD 命令一次搞定:

WORKDIR /app
ADD nickdir.tar.gz .

这应该是 ADD 命令的最佳使用场景了!

从 url 拷贝文件到镜像中
这是一个更加酷炫的用法!但是在 docker 官方文档的最佳实践中却强烈建议不要这么用!!docker 官方建议我们当需要从远程复制文件时,最好使用 curl 或 wget 命令来代替 ADD 命令。原因是,当使用 ADD 命令时,会创建更多的镜像层,当然镜像的 size 也会更大(下面的两段代码来自 docker 官方文档):

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

如果使用下面的命令,不仅镜像的层数减少,而且镜像中也不包含 big.tar.xz 文件:

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

好吧,看起来只有在解压压缩文件并把它们添加到镜像中时才需要 ADD 命令!

加速镜像构建的技巧

在使用 COPY 和 ADD 命令时,我们可以通过一些技巧来加速镜像的 build 过程。比如把那些最不容易发生变化的文件的拷贝操作放在较低的镜像层中,这样在重新 build 镜像时就会使用前面 build 产生的缓存。比如笔者构建镜像时需要用到下面几个文件:

其中 myhc.py 文件不经常变化,而 checkmongo.py、checkmysql.py 和 checkredis.py 这三个文件则经常变化,那么我们可这样来设计 Dockerfile 文件:

WORKDIR /app
COPY myhc.py .
COPY check* ./

让 COPY myhc.py . 单独占据一个镜像层,当 build 过一次后,每次因 checkmongo.py、checkmysql.py 和 checkredis.py 这三个文件变化而导致的重新 build 都不会重新 build COPY myhc.py . 镜像层:

如上图所示,第二步和第三步都没有重新 build 镜像层,而是使用了之前的缓存,从第四步才开始重新 build 了镜像层。当文件 size 比较大且文件的数量又比较多,尤其是需要执行安装等操作时,这样的设计对于 build 速度的提升还是很明显的。所以我们应该尽量选择能够使用缓存的 Dockerfile 写法。

总结

当第一次看到 COPY 和 ADD 命令时不免让人感到疑惑。但分析之后大家会发现 COPY 命令是为最基本的用法设计的,概念清晰,操作简单。而 ADD 命令基本上是 COPY 命令的超集(除了 multistage 场景),可以实现一些方便、酷炫的拷贝操作。ADD 命令在增加了功能的同时也增加了使用它的复杂度,比如从 url 拷贝压缩文件时弊大于利。希望本文能够解去大家对 Dockerfile 中 COPY 和 ADD 命令的疑惑。

<think> 首先,用户的问题是:我想了解DockerFileCOPY命令ADD命令的区别。请问DockerFileCOPY命令ADD命令的区别。 根据系统级指令,我需要: 1. 所有行内数学表达式使用$...$格式。 2. 独立公式使用$$...$$并单独成段。 3. LaTeX语法正确。 4. 使用中文回答。 5. 生成相关问题。 6. 回答中引用的段落末尾自然地添加引用标识,例如[^1]。 用户提供了引用内容,我需要参考这些引用。引用包括: - 引用[1]: 介绍COPYADD命令的区别,强调COPY用于简单复制,ADD更灵活但复杂。 - 引用[2]: 讨论COPYADD的区别,提到COPY是基本用法。 - 引用[3]: 解释COPYADDADDCOPY的超集,但可能增加复杂度。 - 引用[4]: 区分COPYADDDockerfile中的作用。 - 引用[5]: 详细比较COPYADD的特性,包括支持解压和URL下载。 我的回答需要基于这些引用,并自然地添加引用标识。 回答结构应该清晰,帮助用户逐步解决问题。既然用户直接问区别,我可以先概述,然后详细解释。 大纲回复: 1. **引言**:简要介绍COPYADD命令的作用。 2. **主要区别**:列出关键差异点,如功能、使用场景等。 3. **详细解释**:基于引用,解释每个命令的具体行为和例子。 4. **推荐**:给出使用建议,如优先使用COPY。 5. **生成相关问题**:在末尾添加相关问题。 最后,添加相关问题部分。 确保使用中文。 现在,构建回答: - **开头**:直接回答问题。例如:在Dockerfile中,COPYADD命令都用于将文件从主机复制到容器镜像中,但它们之间存在一些关键区别。 - **区别点**: - COPY:简单复制文件和目录。 - ADD:除了复制,还支持自动解压压缩文件和从URL下载文件。 - 引用[5]提到:COPY不支持解压或URL,ADD支持。 - 引用[3]强调ADD可能增加不必要的复杂性。 - **例子**:可以给出简单的代码片段,如引用[5]中的例子。 - **建议**:优先使用COPY,除非需要ADD的额外功能。 - **引用**:在相关部分末尾添加引用,如[^1],[^5]等。 生成相关问题: 例如: 1. 在什么情况下应该使用ADD命令而不是COPY? 2. COPY命令有哪些最佳实践? 3. ADD命令如何处理远程文件? 确保回答真实可靠,基于提供的引用。 </think>### DockerfileCOPY命令ADD命令的区别 在Dockerfile中,`COPY`和`ADD`命令都用于将文件或目录从主机(构建上下文)复制到容器镜像中,但它们在功能、行为和使用场景上存在关键差异。以下我将基于专业资料逐步解释这些区别,帮助您清晰理解。核心区别在于:`COPY`是简单、直接的文件复制命令,而`ADD`提供了额外功能(如自动解压和URL下载),但也可能引入不必要的复杂性[^1][^3][^5]。 #### 1. **基本功能对比** - **`COPY`命令**:仅用于复制本地文件或目录到镜像中。它不支持解压压缩文件或从远程URL下载文件。语法简单:`COPY <源路径> <目标路径>`。例如,复制一个本地文件到镜像: ```dockerfile COPY ./app/file.txt /app/file.txt # 简单复制文件[^5] ``` 这里,`COPY`直接将`file.txt`复制到镜像的`/app`目录,不进行任何额外处理[^2][^4]。 - **`ADD`命令**:是`COPY`的超集,除了复制本地文件外,还支持自动解压`.tar`、`.tar.gz`等压缩文件,并可从URL下载文件直接复制到镜像。语法相同:`ADD <源路径> <目标路径>`。例如: ```dockerfile ADD ./data.tar.gz /app/data # 自动解压压缩文件到/app/data目录 ADD http://example.com/file.txt /app/ # 从URL下载并复制文件[^3][^5] ``` `ADD`在复制时如果源文件是压缩包,会自动解压到目标路径;如果源是URL,会下载文件并复制。但这可能带来副作用,如意外解压或不必要的网络依赖[^1][^3]。 #### 2. **关键区别细节** - **解压功能**: - `COPY`:不支持解压。如果复制压缩文件(如`.tar`),它会被直接复制为普通文件,不会自动解压。 - `ADD`:支持自动解压常见压缩格式(如`.tar`、`.tar.gz`、`.zip`)。这在需要快速部署打包数据时有用,但如果不小心使用,可能导致镜像中包含多余解压文件,增大镜像体积[^3][^5]。 - 示例:假设有`archive.tar`文件: ```dockerfile COPY archive.tar /app/ # 结果:/app/archive.tar(未解压) ADD archive.tar /app/ # 结果:/app/下包含解压后的文件[^5] ``` - **URL支持**: - `COPY`:仅支持本地路径(如`./file.txt`),不支持URL。尝试使用URL会导致构建错误。 - `ADD`:支持从HTTP/HTTPS URL下载文件并复制到镜像。例如,`ADD https://example.com/package.zip /app/`会下载并复制文件,如果文件是压缩包,还会自动解压。但这不是最佳实践,因为下载失败会使构建不可靠,且增加了安全风险[^3][^5]。 - **行为差异**: - `COPY`更可预测:它严格复制文件内容,不会改变文件格式,易于调试和维护。 - `ADD`更灵活但易出错:例如,如果源路径是URL且下载的文件是压缩包,`ADD`会同时下载和解压,这可能不是用户预期的行为。在多层构建(multistage builds)中,`ADD`的复杂行为可能导致镜像不一致[^1][^3][^4]。 - **性能安全性**: - `COPY`通常更快、更安全:因为它只处理本地文件,减少了网络或解压带来的延迟和漏洞。 - `ADD`可能影响构建效率:URL下载依赖网络稳定性,自动解压可能浪费存储空间。Docker社区推荐仅在必要时使用`ADD`[^3][^5]。 #### 3. **使用场景推荐** - **优先使用`COPY`**:在大多数情况下,`COPY`是首选,因为它简单、可靠,且符合Docker最佳实践。例如: - 复制源代码、配置文件或静态文件。 - 当您不需要额外功能时,避免`ADD`以减少错误[^3][^5]。 - **使用`ADD`的场景**:仅在需要特定功能时: - 自动解压压缩文件到镜像中(如部署预打包数据)。 - 从URL下载文件(但推荐在Dockerfile外下载后使用`COPY`,以提高可靠性)。 - 注意:在多层构建中,`ADD`的URL功能可能导致问题,建议用`RUN curl`替代[^1][^5]。 #### 4. **总结最佳实践** - **核心区别总结**: | 特性 | `COPY`命令 | `ADD`命令 | |--------------------|--------------------------------|--------------------------------| | 基本复制功能 | 支持(本地文件) | 支持(本地文件) | | 自动解压压缩文件 | 不支持 | 支持(如.tar, .tar.gz) | | 从URL下载文件 | 不支持 | 支持 | | 复杂度 | 低,易于维护 | 高,可能引入意外行为 | | 推荐使用场景 | 简单复制操作 | 需要解压或URL下载时 | - **最佳实践**: - 始终优先使用`COPY`,除非需要解压或URL功能[^3][^5]。 - 使用`ADD`时,明确检查源文件类型,避免意外解压。 - 对于URL下载,考虑在Dockerfile中使用`RUN`命令结合`curl`或`wget`,以获得更好控制(例如:`RUN curl -O http://example.com/file.txt && COPY file.txt /app/`)[^1][^4]。 通过合理选择命令,您可以构建高效、可靠的Docker镜像。更多细节可参考Docker官方文档和相关教程[^1][^3][^5]。
评论 2
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值