feat. Advanced Docker - Volumes
Containers are disposable. Because of their ephemeral nature, Docker containers don't store any data, by default. This is great... until we need to work with persistent data (which is like... always!). There are three main use cases for Docker volumes
- To keep data around when a container is removed
- To share data between the host filesystem and the Docker container
- To share data with other Docker containers
On this article of feat. series, I'll write about two ways docker deals with persistent data - Volumes & Bind Mounts. Let's start with Volumes. I'll skip all the theory and dive right into learning by doing. Head over to Docker Hub and search for your favourite database name in the search bar. I'll use mysql as an example and click on the latest Dockerfile link.
The link should take you to github and you'll see the respective dockerfile for mysql. Find the line VOLUME /var/lib/mysql (for your choice of database, the name and path might vary but look for the keyword VOLUME). By invoking the VOLUME keyword under dockerfile, we're asking docker to make special location outside the container UFS (read more on UFS here). When running a container from this mysql image, docker creates a new volume location and assigns it to this (/var/lib/mysql) directory in the container. This means any file that we put in that location, will outlive the container until we manually delete the volume. If you've been playing with lots of docker containers, try out this command on your terminal docker volume prune which removes any unused dangling local volume.
Just to reinforce this concept, volumes need manual deletion and we can't clean them up just by removing a container. Let's actually prove that volumes don't get deleted with container-deletion. We'll start off by creating a new mysql container by running this command: docker container run -d --name=mysql -e MYSQL_ALLOW_EMPTY_PASSWORD=True mysql. Once the container is created, then run docker container inspect <container ID>
"Mounts": [
{
"Type": "volume",
"Name": "a41a8e62ea198e226f0c107285e083c2df0f0563db9474e18eaaf998719dc0ac",
"Source": "/var/lib/docker/volumes/a41a8e62ea198e226f0c107285e083c2df0f0563db9474e18eaaf998719dc0ac/_data",
"Destination": "/var/lib/mysql",
"Driver": "local",
"Mode": "",
"RW": true,
"Propagation": ""
}
],
Refer to "Mounts" section of the output which shows the volume mount. The Source is the actual path of the volume on the linux VM which docker creates in the background and the Destination is the relative path which is mapped to the actual path. docker volume ls will show the volume created (if there's no previous volumes created, this should show only one volume). Let's delete the container now with docker container stop <container ID> && docker container rm <container ID>. Moment of truth ??Run docker volume ls and this should still show the same volume (if you had multiple volumes before, the number of volumes should remain the same). To remove the volume, you need to run docker volume rm <volume ID>.
Now let's talk about the second way docker deals with persistent data - Bind Mounts. Bind Mount maps a host file or directory to a container file or directory. This way docker is not going to wipe out the host location when we delete the container. When we don't need the bind mount any more and we re-run the container without it, we would actually see the underlying data that was there before. One other difference from Volumes is that we can't specify Bind Mounts in a Dockerfile; we have to use the -v flag at runtime when running the docker container run command. Let's run latest nginx container using bind mounts and verify the mapping of a local file to that running inside the container.
# Create an index.html file with a content of your choice
# Run an nginx container specifying bind mounts to that index.html
docker run -d -it --name nginx -p 80:80 -v $(pwd):/usr/local nginx
# Go inside the running container and check the content of that file
docker container exec -it nginx bash
# Navigate to /usr/local/index.html and match the content with your local copy
You can stop and remove the container but the local copy of the file will persist with the changes made from within the docker. This was a quick overview of Bind Mounts but if you'd like to know further, you can refer to this link.
This brings us to the end of this article where we learned about persistency in the docker world and two ways docker handles persistency - Volumes & Bind Mounts. In the next article, we'll discuss Docker Compose - a tool for defining and running multi-container Docker applications.