Running docker from a container
It's kind of like docker-in-docker (dind) but not quite!
In a rather standard continuous integration setup there is a process in which we needed a container to trigger other containers to be created and ran. A typical way to do this is to share the host's docker daemon with the container. This way we avoid any pitfalls with a true docker-in-docker setup that is also unnecessary for our use cases since we only need access to the host docker engine to satisfy our needs. The way this is accomplished is by using the volume flag on the docker run command like -v /var/run/docker.sock:/var/run/docker.sock which shares the host docker socket with the container using a bind mounted volume. Alternatively this looks like the following snipped when using docker-compose:
# docker-compose.yml
version: '3.8'
services:
web:
build: .
volumes:
- /var/run/docker.sock:/var/run/docker.sock
Here is where we run into trouble. The error was a bit confusing because the message is a bit of a misdirect:
$ docker-compose up --build
[...]
docker: Error response from daemon: Mounts denied:
The path /foo
is not shared from OS X and is not known to Docker.
You can configure shared paths from Docker -> Preferences... -> File Sharing.
See https://docs.docker.com/docker-for-mac/osxfs/#namespaces for more info.
An example app is available here: https://github.com/djbender/docker-siblings-error-example
It might seem like there is an issue with Docker Desktop's permissions. However the message states that the path /foo is not shared. This is our hint that something else is amiss because /foo is not a path that is available on our host filesystem. Since we're sharing the host's docker engine with the container, all source paths for volumes must available directly on the host! In this particular setup - ./foo:/foo is listed as a volume for the web service in docker-compose.yml to make /foo materialize in the container. We can see now that we've basically moved foo from it's original location to its current location in the container. The tricky part is that this configuration does work when ran directly on the host.
One solution to this issue is to make the source and destination paths exactly the same. If foo is available at /usr/local/src/foo and mounted to the same path than any subsequently created container could use the same source! This also requires an entry to Docker Desktop's File Sharing options on macOS. However I think this is a less desirable solution because it depends on absolute paths which aren't very portable with a codebase under source control.
My preferred solution to this problem is to reorganize the Dockerfile and docker-compose.yml configuration so that these assets are copied directly into the docker image. Instead of virtually mounting these resources via volumes it appears to be much more portable to include them in the Dockerfile instead. This means that we remove the volume entries for - ./foo:/foo in docker-compose.yml and instead add a COPY directive to the Dockerfile.
One conclusion of this experiment is to always double check that your builds also work when ran inside an image.
Ruby Forest Druid.