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.