A container is an instance of an image. The instance consists of the read-only layers of the image, with a unique copy-on-write layer, or sandbox. The writable layer is disposed of when we remove the container. So clearly we need to do something more to make data persist across instances. Docker provides two ways to do this.
When Docker creates a container on Windows, the container is instantiated as a Virtual Hard Disk (VHD). You can see the disk mounted without a drive letter, in Disk Management on the host. Docker keeps track of the layers, but the file operations take place inside the VHD.
If we use the interactive PowerShell console to create a new directory in the container, C:\Logs, then this is created directly inside the VHD:
When Docker removes the container, the VHD is also removed and the directory is gone.
Docker provides two ways to mount a directory on the host file system inside the container file system, so that data can persist across instances:
- Bind mount
- Volume mount.
A bind mount is simply a link to a directory on the host. A volume mount is a link to a directory tracked and managed by Docker. Docker recommends generally using volumes. You can read more about it in the Docker Storage Overview.
The parameter you commonly see to specify a mount is
--volume. A newer parameter, and the one Docker recommends, is
--mount. This has a more explicit syntax.
In this example, we mount a volume on the host called MyNewDockerVolume to C:\MyNewDockerVolume in the container:
docker run -it --rm --name core --mount type=volume,src=MyNewDockerVolume,dst=C:\MyNewDockerVolume microsoft/windowsservercore powershell
If the volume does not already exist, it is created inside the docker configuration folder on the host:
The Hyper-V Host Compute Service (vmcompute.exe) carries out three operations inside the VHD:
CreateFile: \Device\HarddiskVolume13\MyNewDockerVolume. Desired Access: Generic Read/Write, Disposition: OpenIf, Options: Directory, Open Reparse Point, Attributes: N, ShareMode: Read, Write, AllocationSize: 0, OpenResult: Created
FileSystemControl: \Device\HarddiskVolume13\MyNewDockerVolume. Control: FSCTL_SET_REPARSE_POINT
Now if we look in the VHD, in Explorer, we see the directory implemented as a shortcut:
In PowerShell, we can see that the directory mode is "l", to signify a reparse point, or link:
Files already in the volume will be reflected in the folder in the container. Files written to the folder in the container will be redirected to the volume.
Windows reparse points come in several flavours: directory or file link; hard link ("junction") or soft link ("symbolic link" or "symlink"). If we use the command prompt instead of PowerShell we can see that the Docker volume is implemented as a directory symlink:
Working with data in Windows Containers requires keeping three things in mind:
- The difference between bind mount and volume mount
- The different syntax for
- Differences in behaviour between Docker on Linux and Windows hosts.
The first two are well documented. The third is newer and less well documented. The main differences I can find are:
- You cannot mount a single file
- The target folder in the container must be empty
- Docker allows plugins for different drivers. On Linux you can use different storage drivers to connect remote volumes. On Windows the only driver is "local" and so the volume must be on the same host as the container.
If you reference a VOLUME in the Dockerfile to create an image, then the volume will be created automatically, if it does not already exist, without needing to specify it in the
docker run command.