Docker COPY 行为不一致

7

我希望能够将白名单文件夹从文件系统复制到目标容器中。

基本上,我想执行一个“复杂”的操作cp -r app vendor target,其中target是一个Docker容器。

我有一个简单的Docker文件:

FROM alpine

COPY app/* /www/
COPY vendor /www/

由于原因未知,第一个命令并没有复制app目录本身。看起来Docker不能区分appapp/之间的区别,这非常恼人。
为了测试目的,我创建了以下结构:
.
├── app
│   ├── foo
│   └── second
│       └── barf
├── Dockerfile
└── vendor
    └── bar

docker build -t test . && docker run -it test ash:

执行结果?目录结构未得到保留,父级目录未被复制。

/www/
├── bar
├── barf
└── foo

docker info:

Server Version: 1.12.1
Storage Driver: overlay2
 Backing Filesystem: extfs

请问为什么Docker的COPY . /targetCOPY app/* /target会有不同的行为?这是出于什么动机呢?为什么Docker没有使用UNIX cp命令的标准语义呢?

2个回答

6

通常,COPY指令将文件和/或目录从“/app”复制并添加到路径“/www/”的容器中。如果您想在“/www/”内部有一个“app”目录,则COPY指令应如下所示:

COPY /app /www/app/


您可以在文档中了解有关COPY指令的更多信息。这里我从中粘贴了对此行为的解释:

如果 是一个目录,则复制整个目录的内容,包括文件系统元数据。 注意:不会复制目录本身,只会复制其内容。



关于您的更新:
COPY . /targetCOPY app /target的行为相同。它将源目录(从app.目录)的整个内容复制到/target目录中。


当您使用通配符时(例如COPY app/* /www/),您会看到略微不同的行为。它将所有文件从app/复制到/www/,但然后将app/中的每个单独目录视为源并将其内容复制到/www/

为什么在Docker中COPY没有像UNIX的cp命令一样实现?我不知道,但如果您想要,可以创建拉取请求并使用自己的实现 :)


你能列出本地机器上从"./app"目录开始的所有目录吗?我非常确定你在"./app"目录下有"vendor"目录。 - Krzysztof Miksa
这正是我最初的想法。不,它不在那里。如果容器中的目标/app/vendor已经存在,Docker是否会表现出不同的行为呢? - Tombart
我不这么认为。我刚刚测试了一下,创建了Dockerfile和非常相似的结构,它的行为正如我所想的那样。即使在COPY指令之前创建了目录/www/vendor,它也没有被创建,甚至是空的。也许你有其他的进程/指令会创建vendor目录? - Krzysztof Miksa
是的,你说得对,它是由另一个命令创建的。我已经更新了问题,并提供了完整的示例,以便更容易地重现。 - Tombart
1
如果您想保留目录结构,则应使用命令 COPY /app /www/。当您使用通配符时,每个匹配的模式都会解析为源,然后如果它是一个目录,则其整个内容将被复制到目标目录。请记住文档中的注释:只复制目录本身的内容,而不是目录本身。但总的来说,我同意您的观点,这可能会让新用户感到困惑。 - Krzysztof Miksa
显示剩余2条评论

2
这似乎是Docker中的一个已知问题,而且不会很快得到解决。有几种解决方法可以指定多个文件夹并复制文件夹本身和所需内容。请注意,您不能使用星号(例如app/*),因为这会使目录结构变平。 多个COPY命令
RUN mkdir -p app bin config db lib public
COPY app/ app
COPY bin/ bin
COPY config/ config
COPY db/ db
COPY lib/ lib
COPY public/ public

在某些情况下,这会产生一个相当长的列表,并且每个命令都会在图像树中创建一个单独的层。

使用tar

使用tarMakefile的优点是它表现得像一个合适的UNIX程序(您可以定义排除模式、斜杠、*等)。

Makefile

all: 
  tar -cf files.tar app bin config db lib public
  docker build -t image-name .
  rm files.tar

然后在Dockerfile中,您需要使用ADD命令以便将文件提取到所需的结构中:

ADD files.tar /www 

使用 .dockerignore 文件

Dockerfile 中,只需复制所有内容即可。

COPY . /www

.dockeringnore中列出不应该被复制的所有模式:

.git
test/
doc/
log/
tmp/
Dockerfile
Makefile

请注意,一旦您忽略了父文件夹,就没有办法手动从已经被忽略的文件夹中复制单个文件(相关Docker问题)。请保留HTML标签。
COPY doc/README /www

仅仅不会被执行。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接