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.
WORKDIR /go/src/github.com/sparkdevo/href-counter/
RUN go get -d -v golang.org/x/net/html
COPY app.go .
RUN CGO_ENABLED= 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 命令的疑惑。

参考:
Docker COPY: Dockerfile best practices
Best practices for writing Dockerfiles
Dockerfile COPY
Dockerfile ADD

Dockerfile 中的 COPY 与 ADD 命令的更多相关文章

  1. 【Docker】涨姿势,深入了解Dockerfile 中的 COPY 与 ADD 命令

    参考资料:https://www.cnblogs.com/sparkdev/p/9573248.html Dockerfile 中提供了两个非常相似的命令 COPY 和 ADD,本文尝试解释这两个命令 ...

  2. docker(11)Dockerfile 中的COPY与ADD 命令

    前言 Dockerfile 中提供了两个非常相似的命令 COPY 和 ADD,本文尝试解释这两个命令的基本功能,以及其异同点,然后总结其各自适合的应用场景. Build 上下文的概念 在使用 dock ...

  3. 畸形的 dockerfile中的COPY命令-

    dockerfile中的COPY是指COPY 指定目录的“子级目录”下所有的目录和文件,到指定目录中,这个shell中的cp命令大相径庭,使得很多人纳闷,怎么cpy过去的文件不是自己想要的

  4. Dockerfile中COPY命令的简单性

    dockerfile中的COPY命令是不会拷贝目录结构的,它只会单纯把包含的所有文件拷贝到另一个目录中去. 相关链接:https://www.cnblogs.com/sparkdev/p/957324 ...

  5. Docker中的Dockerfile命令详解FROM RUN COPY ADD ENTRYPOINT...

    Dockerfile指令 这些建议旨在帮助您创建高效且可维护的Dockerfile. FROM FROM指令的Dockerfile引用 尽可能使用当前的官方图像作为图像的基础.我们推荐Alpine图像 ...

  6. Dockerfile 中对常用命令详解

    说明 Dockerfile 是一个用来构建镜像的文本文件,文本内容包含了一条条构建镜像所需的指令和说明. 在Dockerfile 中命令书写对先后顺序及表示其执行对顺序,在书写时需注意. 约定 命令不 ...

  7. Docker Dockerfile COPY vs ADD

    http://blog.163.com/digoal@126/blog/static/163877040201410341236664/   在Dockerfile中, 我们可以使用ADD和COPY拷 ...

  8. asp.net core中DockerFile文件中的COPY

    今天在ubuntu系统中使用docker部署asp.net core时遇到了一个问题,docker build 的时候总会在最后一步提示 lstat obj/Docker/publish: no su ...

  9. Images之Dockerfile中的命令2

    COPY COPY has two forms: COPY [--chown=<user>:<group>] <src>... <dest> COPY ...

随机推荐

  1. 如何用python下载一张图片

    如何用python下载一张图片 这里要用到的主要工具是requests这个工具,需要先安装这个库才能使用,该库衍生自urllib这个库,但是要比它更好用.多数人在做爬虫的时候选择它,是个不错的选择. ...

  2. 微信跳转,网页跳转微信app跳转公众号关注页面[转载]

    [微信跳转链接]之跳转公众号关注页面如何做到在微信内部在这里插入代码片浏览器打开的webview页面中,跳转到微信公众号的关注页面呢!我们可以通过访问微信提供的URL协议(weixin://)来实现这 ...

  3. RabbitMQ 官方demo1

    public class RabbitMqSend { public static void Test() { var factory = new ConnectionFactory() { Host ...

  4. windows服务定时任务

    其实定时任务时不时会碰到,只不过解决方案也不是只有一个,网上也有很多文章,但是没有一篇说得很清楚,尤其是安装环节,今天就着重说一下安装, 其他步骤带过,C#开发windows服务,开发,安装,调试 1 ...

  5. Prometheus 自定义exporter 监控key

    当Prometheus的node_exporter中没有我们需要的一些监控项时,就可以如zabbix一样定制一些key,让其支持我们所需要的监控项. 例如,我要根据 逻辑cpu核数 来确定load的告 ...

  6. AI-逻辑回归函数、激活函数、损失函数

    最近开始学习人工智能,先从基本的概念学起 逻辑回归函数(预测函数):z = dot(w,x) + b 解释:假设有三个特征,即x可以表示为(x1,x2,x3),w表示权重,对应每个特征的重要程度,b表 ...

  7. [Swift]LeetCode201. 数字范围按位与 | Bitwise AND of Numbers Range

    Given a range [m, n] where 0 <= m <= n <= 2147483647, return the bitwise AND of all numbers ...

  8. [Swift]LeetCode657. 机器人能否返回原点 | Robot Return to Origin

    There is a robot starting at position (0, 0), the origin, on a 2D plane. Given a sequence of its mov ...

  9. Zabbix4.0.3解决中文乱码

    字体下载地址:https://github.com/hejianlai/Zabbix/raw/master/font/msyh.ttf放到以下目录,可能你的路径和我的不一样用find查出来cd /us ...

  10. Nginx学习系列二Linux下Nginx实现负载均衡

    关于在本地虚拟机(VMware 14)下安装Linux同时安装Nginx,请参考Nginx学习系列之搭建环境 1.启动Nginx 在Nginx安装成功的前提下,启动Nginx 已root模式登陆(权限 ...