Search code examples
dockerdocker-composessh

How to access bound mounts during docker build


I've defined a docker-compose.yml like this:

services:
  api:
    build:
      context: .
      dockerfile: Dockerfile
    image: test-api-local
    environment:
      STORAGE_TYPE: local
      DATA_DIR: /data
      CACHE_DIR: /cache
      DEBUG: "true"
    ports:
      - "8000:8000"
    volumes:
      - type: bind
        source: /home/user/.ssh
        target: /root/.ssh
        read_only: true

and a dockerfile like this:

# Use Python 3.10 slim image as base
FROM python:3.10-slim


WORKDIR /app
RUN echo "$(ls -la /root/)" && echo "$(ls -la /root/.ssh/)"    


and the output of the ls commands is:

#6 [3/3] RUN echo "$(ls -la /root/)" && echo "$(ls -la /root/.ssh/)"
#6 0.254 total 20
#6 0.254 drwx------ 1 root root 4096 Feb  4 04:30 .
#6 0.254 drwxr-xr-x 1 root root 4096 Feb  5 10:21 ..
#6 0.254 -rw-r--r-- 1 root root  571 Apr 10  2021 .bashrc
#6 0.254 -rw-r--r-- 1 root root  161 Jul  9  2019 .profile
#6 0.254 -rw------- 1 root root    0 Feb  4 04:30 .python_history
#6 0.254 -rw-r--r-- 1 root root  169 Feb  4 04:26 .wget-hsts
#6 0.256 ls: cannot access '/root/.ssh/': No such file or directory
#6 0.256 
#6 DONE 0.3s

Shouldn't the /root have an .ssh folder as defined by the target of the bind mount?

Docker's bind mounts docs state as use case: "Sharing .. files from the host machine to containers."

It also states: "Bind mounts are also available for builds:" Consequently, I believe that I'm implementing it incorrectly.

Bind mounts are appropriate for the following types of use case:

Sharing source code or build artifacts between a development environment on the Docker host and a container.

When you want to create or generate files in a container and persist the files onto the host's filesystem.

Sharing configuration files from the host machine to containers. This is how Docker provides DNS resolution to containers by default, by mounting /etc/resolv.conf from the host machine into each container.

Bind mounts are also available for builds: you can bind mount source code from the host into the build container to test, lint, or compile a project.

Following the answer by boyvinall in this question doesn't work. My question is why doesn't it work.

What am I missing here?

I'm using:

docker-buildx-plugin (0.20.0-1~ubuntu.22.04~jammy) containerd.io (1.7.25-1) docker-ce-cli (5:27.5.1-1~ubuntu.22.04~jammy) docker-ce (5:27.5.1-1~ubuntu.22.04~jammy)

This ended up working:

services:
  api:
    build:
      context: .
      dockerfile: Dockerfile
    image: test-api-local
    environment:
      STORAGE_TYPE: local
      DATA_DIR: /data
      CACHE_DIR: /cache
      DEBUG: "true"
    ports:
      - "8000:8000"
    volumes:
      - type: bind
        source: ${SSH_AUTH_SOCK}
        target: /ssh-agent

with this command:

DOCKER_BUILDKIT=1 docker build \
  --ssh default \
  -f Dockerfile.local \
  -t test-api-local .

This command also works:

docker compose -f docker-compose.local.yml --ssh default

Which indicates to me that the issue is likely due to:

  1. the differences between docker-compose and docker build or docker compose, as previously I was using docker-compose and none of the configurations work with docker-compose
  2. mounting the ssh-agent vs mounting the ssh keys and then trying to start the agent and add keys within the build.

Solution

  • The docker-compose.yml defines the services, i.e., one or multiple containers for your project. For the images used to start these containers you can define how to build them with the build property of the services.

    But this also means that the volumes of a service are defined only in regards to the running containers and have no effect on the build of them. Building the images and running the containers are distinctly different things. That's why your previous attempts to bind-mount something into the build process didn't work.

    Though you can bind-mount directories into the build process with RUN --mount=type=bind this is limited to directories from other images/build stages or the build context. So you can't use this to bind-mount arbitrary directories from the host.

    But since what you're actually trying to is to make your ssh credentials available you should be using RUN --mount=type=ssh in your Dockerfile:

    FROM python:3.10-slim
    
    RUN --mount=type=ssh ssh ...
    

    To make the ssh-agent socket available during build start it with the --ssh flag:

    docker compose build --ssh default
    

    or add the ssh build property to your docker-compose.yml:

    services:
      api:
        build:
          context: .
          dockerfile: Dockerfile
          ssh: ["default"]