Docker base image filesystem - docker

Docker can't modify base image's filesystem, but can't copy it. How can store its changes during container usage? I see that it stores files under /var/lib/docker, but how can store filesys' changes without modifying it? What is the methodology?

It does store changes through a new filesystem layer, because of its copy-on-write mechanism:
Those changes disappear after a docker rm (unless you docker commit right after a docker stop)
If you want some persistence, you would need to use a volume or use a data volume container.
When doing a docker run, you can mount a volume from your host or mount one from a data container.

Related

Sharing RO data between Docker containers without copying it?

I have one Docker image data-image which contains about 80 GB of data at /data. I would like a container from another image runner-image to have read-only access to this data. The data is never modified.
The recommended way to share data between containers is with a volume. However per the docs:
If you start a container which creates a new volume, and the container has files or directories in the directory to be mounted such as /app/, the directory’s contents are copied into the volume.
When I start a container which creates the volume:
docker run --volume data-volume:/data:ro data-image
the Docker daemon grinds to a halt because it is copying all of the data into the volume.
Once it eventually finishes, I can indeed see the data from the runner image:
docker run --volume data-volume:/data:ro runner-image ls /data
This shows the data as expected, but the initial copy is slow and expensive. How can I avoid that?
(data-image is the image holding the data, runner-image is the image which would like access to the data, data-volume is the name of the volume created here - but maybe volumes are not the best choice here.)
Is it possible to for one container to share read-only data to another, without first copying it? Thanks for any help!

Create Docker image which prevents volume mounting

We have a system in which the user can start sessions inside a number of docker containers. When they do this their home directory is automatically mounted to the docker container. We can't modify the system which starts the docker containers and mounts the directory.
Our goal is to have one image not automatically mount this container. Is there something that I can do to the image to basically make one directory unmountable?
No. If you can docker run a container, you can always use docker run -v to mount any host directory over any directory, and the original contents of the image will be hidden.
Docker's general model is that the image has somewhat limited powers, but you can specify most things when you start the container. Trying to prevent a volume mount (more frequently asked, trying to force a volume mount) is the opposite of this model; the image has no way to prevent how it will eventually be used.

How does volume mount from container to host and vice versa work?

docker run -ti --rm -v DataVolume3:/var ubuntu
Lets say I have a volume DataVolume 3 which pulls the contents of /var in the ubuntu container
even after killing this ubuntu container the volume remains and I can use this volume DataVolume3 to mount it to other containers.
This means with the deletion of container the volume mounts are not deleted.
How does this work ?
Does that volume mount mean that it copies the contents of /var into some local directory because this does not look like a symbolic link ?
If I have the container running and I create a file in the container then the same file gets copied to the host path ?
How does this whole process of volume mount from container to host and host to container work ?
Volumes are used for persistent storage and the volumes persists independent of the lifecycle of the container.
We can go through a demo to understand it clearly.
First, let's create a container using the named volumes approach as:
docker run -ti --rm -v DataVolume3:/var ubuntu
This will create a docker volume named DataVolume3 and it can be viewed in the output of docker volume ls:
docker volume ls
DRIVER VOLUME NAME
local DataVolume3
Docker stores the information about these named volumes in the directory /var/lib/docker/volumes/ (*):
ls /var/lib/docker/volumes/
1617af4bce3a647a0b93ed980d64d97746878564b141f30b6110d0818bf32b76 DataVolume3
Next, let's write some data from the ubuntu container at the mounted path var:
echo "hello" > var/file1
root#2b67a89a0050:/# cat /var/file1
hello
We can see this data with cat even after deleting the container:
cat /var/lib/docker/volumes/DataVolume3/_data/file1
hello
Note: Although, we are able to access the volumes like shown above but it not a recommended practice to access volumes data like this.
Now, next time when another container uses the same volume then the data from the volume gets mounted at the container directory specified as part of -v flag.
(*) The location may vary based on OS as pointed by David and probably can be seen by the docker volume inspect command.
Docker has a concept of a named volume. By default the storage for this lives somewhere on your host system and you can't directly access it from outside Docker (*). A named volume has its own lifecycle, it can be independently docker volume rm'd, and if you start another container mounting the same volume, it will have the same persistent content.
The docker run -v option takes some unit of storage, either a named volume or a specific host directory, and mounts it (as in the mount(8) command) in a specific place in the container filesystem. This will hide what was originally in the image and replace it with the volume content.
As you note, if the thing you mount is an empty named volume, it will get populated from the image content at container initialization time. There are some really important caveats on this functionality:
Named volume initialization happens only if the volume is totally empty.
The contents of the named volume never automatically update.
If the volume isn't empty, the volume contents completely replace what's in the image, even if it's changed.
The initialization happens only on native Docker, and not for example in Kubernetes.
The initialization happens only on named volumes, and not for bind-mounted host directories.
With all of these caveats, I'd avoid relying on this functionality.
If you need to mount a volume into a container, assume it will be empty when your entrypoint or the main container command starts. If you need a particular directory layout or file structure there, an entrypoint script can create it; if you're expecting it to hold particular data, keep a copy of it somewhere else in your image and copy it in if it's not already there (or, perhaps, always).
(*) On native Linux you can find a filesystem location for it, but accessing this isn't a best practice. On other OSes this will be hidden inside a virtual machine or other opaque storage. If you need to directly access the data (or inject config files, or read log files) a docker run -v /host/path:/container/path bind mount is a better choice.
Volumes are part of neither the container nor the host. Well, technically everything resides in the host machine. But the docker directories are only accessible by users in "docker" group. The files in these directories are separately managed by docker.
"Volumes are stored in a part of the host filesystem which is managed by Docker (/var/lib/docker/volumes/ on Linux)."
Hence volumes are like the union of files under the docker container and the host itself. Any addition on either end will be added to the volume(/var/lib/docker/volumes), not hard copy, rather something like symbol link
As volumes can be shared across different containers, deleting a container does not cascade to the volumes associated with it.
To remove unused volumes:
docker volume prune .

Docker temporary files strategy

My docker produces some temporary files.
Is there an encouraged strategy regarding those?
If I put those to /tmp, I'm not sure they'll get cleared. (Edit note: the link is dead. The question was, "Are default cronjobs executed in a docker container?")
Or should I expose the volume /tmp from the host machine?
I am not aware of any encouraged way to manage temporary files with Docker as it will mostly depend on how you need to handle these temporary files with your application (should they be deleted on restart? Periodically?...)
You have several possibilities depending on your needs:
Use Docker tmpfs mount
You can mount a tmpfs volume which will persist data as long as the container is running (i.e. the data in the volume will be deleted when the container stops), for example:
docker run --mount type=tmpfs,destination=/myapp/tmpdir someimage
This may be useful if you (can) restart your containers regularly and the temporary data may be recreated on container restart. However if you need to be able to clean up temporary data while the container is running, this is not a good solution as you will need to stop the container to have your temporary data cleaned.
Edit: as per #alexander-azarov coment, the tmpfs volume size is unlimited by default with the risk of the container using up all the machine memory. Using tmpfs-size flag is recommended to mitigate that risk, such as docker run --mount type=tmpfs,destination=/app,tmpfs-size=4096
Writing into the container writable layer
The writable layer of the container is where all the data will be written in the container if no volume is mounted. It will persist on container restart, but will be deleted if the container is deleted.
This way the temporary data will be deleted only when the container is deleted. It may be a good solution for short-lived containers, but not for long-lived containers.
Mounting host machine /tmp in the container with a bind mount
For example:
docker run -v /tmp/myapp-tmp-dir:/myapp/tmpdir someimage
This will cause all data to be written in the host machine /tmp/myapp-tmp-dir directory, and result will depend on how the host machine manage /tmp (in most cases, data are cleared upon machine restart)
Create and mount a volume to manage data into
You can create a volume which will contain your data, for example:
docker run --mount source=myappvol,target=/myapp/tmpdir someimage
And manage the data in the volume: mount-it in another container and cleanup the data, deleting the volume, etc.
These are the most common solutions relying (almost) solely on Docker functionalities. Another possibility would be to handle temporary files directly from your software or app running in the container, but it's more an application-related issue than a Docker-related one.

How can I have shared assets (pictures, text documents, etc) between my Docker container and host system?

I have a Docker container and I am trying to make it so that all of the files in /var/www/ on the container will be saved on the host system at a location (/home/me), and vise-versa. Is it possible to have this shared space between the two?
Would you accomplish this with mount points, or is there a better method?
Thanks
You can use volumes for sharing between container and host.
docker run -v /home/me:/var/www <image>
If you have a fixed files/data, you can add to the image using dockerfile or committing after copying into container. If you want to share rw dir between host and container, you need to use the volumes. Your data will also be persisted even if you remove and recreate a new container.
There are three ways that you can do this
Use volumes. Official docs
Burn the files in your image. Basically include the creation of the files inside the Dockerfile. This means every container container from that image will have an initial state of sorts.
Use data-only containers. These are containers without a running process that contain the data that you need. This also uses volumes. But instead of mounting to the host, your containers mount on the data-only container (which in turn mounts on the host if you want to). This answer will be useful

Resources