简要说明在使用Dockerfile构建镜像时,ADDCOPY指令的使用差异。

近期在使用Docker时,发现部分项目中采用Dockerfile来构建自定义镜像时存在COPYADD两条指令混用的情况,为了规范使用,基于网络上的相关资料,简要的对比总结了它们的差异,供后续参考。

对比

Docker官网关于COPY指令的描述如下

The COPY instruction copies new files or directories from <src> and adds them to the filesystem of the container at the path <dest>.

关于ADD描述如下

The ADD instruction copies new files, directories or remote file URLs from <src> and adds them to the filesystem of the image at the path <dest>.

从它们的描述可知,这两条指令的作用基本上类似,都是将特定文件或文件夹拷贝到镜像中,不同的是ADD指令还能从远程URL中拷贝文件并添加到镜像中。

继续查看对应的文档,在ADD的指令描述下有如下说明

If <src> is a local tar archive in a recognized compression format (identity, gzip, bzip2 or xz) then it’s unpacked as a directory. Resources from remote URLs aren’t decompressed. When a directory is copied or unpacked, it has the same behavior as tar -x.

从中可知若要拷贝的本地文件属于tar系列的压缩文件,则会自动解压为对应的文件夹或文件,这是其相对于COPY的另一个显著的不同点。

关于它们更直观的对比说明,可在源文件中查看相应的备注:

1
2
3
4
5
6
// COPY foo /path
//
// Same as 'ADD' but without the tar and remote url handling.
func dispatchCopy(d dispatchRequest, c *instructions.CopyCommand) error {
    // xxx
}

总结下来就是 COPY指令处理不能进行压缩文件和URL文件流的处理之外,其它功能与ADD指令类似。

验证

为了便于观察Dockerfile构建过程中的输出,可在构建指令中加入--progress=plain来查看输出的详细信息,类似如下

1
docker build --progress=plain --no-cache -t custom:v1.0 -f Dockerfile .

ADD指令

  • 验证普通的文件拷贝

    Dockerfile文件如下

    1
    2
    3
    4
    5
    
    FROM ubuntu:18.04
    RUN mkdir /home/lucumt
    WORKDIR /home/lucumt
    ADD test.sh /home/lucumt
    RUN ls /home/lucumt
    

    输出结果如下

    ADD指令使用普通文件

  • 验证URL文件流下载

    Dockerfile文件如下1

    1
    2
    3
    4
    5
    
    FROM ubuntu:18.04
    RUN mkdir /home/lucumt
    WORKDIR /home/lucumt
    ADD https://nginx.org/download/nginx-1.0.15.tar.gz /home/lucumt
    RUN ls /home/lucumt
    

    输出结果如下,从中可以看出对于URL类型的文件,在Dockerfile的构建过程中只会拷贝,不会解压缩

    ADD指令使用URL文件

  • 验证tar文件解压

    Dockerfile文件如下

    1
    2
    3
    4
    5
    
    FROM ubuntu:18.04
    RUN mkdir /home/lucumt
    WORKDIR /home/lucumt
    ADD nginx-1.0.15.tar.gz /home/lucumt
    RUN ls /home/lucumt
    

    输出结果如下,可以看出对于从本地拷贝的tar类型文件,Dockerfile构建过程中会默认进行解压缩

    ADD指令使用tar文件

COPY指令

  • 验证普通文件

    Dockerfile文件内容如下

    1
    2
    3
    4
    5
    
    FROM ubuntu:18.04
    RUN mkdir /home/lucumt
    WORKDIR /home/lucumt
    COPY test.sh /home/lucumt
    RUN ls /home/lucumt
    

    输出结果如下

    COPY指令使用普通文件

  • 验证URL文件流下载

    Dockerfile文件内容如下

    1
    2
    3
    4
    5
    
    FROM ubuntu:18.04
    RUN mkdir /home/lucumt
    WORKDIR /home/lucumt
    COPY https://nginx.org/download/nginx-1.0.15.tar.gz /home/lucumt
    RUN ls /home/lucumt
    

    输出结果如下,可看出由于COPY指令不支持URL格式,构建过程会出错

    COPY指令使用URL文件

  • 验证tar文件解压

    Dockerfile文件内容如下

    1
    2
    3
    4
    5
    
    FROM ubuntu:18.04
    RUN mkdir /home/lucumt
    WORKDIR /home/lucumt
    COPY nginx-1.0.15.tar.gz /home/lucumt
    RUN ls /home/lucumt
    

    输出结果如下,可看出此时对于本地压缩文件不会进行解压。

    COPY指令使用压缩文件

总结

官方推荐的是在不需要使用ADD指令的高级特性的场景下,优先使用COPY指令,其更直观也不会造成困惑

假设对于一个新手来说,采用类似ADD nginx-1.0.15.tar.gz /home/lucumt进行构建后最后却得到的是一个解压文件,在不熟悉相关指令细节时会让人觉得很奇怪。

参考链接:

  1. ADD or COPY
  2. Docker ADD vs. COPY: What are the Differences?
  3. What is the difference between the COPY and ADD commands in a Dockerfile?

  1. 1.6.0 版本开始,ADD指令支持通--checksum属性对远程URL文件在下载时进行校验来确保文件准确 ↩︎