syntaqx
syntaqx

Reputation: 2866

Unable to bind host volume in docker-compose for Docker Windows

I've been attempting to solve this problem for what seems like an eternity, but it seems as though Docker for Windows has only recently been able to successfully bind volumes from the Windows host to its Docker containers. However, I'm unable to get the same functionality working via docker-compose. Does anyone have any definitive information surrounding this, or thoughts on how I might go about getting this to work?

I have read a painful amount of GitHub issues about Windows volume mounting, but it seems as though most of the answers are related to getting Docker to mount the volume. I'm having no issues there, just docker-compose.

To illustrate my issue, the following command gives me the following output, which is the correct contents of my local directory, mounted in Docker:

$ docker run --rm -v c:/Users/synta/go/src/github.com/syntaqx/example:/go/src/github.com/syntaqx/example alpine ls /go/src/github.com/syntaqx/example
LICENSE
README.md
cmd
docker-compose.yml
docs
go.mod
go.sum
example.go

However, given the same implementation within a docker-compose.yml:

version: '3.6'
services:
  example:
    build:
      context: .
      dockerfile: Dockerfile
    volumes:
      - /c/Users/syntaqx/go/src/github.com/syntaqx/example:/go/src/github.com/syntaqx/example

And the following Dockerfile:

ARG GO_VERSION=1.11
ARG ALPINE_VERSION=3.8

FROM golang:${GO_VERSION}-alpine${ALPINE_VERSION} AS builder

WORKDIR /go/src/github.com/syntaqx/example
RUN ls -alh

ENTRYPOINT ["go"]

I'm given literally no output (ls -alh gives . and .., letting me know the directory is definitely there via WORKDIR, but not populated from the binding):

$ docker-compose up -d --build
Building example
Step 1/6 : ARG GO_VERSION=1.11
Step 2/6 : ARG ALPINE_VERSION=3.8
Step 3/6 : FROM golang:${GO_VERSION}-alpine${ALPINE_VERSION} AS builder
 ---> be1230a1b343
Step 4/6 : WORKDIR /go/src/github.com/syntaqx/example
 ---> Using cache
 ---> a0ccb401a86a
Step 5/6 : RUN ls /go/src/github.com/syntaqx/example
 ---> Running in 0cd52d44a637
Removing intermediate container 0cd52d44a637
 ---> 4f362f738e49
Step 6/6 : ENTRYPOINT ["go"]
 ---> Running in 5d7a1e841bfe
Removing intermediate container 5d7a1e841bfe
 ---> 9da9cfcf372a

...

Perhaps I'm missing something obvious here, but I've tried a dozen ways of expressing the volume PATH (., C:\, /c/, ///c), with relative paths apparently being very broken in Docker Windows, and the other paths not changing the result.

Also very sorry if this is a duplicate, but from what I've seen, most other questions are very specifically trying to mount host volumes in Docker, period. Or, the issue with relative paths, which I'm happy to side step for the time being. To repeat, all of that is working fine for me as shown in my example, just docker-compose seems broken.

Hope one of you guys can help me! Feeling very out of my depth here.

Version

about docker

Upvotes: 2

Views: 1900

Answers (1)

syntaqx
syntaqx

Reputation: 2866

What a long road that was.

But, I learned something: I actually had a fundamental misunderstanding of how volume mounts work within Docker. To my surprise, everything, including relative volume paths are working as expected.

The fundamental misunderstanding I had for volume mounts is that they are not available during build steps, but the mount is attached to the final container. Docker imposes this requirement, as without it, builds would not be repeatable.

So, in the usecase I provided above, the only difference (albeit not obvious until I understood this) between the Dockerfile and the docker run command, is that my Dockerfile is actively building a container, and attempting to execute commands, where the docker run is only running the command on the prebuilt alpine.

So, is it possible to do what I was doing? Sort of

I ran into a couple of cheese solutions that utilize a decorator-ish entrypoint.sh chaining pattern, allowing you to chain multiple Dockerfile build outputs into their final container, and when it's run, executes chained commands before the given CMD. Simply put, you have a bunch of dockerfiles that all end with ENTRYPOINT ["entrypoint.sh"], and then outline the steps you would previously leverage RUN for:

  • ls.dockerfile
  • mod.dockerfile
  • final.dockerfile
#!/bin/sh
set -eu

until /go/src/github.com/syntaqx/example && go mod download
do
    echo "Waiting for mount..."
done

sh -c "go $*"

Then, you can have each dockerfile shove commands on top of the last one.

Note: I did not end up using this once, as I describe below, so this shell may not work, but I thought it was a cool implementation thought, so I wanted to nod to it.

However, for my use case, all of this was very unnecessary. Knowing why the commands are failing, I'm I'm able to simply defer the RUN commands until the final ENTRYPOINT, create a singular entrypoint.sh, and then run them in the order I wanted during the build. I don't benefit from dependency caching this way, but I also don't need it for this particular use case.

Thanks for reading, and I hope you learned something too!

Upvotes: 4

Related Questions