tjruesch
tjruesch

Reputation: 68

How to copy a subproject to the container in a multi container Docker app with Docker Compose?

I want to build a multi container docker app with docker compose. My project structure looks like this:

docker-compose.yml
...
webapp/
   ...
   Dockerfile
api/
   ...
   Dockerfile

Currently, I am just trying to build and run the webapp via docker compose up with the correct build context. When building the webapp container directly via docker build, everything runs smoothly.

However, with my current specifications in the docker-compose.yml the line COPY . /webapp/ in webapp/Dockerfile (see below) copies the whole parent project to the container, i.e. the directory which contains the docker-compose.yml, and not just the webapp/ sub directory. For some reason the line COPY requirements.txt /webapp/ works as expected.

What is the correct way of specifying the build context in docker compose? Why is the . in the Dockerfile interpretet as relative to the docker-compose.yml, while the requirements.txt is relative to the Dockerfile as expected? What am I missing?

Here are the contents of the docker-compose.yml:

version: "3.8"

services:
  frontend:
    container_name: "pc-frontend"
    volumes:
        - .:/webapp
    env_file:
      - ./webapp/.env
    build:
      context: ./webapp
    ports:
      - 5000:5000

and webapp/Dockerfile:

FROM python:3.9-slim

# set environment variables
ENV PYTHONWRITEBYTECODE 1
ENV PYTHONBUFFERED 1

# set working directory
WORKDIR /webapp

# copy dependencies
COPY requirements.txt /webapp/

# install dependencies
RUN pip install -r requirements.txt

# copy project
COPY . /webapp/  # does not work as intended

# add entrypoint to app
# ENTRYPOINT ["start-gunicorn.sh"]

CMD [ "ls", "-la" ]  # for debugging

# expose port
EXPOSE 5000

Upvotes: 0

Views: 251

Answers (1)

David Maze
David Maze

Reputation: 159722

The COPY directive is (probably) working the way you expect. But, you have volumes: that are overwriting the image content with something else. Delete the volumes: block.

The image build sequence is working exactly the way you expect. build: { context: ./webapp } uses the webapp subdirectory as the build context and sends it to the Docker daemon. When the Dockerfile for example COPY requirements.txt . it comes out of this directory. If you, for example, docker-compose run frontend pip freeze, you should see the installed Python packages.

After the image is built, Compose starts a container, and at that point volumes: take effect. When you say volumes: ['.:/webapp'], here the . before the colon refers to the directory containing the docker-compose.yml file (and not the webapp subdirectory), and then it hides everything in the /webapp directory in the container. So you're replacing the image's /webapp (which had been built from the webapp subdirectory) with the current directory on the host (one directory higher).

You should usually be able to successfully combine an ordinary host-based development environment and a Docker deployment setup. Use a non-Docker Python virtual environment to build the application and run its unit tests, then use docker-compose up --build to run integration tests and the complete application. With a setup like this, you don't need to deal with the inconveniences of the Python runtime being "somewhere else" as you're developing, and you can safely remove the volumes: block.

Upvotes: 1

Related Questions