Docker mount a volume and populate it from container - docker

I'd like to mount a volume from a windows host to a linux container and have the content of the target folder in the linux container populate the folder in the windows host.
Example:
- host folder: c:\Users\abc\myfolder
- container folder: /data/mydata
The container is built from an image that creates data inside /data/mydata
If I do docker run -v c:\Users\abc\myfolder:/data/mydata image, then c:\Users\abc\myfolder content will override whatever was on /data/mydata inside the container. I would like to achieve the opposite (put the content of /data/mydata from the container in c:\Users\abc\myfolder)
Creating a shared folder and then logging inside the container and copying the content of /data/mydata to the shared folder would expose the content of /data/mydata to the windows host, but it involves a manual copy and is not very efficient.
Thank you.

There is a feature to control read and write permissions
You can specify that a volume should be read-only by appending :ro to the -v switch:
docker run -v /path/in/host:/path/in/container:ro my_image_name
But just works in container folder and by default (with any chance to modify) is read-write on the host
Sync
Maybe you could:
create a folder called /folders/left (c:\Users\abc\myfolder in your case)
create a folder called /folders/right
create a container to populate the /folders/right
docker run -v /folders/right:/path/in/container my_image_name
ensure /folders/right is empty before container startup in order to not override the internal container folder
with this you will have /folders/left (host folder) and /folders/right (changes made by container)
finally try to sync left to right or another configurations with some tool
linux https://unix.stackexchange.com/a/203854/188975

Related

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 .

Difference in Volumes in docker run and COPY in dockerfile

If I do something like
docker run -v /opt/datadir:/var/lib/mysql image
I am mapping some location inside the container to a location in the host.
How is this different to the command COPY used when writing a Dockerfile?
The major difference is seen in case we edit any of the file present inside that location.
Suppose the directory /opt/datadir contains a file temp.txt
In case of bind mount, if you try to edit the file temp.txt from the host machine, the changes will be reflected inside the container and vice-versa.
When we create COPY command in Dockerfile, it copies the content to the filesystem of the container. Hence any changes done inside the container are DOES NOT affect the files present on the host machine.
In this case, if you want changes done on the host machine to be reflected inside the container, then you need to build a docker image and run a new container using the updated image.
When to use what?
For scenarios where the resource needs frequent updates, use bind mounts.
Eg: We want to provide our web server a configuration file that might change frequently.
In case the resource is independent of host filesystem, use COPY command inside dockerfile.
Eg: .tar, .zip, .war files or any file that requires no or very few updates inside the container.

-v deleted all the data from the docker container

I made a docker image called myImage, there is a folder: /data I want to let the user edit it by themselves. I read that -v flag can mount the volume, so I used it like following:
I run the container with this command:
docker run -v /my_local_path:/data -it myImage /bin/bash
But surprisingly, docker cleared all the files in /data in the container. But this is not I want... I want actually the host can get all the files from /data... :(
How can I do that?
When you share a volume like this, the volume on the host overwrites the volume in the container, so the files in the container's folder will be removed.
What you need to do is put the files in the container in folder A (a folder in the container). Mount folder B (another folder in the container). Then AFTER the volume is mounted, move the files from folder A to folder B. Then these files will be both available to the host and inside the container.
You can achieve this 'move files' operation using a RUN or an ENTRYPOINT script in your Dockerfile.
See Run a script in Dockerfile
Sorry, I forget if you need RUN or ENTRYPOINT (or if either will work) but one of these will definitely do it.
I think you want ENTRYPOINT because an ENTRYPOINT script runs AFTER the container is created. Thus it will run after the volume is mounted.

Make directory available locally in Docker

I have a directory in my Docker container, and I'm trying to make it available locally using -v screenshots:/srv/screenshots in my docker run command but it's not available.
Do I need to add something else to my command?
Host volumes are mapped from the host into the container, not the other way around. This is one way to have persistent storage (so the data don't disappear when the container is re-created).
You can copy the screenshot folder to your host with docker cp and map them in.
You will have your screenshots in the local screenshots folder. Mapping them in with -v screenshots:/srv/screenshots makes them appear in /srv/screenshots in the container, but these files are really on the host.
See: Mount a host directory as data volume

Share folder from docker container to host

Is there a way to share folder from docker container to host?
For example I have tomcat inside docker container and I want it to be visible from the outside.
If I do
volumes:
- /opt/tomcat:/opt/tomcat
I receive an error in the container:
"No such file or directory /opt/tomcat/bin/catalina.sh"
I don't think Docker allows you to that. That command will mount your host folder in the container, so your files in the container are not visible anymore.
Two options:
You can access the container files using this trick (GitHub issue):
sudo ls /proc/$(docker inspect --format {{.State.Pid}} YOUR_CONTAINER_NAME)/root. To access them you will need root privileges, or you can use bindfs to match root user with your user name (see the same thread).
Create a new volume, copy the files you need to be accessible to there and mount it inside the container, in the right place

Resources