Docker – Part 5 – Volumes

By Huntly Cameron Docker

There’s one last concept we need to understand to have enough of the fundamentals to be dangerous. And that would be volumes.

Persisting data

Containers are temporary things. When stopping a container will stick around. However, when the container is removed, all modifications will be lost.

To see this in action lets spin up an alpine container, name it data-test so we can easily control it, and make some changes:

docker run --name data-test -it alpine /bin/sh

Note, this run command has the -i and -t flags on it. This means we want an interactive container with a tty provided. You almost always use these two flags together. Then when the container has spun up, we run /bin/sh on that container to get a shell prompt.

All going well you should have a shell / #

echo "need to keep this" > important
cat important

When you cat the file you should see:

need to keep this

Type exit or press ctrl+d to exit. Then we can stop the container:

docker stop data-test

After that start the container with docker start data-test

To get back into the container, we need to run a docker exec command. This will run the specified command against a container. We want an interactive shell so we run the following:

docker exec -it data-test /bin/sh

In the shell prompt run cat important. Your super important message should still be there.

Exit back out of the shell and run the following commands to stop and then remove the container:

docker stop data-test
docker rm data-test

Now lets recreate the container and see if our message exists:

`docker run –name data-test -it alpine /bin/sh

If you run cat important you will get an error saying the file couldn’t be found.

Clean up by removing the data-test container by running

docker rm data-test

Volumes

To get around this docker provides two methods: volumes, and bind mounts. Both serve different purposes.

Bind mounts are a way to give docker direct access to a folder on your computer. For example, if you had a development server and wanted to edit the code and see it in action, you would use a bind mount.

Volumes, on the other hand, are isolated data stores that persist even when a container is stopped, removed, and then rebuilt. You would use these for things like database storage or caches. You can attach one volume to multiple containers.

Whilst you cant access volumes directly to edit the files you could attach a volume to an alpine container and then use a interactive shell to get at the contents.

Using a volume

First we need to create a volume, to do this we use the following command:

docker volume create important-data

Now that we’ve created our volume, we can attach it to a container when we run it.

docker run --rm -v imortant-data:/home --name data-test -it alpine /bin/sh

The -v flag is our volume flag and we tell it to map the important-data volume to the /home directory. We’re also using the --rm flag to remove the container once we exit.

/ # cd /home/
/home # echo "important stuff" > important
/home # cat important
important stuff
/ #

Now exit the shell, which will stop and remove the container. Start it back up again in the same way and then run the cat command to see that the contents of our important file have been saved

docker run --rm -v imortant-data:/home --name data-test -it alpine /bin/sh

Then once you get the shell prompt, cat out the contents of important:

/ # cat /home/important
important stuff

Finally, lets attach our volume to a completly different container and verify that they work as we expect. For this we’ll use the busybox image:

docker run --rm -v imortant-data:/home --name data-test -it busybox 

/ # cat /home/important
important stuff
/ #

Using a bind mount

Bind mounts are useful when we want to allow docker access to a particular folder on our machine.

Create a new empty directory called web-test and then create an index.html file with the following content:

<!DOCTYPE html>
<html>
 <head>
   <title>Hello, world</title>
 </head>
 <body>
 <h1>Hello, World!</h1>
 </body>
 </html>

We can now use an NGINX container to serve this. We bind mount that directory to where the NGINX container serves content using the following command:

docker run --rm -p80:80 --name some-nginx -v .:/usr/share/nginx/html:ro -d nginx

Notice how this is simillar to the volume mount. Confusingly, even with the bind mount we use the -v flag but instead of passing it a volume as the first argument, we pass a directory. Also, by default bind mounts are read/write. If we want to specify read only we add the :ro part at the end of our bind mount.

If you fire up http://localhost you should see the hello world page in all its glory. Try changing the file and reloading. You may have to do a hard refresh in your browser (usually ctrl/cmd + shift + r to see the changes).