COPY 指令一共有两种使用格式,一种类似于 shell 命令行的使用方式,一种类似于数组参数的使用方式:
COPY [--chown=<user>:<group>] <源路径> … <目标路径>
COPY [--chown=<user>:<group>] [“<源路径1>”, …, “<目标路径>”]
比如:
1 | COPY php.ini /etc/php/8.1/cli/php.ini |
<源路径> 可以是一个也可以是多个,甚至可以是通配符,但其通配符规则要满足 Go 的 filepath.Match 规则,
1 | 将构建上下文目录中 hom 开头的所有文件拷贝到容器中的 /mydir/ 目录中 |
目标路径 可以是容器内的绝对路径,也可以是容器内相对于 WORKDIR 的相对路径,而且目标路径不需要提前创建,如果目标路径不存在的话,Docker 会在复制文件前先行创建缺失的目录。
亿点点细节
首先,假设我们构建镜像的上下文中有如的下目录结构:
1 | config |
1. 作用域问题
COPY 和 ADD 指令均不能拷贝构建上下文之外的本地文件,这一点很好解释,因为 docker 在执行 build 命令时,docker 客户端会把上下文中的所有文件发送给 docker daemon,而镜像构建操作是由 docker daemon 完成的。
2. 拷贝目录的问题
首先,对于目录而言,COPY 和 ADD 命令具有相同的特点:只复制目录中的内容而不包含目录自身。比如我们在 Dockerfile 中写如下指令:
1 | COPY config /home/ |
这表示把 config 目录下的所有文件都拷贝到容器中的 /home 目录中,这样我们在容器中看到的目录结构会是:
1 | home |
确实 config 下的所有文件都没少,但 config 目录不见了,如果想让这些文件还保存在 config 目录中,可以使用如下两种方案解决:
1 | 在源路径的结尾使用 /,表示要拷贝的文件是目录本身 |
3. 乱用通配符的问题
有些新手,在想把 config 目录中的文件全部复制到 Docker 镜像中去的时候,会使用如下指令语句:
1 | COPY config/* /home/ |
结果发现,config 目录下的所有文件都复制到容器中的 /home 目录下了,但丢失了 config 目录的所有层级结构:
1 | root@9591016aff6c:/# ls -l /home |
这是因为 config/* 语法是匹配 config 目录下的所有文件,并把它们统一拷贝到容器中 /home 目录下,所以正确的语法应该是上面提到的两种方案:
1 | COPY config/ /home/ |
文件权限
需要注意的是,使用 COPY 指令时,源文件的各种元数据都会被保留下来。比如读、写、执行权限、文件变更时间等。这个特性对于镜像定制很有用,特别是构建的相关文件都在使用 Git 进行管理的时候。
最后,在使用 COPY 指令的时候还可以加上 --chown=<user>:<group> 选项来改变文件的所属用户及所属组:
1 | COPY --chown=55:mygroup files* /mydir/ |