Container Persistent Storage for Docker Swarm using a GlusterFS Volume Plugin

From one of my previous posts I demonstrated how to provide persistent storage for your containers by using a Convoy NFS Plugin.

I've stumbled upon one AWESOME GlusterFS Volume Plugin for Docker by @trajano, please have a look at his repository. I've been waiting for some time for one solid glusterfs volume plugin, and it works great.

What we will be doing today

We will setup a 3 node replicated glusterfs volume and show how easy it is to install the volume plugin and then demonstrate how storage from our swarms containers are persisted.

Our servers that we will be using will have the private ip's as shown below:

10.22.125.101
10.22.125.102
10.22.125.103

Setup GlusterFS

Have a look at this post to setup the glusterfs volume.

Install the GlusterFS Volume Plugin

Below I'm installing the plugin and setting the alias name as glusterfs, granting all permissions and keeping the plugin in a disabled state.

$ docker plugin install --alias glusterfs trajano/glusterfs-volume-plugin --grant-all-permissions --disable

Set the glusterfs servers:

$ docker plugin set glusterfs SERVERS=10.22.125.101,10.22.125.102,10.22.125.103

Enable the glusterfs plugin:

$ docker plugin enable glusterfs

Create a Service in Docker Swarm

Deploy a sample service on docker swarm with a volume backed by glusterfs. Note that my glusterfs volume is called gfs

version: "3.4"

services:
  foo:
    image: alpine
    command: ping localhost
    networks:
      - net
    volumes:
      - vol1:/tmp

networks:
  net:
    driver: overlay

volumes:
  vol1:
    driver: glusterfs
    name: "gfs/vol1"

Deploy the stack:

$ docker stack deploy -c docker-compose.yml test
Creating service test_foo

Have a look on which node is your container running:

$ docker service ps test_foo
ID                  NAME                IMAGE               NODE                DESIRED STATE       CURRENT STATE            ERROR               PORTS
jfwzb7yxnrxx        test_foo.1          alpine:latest       swarm-worker-1      Running             Running 37 seconds ago

Now jump to the swarm-worker-1 node and verify that the container is running on that node:

$ docker ps
CONTAINER ID        IMAGE                                          COMMAND                  CREATED             STATUS                  PORTS               NAMES
d469f341d836        alpine:latest                                  "ping localhost"           59 seconds ago      Up 57 seconds                               test_foo.1.jfwzb7yxnrxxnd0qxtcjex8lu

Now since the container is running on this node, we will also see that the volume defined in our task configuration will also be present:

$ docker volume ls
DRIVER                       VOLUME NAME
glusterfs:latest             gfs/vol1

Exec into the container and look at the disk layout:

$ docker exec -it d469f341d836 sh
/ # df -h
Filesystem                Size      Used Available Use% Mounted on
overlay                  45.6G      3.2G     40.0G   7% /
10.22.125.101:gfs/vol1   45.6G      3.3G     40.0G   8% /tmp

While you are in the container, write the hostname's value into a file which is mapped to the glusterfs volume:

$ echo $HOSTNAME > /tmp/data.txt
$ cat /tmp/data.txt
d469f341d836

Testing Data Persistence

Time to test the data persistence. Scale the service to 3 replicas, then hop onto a new node where a replica resides and check if the data was persisted.

$ docker service scale test_foo=3
test_foo scaled to 3
overall progress: 3 out of 3 tasks
1/3: running   [==================================================>]
2/3: running   [==================================================>]
3/3: running   [==================================================>]
verify: Service converged

Check where the containers are running:

$ docker service ps test_foo
ID                  NAME                IMAGE               NODE                DESIRED STATE       CURRENT STATE            ERROR               PORTS
jfwzb7yxnrxx        test_foo.1          alpine:latest       swarm-worker-1      Running             Running 2 minutes ago
mdsg6c5b2nqb        test_foo.2          alpine:latest       swarm-worker-3      Running             Running 15 seconds ago
iybat57t4lha        test_foo.3          alpine:latest       swarm-worker-2      Running             Running 15 seconds ago

Hop onto the swarm-worker-2 node and check if the data is persisted from our previous write:

$ docker exec -it 4228529aba29 sh
$ cat /tmp/data.txt
d469f341d836

Now let's append data to that file, then delete the stack and recreate to test if the data is still persisted:

$ echo $HOSTNAME >> /tmp/data.txt
$ cat /tmp/data.txt
d469f341d836
4228529aba29

On the manager delete the stack:

$ docker stack rm test
Removing service test_foo

The deploy the stack again:

$ docker stack deploy -c docker-compose.yml test
Creating service test_foo

Check where the container is running:

$ docker service ps test_foo
ID                  NAME                IMAGE               NODE                DESIRED STATE       CURRENT STATE           ERROR               PORTS
9d6z02m123jk        test_foo.1          alpine:latest       swarm-worker-1      Running             Running 2 seconds ago

Exec into the container and read the data:

$ docker exec -it 3008b1e1bba1 cat /tmp/data.txt
d469f341d836
4228529aba29

And as you can see the data is persisted.

Resources

Please have a look and star @trajano's repository:

Thanks to frank mckenna  and unsplash.com for this sweet header photo!