敏捷冲冲_

The Art of software design

Dockerfile COPY 指令详解

COPY 指令一共有两种使用格式,一种类似于 shell 命令行的使用方式,一种类似于数组参数的使用方式:

  • COPY [--chown=<user>:<group>] <源路径> … <目标路径>

  • COPY [--chown=<user>:<group>] [“<源路径1>”, …, “<目标路径>”]

COPY 指令将把构建上下文目录 <源路径> 中的文件或目录复制到镜像内 <目标路径> 的位置(新建一层)。

比如:

1
COPY php.ini /etc/php/8.1/cli/php.ini

<源路径> 可以是一个也可以是多个,甚至可以是通配符,但其通配符规则要满足 Go 的 filepath.Match 规则,

1
2
3
4
5
# 将构建上下文目录中 hom 开头的所有文件拷贝到容器中的 /mydir/ 目录中
COPY hom* /mydir/

# 将构建上下文目录中 homa.txt homb.txt ... 等文件拷贝到容器中的 /mydir/ 目录中
COPY hom?.txt /mydir/

目标路径 可以是容器内的绝对路径,也可以是容器内相对于 WORKDIR 的相对路径,而且目标路径不需要提前创建,如果目标路径不存在的话,Docker 会在复制文件前先行创建缺失的目录。

亿点点细节

首先,假设我们构建镜像的上下文中有如的下目录结构:

1
2
3
4
5
6
7
8
config
————php
————————php.ini
————nginx
————————default.conf
————mysql
————————my.ini
Dockerfile

1. 作用域问题

COPY 和 ADD 指令均不能拷贝构建上下文之外的本地文件,这一点很好解释,因为 docker 在执行 build 命令时,docker 客户端会把上下文中的所有文件发送给 docker daemon,而镜像构建操作是由 docker daemon 完成的。

2. 拷贝目录的问题

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

1
COPY config /home/

这表示把 config 目录下的所有文件都拷贝到容器中的 /home 目录中,这样我们在容器中看到的目录结构会是:

1
2
3
4
5
6
7
8
home
————php
————————php.ini
————nginx
————————default.conf
————mysql
————————my.ini
Dockerfile

确实 config 下的所有文件都没少,但 config 目录不见了,如果想让这些文件还保存在 config 目录中,可以使用如下两种方案解决:

1
2
3
4
5
# 在源路径的结尾使用 /,表示要拷贝的文件是目录本身
COPY config/ /home/

# 在目标路径中指定 config 目录名称,这样会自动创建 /home/config 目录
COPY config /home/config/

3. 乱用通配符的问题

有些新手,在想把 config 目录中的文件全部复制到 Docker 镜像中去的时候,会使用如下指令语句:

1
COPY config/* /home/

结果发现,config 目录下的所有文件都复制到容器中的 /home 目录下了,但丢失了 config 目录的所有层级结构:

1
2
3
4
root@9591016aff6c:/# ls -l /home
-rwxrwxrwx 1 root root 0 Jun 23 07:33 default.conf
-rwxrwxrwx 1 root root 0 Jun 23 07:26 my.ini
-rwxrwxrwx 1 root root 0 Jun 23 07:32 php.ini

这是因为 config/* 语法是匹配 config 目录下的所有文件,并把它们统一拷贝到容器中 /home 目录下,所以正确的语法应该是上面提到的两种方案:

1
2
3
COPY config/ /home/

COPY config /home/config/

文件权限

需要注意的是,使用 COPY 指令时,源文件的各种元数据都会被保留下来。比如读、写、执行权限、文件变更时间等。这个特性对于镜像定制很有用,特别是构建的相关文件都在使用 Git 进行管理的时候。

最后,在使用 COPY 指令的时候还可以加上 --chown=<user>:<group> 选项来改变文件的所属用户及所属组:

1
2
3
4
COPY --chown=55:mygroup files* /mydir/
COPY --chown=bin files* /mydir/
COPY --chown=1 files* /mydir/
COPY --chown=10:11 files* /mydir/