Tombart
Tombart

Reputation: 32378

Docker COPY behaving inconsistently

I would like to copy whitelisted folders from filesystem into target container.

Basically I'd like to perform "complex" operation cp -r app vendor target, where target is a Docker container.

I have simple Docker file:

FROM alpine

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

For reasons unknown the first command doesn't copy the app directory itself. It seems that Docker doesn't distinguish the difference between app and app/ which is quite irritating.

For testing purposes I've created following structure:

.
├── app
│   ├── foo
│   └── second
│       └── barf
├── Dockerfile
└── vendor
    └── bar

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

Result? Directory structure is not kept and parent directories not copied.

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

docker info:

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

Can anyone explain me why Docker's COPY . /target and COPY app/* /target behaves differently? What's the motivation? Why Docker doesn't used standard semantic of UNIX cp command?

Upvotes: 8

Views: 6721

Answers (2)

Tombart
Tombart

Reputation: 32378

This seems to be a known issue in Docker which won't get fixed any time soon.

There are several workaround how to specify multiple folder and copy folder itself and desired content. Note that you can't use asterisk (e.g. app/* which would cause flattening directory structure.

Multiple COPY commands

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

In some cases this produce quite a long list and each command creates a separate layer in the image tree.

Using tar

Using tar and Makefile has an advantage that it behaves like a proper UNIX program (you can define exclude patterns, slashes, * etc.).

Makefile:

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

then in Dockerfile you have to use ADD command in order to extract files into desired structure:

ADD files.tar /www 

Using .dockerignore

In Dockerfile simply copy everything

COPY . /www

and in .dockeringnore list all patterns that should not be copied:

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

note that once you're ignoring parent folder, there's no way how to manually copy single file from a folder that's already ignored (related Docker issue):

COPY doc/README /www

just won't be executed.

Upvotes: 2

Krzysztof Miksa
Krzysztof Miksa

Reputation: 1539

Generally, COPY instruction copy files and/or directories from "/app" and adds them to the container at path "/www/". If you want to have a "app" directory inside of "/www/" then your COPY instruction should looks like:

COPY /app /www/app/


You can read more about COPY instruction in documentation. Here I paste explanation of this behaviour from it:

If is a directory, the entire contents of the directory are copied, including filesystem metadata. Note:The directory itself is not copied, just its contents.



Regarding to your update:
COPY . /target and COPY app /target behaves the same. It takes entire contents from source directory ( from app or from . directory ) and copy it to the /target directory.


You can see slightly different behaviour when you use wildcards, eg. COPY app/* /www/. It copy all files from app/ to /www/ but then it treat every single directory from app/ like a source and copy its contents to /www/.

Why in Docker COPY is not implemented in the same way like it is in UNIX's cp command? I don't know, but if you want you can create pull request with own implementation :)

Upvotes: 7

Related Questions