Docker and Kubernetes for Java Developers
上QQ阅读APP看书,第一时间看更新

Creating a volume

As you remember from Chapter 1, Introduction to Docker, there's a settings screen in Docker for Windows or Docker for Mac, that allows us to specify which drives Docker can have access to. For a start, let's mark drive D in our Docker for Windows to make it available for Docker containers:

For the purpose of our volume examples, I've created a docker_volumes/volume1 directory on my D drive and created an empty data.txt file inside:

There are two ways to create volumes. The first one is to specify the -v option when running an image. Let's run the busybox image we already know and, at the same time, create a volume for our data:

$ docker run -v d:/docker_volumes/volume1:/volume -it busybox  

In the previous command, we have created a volume using the -v switch and instructed Docker that the host directory d:/docker_volumes/volume1 should be mapped into the /volume directory in the running container. If we now list the contents of the /volume directory in the running busybox container, we can see our empty data1.txt file, as you can see in the following screenshot:

The parameters in the -v options are the directory on the host (your own operating system in this case, it is d:/docker_volumes/volume1 in our example), a colon, and a path at which it will be available for the container, /volume1 in our example. The volume created is a kind of mapped directory. It will be available for the container and also available from the host operating system. Any files already existing in the mapped directory (host's d:/docker_volumes/volume1) will be available inside the container; they will not be deleted during the mapping.

The -v option can be used not only for directories but for a single file as well. This can be very useful if you want to have configuration files available in your container. The best example for this is the example from the official Docker documentation:

$ docker run -it -v ~/.bash_history:/root/.bash_history ubuntu 

Executing the previous command will give you the same bash history between your local machine and a running Ubuntu container. And best of all, if you exit the container, the bash history on your own local machine will contain the bash commands you have been executing inside the container. Mapping files can be useful also for you, as a developer, when debugging or trying out your application configuration, for example.

Mapping a single file from a host allows exposing a configuration of your application.

Apart from creating a volume when starting a container, there is a command to create a volume prior to starting a container. We will use it now.

The simplest form of creating a nameless volume will be just:

$ docker volume create 

As the output, Docker will give you the volume identifier, which you can later use to refer to this volume. It's better to give a volume a meaningful name. To create a standalone, named volume, execute the following command:

$ docker volume create --name myVolume  

To list the volumes we now have available, execute the docker volume ls command:

$ docker volume ls  

The output will be simply the list of volumes we have created so far:

Volumes created this way will not be mapped explicitly with a path on the host. If the container's base image contains data at the specified mount point (as a result of Dockerfile processing), this data will be copied into the new volume upon volume initialization. This is different in comparison to specifying a host directory explicitly. The idea behind it is that when creating your image, you should not care about the location of the volume on the host system, making the image portable between different hosts. Let's run another container and map the named volume into it:

$ docker run -it -v myVolume:/volume --name myBusybox3 busybox  

Note that this time, we do not specify a path on the host. Instead, we instruct Docker to use the named volume we created in the previous step. The named volume will be available at the /volume path in the container. Let's create a text file on the volume:

If we run another container now, specifying the same named volume, we will be able to access the same data we have available in our myBusybox3 container which was created previously:

$ docker run -it -v myVolume:/volume --name myBusybox4 busybox  

Our two containers share the single volume now, as you can see in the following screenshot:

Docker named volumes are an easy way of sharing volumes between containers. They are also a great alternative to data-only containers that used to be a common practice in the old days of Docker. This is no longer the case—named volumes are way better. It's worth noting that you are not limited to just one volume per container, as that would be a serious limitation.

You can use the -v multiple times to mount multiple data volumes.

Another option to share the volume between containers is the -volumes-from switch. If one of your containers has volumes mounted already, by using this option we can instruct Docker to use the volume mapped in some other container, instead of providing the name of the volume. Consider this example:

$ docker run -it -volumes-from myBusybox4 --name myBusybox5 busybox  

After running the myBusybox5 container this way, again, if you enter the /volume directory in the myBusybox5 container running, you will see the same data.txt file.

The docker volume ls command can take some filter parameters, which can be quite useful. For example, you can list volumes that are not being used by any container:

docker volume ls -f dangling=true 

Volumes that are no longer used by any container can be easily removed by using the docker volumes prune command:

docker volume prune  

To list volumes being created with a specific driver (we are going to cover drivers in a short while), you can filter a list using the driver filter, as in the following example:

docker volume ls -f driver=local  

Last but not least, another way of creating a volume is the VOLUME CREATE instruction in a Dockerfile. We will be using it later in the book when creating an image from a Dockerfile. Creating volumes using the VOLUME CREATE instruction has one but very important difference in comparison to using the -v option during the container startup: you cannot specify a host directory when using VOLUME CREATE. It's an analogy to exposing and mapping ports. You cannot map a port in a Dockerfile. Dockerfiles are meant to be portable, shareable, and host-independent. The host directory is 100% host-dependent and will break on any other machine, which is a little bit off from the Docker's idea. Because of this, it is only possible to use portable instructions within a Dockerfile.

If you need to specify a host directory when creating a volume, you need to specify it at runtime.