Sharing data between containers with volumes
Volumes are units of storage. They have a separate life cycle to containers, so they can be created independently and then mounted inside one or more containers. You can ensure containers are always created with volume storage using the VOLUME instruction in the Dockerfile.
You specify volumes with a target directory, which is the location inside the container where the volume is surfaced. When you run a container with a volume defined in the image, the volume is mapped to a physical location on the host, which is specific to that one container. More containers running from the same image will have their volumes mapped to a different host location.
In Windows, volume directories need to be empty. In your Dockerfile, you can't create files in a directory and then expose it as a volume. Volumes also need to be defined on a disk that exists in the image. In the Windows base images, there is only a C drive available, so volumes need to be created on the C drive.
The Dockerfile for dockeronwindows/ch02-volumes:2e creates an image with two volumes, and explicitly specifies the cmd shell as the ENTRYPOINT when containers are run from the image:
# escape=`
FROM mcr.microsoft.com/windows/nanoserver:1809
VOLUME C:\app\config
VOLUME C:\app\logs
USER ContainerAdministrator
ENTRYPOINT cmd /S /C
When I run a container from that image, Docker creates a virtual filesystem from three sources. The image layers are read-only, the container's layer is writeable, and the volumes can be set to read-only or writeable:
Because volumes are separate from the container, they can be shared with other containers even if the source container isn't running. I can run a task container from this image, with a command to create a new file in the volume:
docker container run --name source dockeronwindows/ch02-volumes:2e "echo 'start' > c:\app\logs\log-1.txt"
Docker starts the container, which writes the file, and then exits. The container and its volumes haven't been deleted, so I can connect to the volumes in another container using the --volumes-from option and by specifying my first container's name:
docker container run -it --volumes-from source dockeronwindows/ch02-volumes:2e cmd
This is an interactive container, and when I list the contents of the C:\app directory, I'll see the two directories, logs and config, which are volumes from the first container:
> ls C:\app
Directory: C:\app
Mode LastWriteTime Length Name
---- ------------- ------ ----
d----l 6/22/2017 8:11 AM config
d----l 6/22/2017 8:11 AM logs
The shared volume has read and write access, so I can see the file created in the first container and append to it:
C:\>type C:\app\logs\log-1.txt
'start'
C:\>echo 'more' >> C:\app\logs\log-1.txt
C:\>type C:\app\logs\log-1.txt
'start'
'more'
Sharing data between containers like this is very useful; you can run a task container that takes a backup of data or log files from a long-running background container. The default access is for volumes to be writeable, but that's something to be wary of, as you could edit data and break the application running in the source container.
Docker lets you mount volumes from another container in read-only mode instead, by adding the :ro flag to the name of the container in the --volumes-from option. This is a safer way to access data if you want to read it without making changes. I'll run a new container, sharing the same volumes from the original container in read-only mode:
> docker container run -it --volumes-from source:ro dockeronwindows/ch02-volumes:2e cmd
C:\>type C:\app\logs\log-1.txt
'start'
'more'
C:\>echo 'more' >> C:\app\logs\log-1.txt
Access is denied.
C:\>echo 'new' >> C:\app\logs\log-2.txt
Access is denied.
In the new container I can't create a new file or write to the existing log file, but I can see the content in the log file from the original container, and the line appended by the second container.