Docker (Swarm) and NFS volumes

Investigating how to use shared volumes with Docker (Swarm), I decided to take a look at NFS volumes, since this is probably the most used on premises way to share folders.

With Docker, you have 3 different syntaxes to mount NFS volumes :

  • simple container (via docker volume create + docker run)
  • single service (via docker service create)
  • complete stack (docker deploy -f stack.yml)

I actually had some trouble mounting NFS volumes, especially with images that COPY files into declared volumes.

So, without further ado…

Mounting a NFS share to a Docker container

Let’s start creating a folder under our nfs share (/nfs)

Now we have a file named « hello-test » under /nfs/test/

Let’s create a new docker volume linked to this nfs share, and then let’s spin up a new container that mounts this volume :

So far, so good ! (did you notice the host root user mapping to nfsnobody and the container root user mapping to nobody? we’ll come back to this a bit later)
Things unfortunately get weirder when you mount to a defined VOLUME location.
For example, with this Dockerfile :

Everything works great.
Now if you try to do the same thing with an image that copied something to this volume :

and try to map /data to the nfs volume, this time you’ll get :

To get out of this issue, you need the nocopy option :

Notice how the original empty.file from the Docker file was « nocopy »ed
So we got away with our issue, well, except something that was provided by the image (empty.file), is not anymore…

Let’s clean up first the previous experimentation

and move on to services.

Mounting a NFS share to a Docker service

This use case is more interesting than the previous one, since you only need to mount once to the service, and the volume will be mounted to any container created by this service, on any host part of the swarm. (here the service will just ls /data; by the way, don’t try to do anything fancy in entrypoint overriding from a service creation  – the parsed command is … not obvious…)

So far so good, but now with the infamous VOLUME with copy :

The service won’t create the container ! To see that, use service ps :

So adding a volume-nocopy=true should solve the issue :

Since, per the documentation :

« By default, if you attach an empty volume to a container, and files or directories already existed at the mount-path in the container (dst), the Engine copies those files and directories into the volume, allowing the host to access them. Set volume-nocopy to disables copying files from the container’s filesystem to the volume and mount the empty volume. A value is optional:  »

and indeed, the service is now started :

now, to have this service interact with others, let’s create a stack

Mounting a NFS share in a Docker Stack

To create a stack, we’ll create a docker-compose.yml file this time :

So a producer will write a file to the nfs share, and a consumer will ls -al the folder where the file is supposed to be written to; let’s deploy it and see how well that goes :

So that worked pretty well : you’ll notice the nocopy syntax that is this time :

If you make the mistake of putting it like this :

you’ll probably end up with :

since there is no folder named /nfs/test:nocopy !

Was that so terrible ?

Well, the different syntaxes are a bit confusing, and the error messages sometimes are…

Oh, I almost forgot, about the users ownerships : if you run an image that creates a new user and uses this owner to start the process that will write the file to the NFS share : well, according to your NFS share setup, it could be that the user created in the container could map to … a whole different user than nfsnobody !
In that case make sure all your images create this user the same way on Docker nodes that are configured the same, so that they’ll all interact nicely with the NFS share !

Special thanks to my colleague Akom who helped me debug those error messages along the way !

Open Source, Java, Docker, Continuous Integration, Self hosting