I have a simple flask project that is running fine with docker, but one step of the build is confusing me. Initially I had trouble getting pip to install the requirements.txt
file. It would fail indicating that the requirements file couldn't be found, #0 2.664 ERROR: Could not open requirements file: [Errno 2] No such file or directory: 'requirements.txt'
. I accidentally got it to run by adding a line in the flask Dockerfile to copy the requirements.txt
file to the flask container's /app
directory, but I'm confused about what it's actually doing because the file seems to already be in that directory. I also don't have to copy over any other files and it runs fine.
I looked through docker's best practices documentation for the COPY
command and saw this. COPY only supports the basic copying of local files into the container...
. So COPY
isn't just the same as the linux cp
command to copy contents within a container, it's from local into a container? Does this mean that commands in a Dockerfile don't actually have access to bind mounted volume data during the build unless it's copied over from the local side? If this is the case then I believe that solves my issue! Project structure below for reference of what lead to the issue.
This is the general structure of the project
project_root /
flask_app /
...
Dockerfile
requirements.txt
database /
nginx /
...
Dockerfile
docker-compose.yml
Here are the contents of docker-compose.yml
located in the root directory of the project.
services:
nginx:
...
flask-app:
build: flask_app
volumes:
- ./flask_app:/app
- ./database:/database
command: sh -c "gunicorn -w 4 -b 0.0.0.0:8001 'server:create_app()'"
# tty: true # used for testing to ssh into container
Here's the flask_app/Dockerfile
contents
FROM python:3.11.1-alpine3.17
COPY requirements.txt /app/ <-- Removing this causes the "no such file" error
WORKDIR /app
RUN pip install -r requirements.txt
# also tried as /app/requirements.txt and it fails without the copy command
What exactly is the copy doing that enables the requirements file to be found? I don't have to copy over any of the other flask related files to the container's /app
directory and it still runs. There is no requirements.txt
file in the project root either.
The requirements file seems to be present in the container's /app
directory even without the copy command. I checked by first commenting out the flask Dockerfile contents to be...
FROM python:3.11.1-alpine3.17
#COPY requirements.txt /app/
#
#WORKDIR /app
#
#RUN pip install -r requirements.txt
And then in the docker-compose.yml
file I swap the commented out tty line with the gunicorn command line above it. Then I can ssh into the flask container and see everything there, including the requirements.txt file in the /app
directory. I've verified this with a clean build by removing all the images using docker rmi $(docker images -a -q)
then building w/out cache using docker-compose build --no-cache
and then run it.
Thanks
CodePudding user response:
So COPY isn't just the same as the linux cp command to copy contents within a container, it's from local into a container? Does this mean that commands in a Dockerfile don't actually have access to bind mounted volume data during the build unless it's copied over from the local side?
This is correct, because those are 2 different steps. I think what you are confused about is the general workflow of Docker. There are essentially 2 steps that docker executes to start your application:
1. Building the Docker Image
This uses the Dockerfile to build an docker image. You can imagine a docker image for our purposes as a tar/zip of all the files you specify with your instructions inside the Dockerfile. The cool thing about Dockerfiles is, that you don't need to specify all the files. You can just run commands and Docker will automatically detect all the files installed by pip. The docker image should represent your finished product. Everything your application needs to run should be inside the docker image.2. Running the Docker Container
Running a docker container actually first creates the container from the docker image and then does all the things specified in the docker-compose.yml, such as doing port binding, mounting volumes, etc. For convenience the docker-compose.yml actually includes the `build` instruction which triggers a new docker image build every time the container is started.If your setup is a development setup binding the source code as a volume is fine, but as I said earlier, if you want to deploy your code everything should be inside the docker image.
Now to answer your question on why it doesn't work: The RUN pip install
step is run while building your Docker image. At this point in time docker doesn't know about any bind mounts or volumes yet, so naturally nothing is in the app directory. After you started your container the files are there, because you are specifying the bind mount in your docker-compose.yml.