adagio
adagio

Reputation: 23

Docker volume mount 0 size files

I am running a Docker container with a volume mounted to /etc.

I create the container using the following command:

$ docker container run -dt -v volume01:/etc alpine sh
81cf19f2c9ea23d79b900fe8f195fd333df376627710aa78f38f35cf565bfaf0

Now I see that some files in the volume directory have 0 size, e.g. the /etc/hosts file:

# ls -ltrh /var/lib/docker/volumes/volume01/_data/hosts
-rwxr-xr-x 1 root root 0 26 nov 10:42 /var/lib/docker/volumes/volume01/_data/hosts

This file is actually not empty, and if I check the size of /etc/hosts in the container, it is not 0:

$ docker exec -ti 81cf19f2c9ea ls -lt /etc/hosts
-rw-r--r--    1 root     root           174 Nov 26 09:42 /etc/hosts

Any idea why that happens?

Thanks in advance!

Upvotes: 2

Views: 1809

Answers (1)

Danila Kiver
Danila Kiver

Reputation: 3758

What you observe is a very interesting effect. To explain it, let's go through some facts.

First: few files (/etc/hosts, /etc/hostname and /etc/resolv.conf) are treated by Docker in a very special way, as they contain networking-related settings which should be different for different containers.

Docker generates these files for every container and keeps them on the host system. The container's file system has bind mounts of these files:

$ docker run alpine /bin/sh -c 'mount | grep etc'
/dev/sda1 on /etc/resolv.conf type ext4 (rw,relatime)
/dev/sda1 on /etc/hostname type ext4 (rw,relatime)
/dev/sda1 on /etc/hosts type ext4 (rw,relatime)

Second: before performing the bind mounts, Docker has to ensure that their targets exist (as the kernel requires the mountpoints to exist at the time of mounting). This kind of stuff gets configured in so-called init layer — a read-only layer which is mounted atop the image file system and contains the required files. In fact, Docker just removes the original version of the files (if any) and creates empty stubs at their locations.

To check this, try running a privileged container and umount(8) any of these special files:

$ docker run -it --privileged alpine

/ # cd /etc

/etc # ls -li hosts
3156636 -rw-r--r--    1 root     root           174 Nov 27 09:37 hosts

/etc # umount hosts

/etc # ls -li hosts
3156653 -rwxr-xr-x    1 root     root             0 Nov 27 09:37 hosts

Third: the alpine image itself has these files, but their content is a neutral default:

$ docker save alpine | tar xO 51667c1dd92cb4d851a7d6413545f5883b758f87251387e70ba531a4562e389f/layer.tar | tar xO etc/hosts
127.0.0.1 localhost localhost.localdomain
::1   localhost localhost.localdomain

So, the image file system contains one (default) version of these files, the topmost RO layer of the container file system (the image file system + init layer) has the second (empty) version of these files, and the file system of the running container (the image file system + init layer + RW layer + all mounts) has the third version of these files.

Now, what all this stuff has to do with the volume?

When you create and use a non-bind volume, Docker populates the volume's _data directory with the files which reside at the volume's mountpoint in the container, i.e. in your example, when you run

docker container run -dt -v volume01:/etc alpine sh

Docker copies container's /etc to /var/lib/docker/volumes/volume01/_data.

The question is: which version of the special files discussed above gets copied? The answer is: the stubs from the init layer, as the copy is performed after the container creation from its topmost layer, not in the runtime (when the "real" versions of these files are mounted).

Thus, you observe zero-sized /etc/hosts, /etc/hostname and /etc/resolv.conf in the volume, though in the runtime they are hidden by bind mounts of the Docker-managed versions of these files.

Upvotes: 4

Related Questions