I’m new into Docker. I have a problem with uploading file in Flask app that is contained in Docker and accessing it.
Let’s say that I have a project in this structure:
/home
| /myProjects
| /myDockers
| | /myApp
| | | /controller
| | | ...
| | | wsgi.py
| | | .gitlab-ci.yml
| | | DockerFile
| | | gunicorn.sh
| /uploads
My Flask app is in /MyApp, while I want to upload file into /uploads. The app was deployed on Docker with these configurations:
.gitlab-ci.yml:
stages:
- build
- deploy
build-image:
stage: build
only:
refs:
- dev
script:
- cd /home/myProjects/myDocker/myApp/
- git checkout dev
- git pull origin dev
- docker container rm -f myApp
- docker image rm -f myApp
- docker build -t myApp -f 20021_DockerfileLmsGeneralRepositoryService .
deploy-container:
stage: deploy
only:
refs:
- dev
script:
- docker run -d --network host -e DATABASE_URL=$DATABASE_URL -e REDIS_HOST=$REDIS_HOST --name myApp myApp
DockerFile:
FROM python:3.8.7-slim-buster AS compile-image
RUN apt-get update && apt-get install -y --no-install-recommends build-essential gcc libpq-dev && apt-get install -y apt-utils
COPY ./requirements.txt .
RUN pip install --upgrade --user -r requirements.txt
RUN pip install injector
COPY . /code
FROM python:3.8.7-slim-buster AS build-image
COPY --from=compile-image /code/ /code
COPY --from=compile-image /root/.local /root/.local
# Make sure scripts in .local are usable:
ENV PATH=/root/.local/bin:$PATH
WORKDIR /code
RUN chmod a+x gunicorn.sh
ENTRYPOINT ["sh","./gunicorn.sh"]
gunicorn.sh:
#!/bin/sh
gunicorn wsgi:application -w 2 --threads 2 --preload -b 0.0.0.0:20021
I already create an API in my Flask app to upload file and succeed in local (Windows) PC, but then when I deploy it into the development server, the structure of folder is different from what I expected. When I checked the project structure using script, it returned:
"/code/controller"
Which is different from the structure of the project in the server, so I ask my senior and he said I should learn about Volume in Docker since the project is contained inside Docker container, but I never used Docker in the first place.
I also already tried to change the script in .gitlab-ci.yml into this (I tried this from this question) and yeah it doesn’t work:
- docker run -d --network host -e DATABASE_URL=$DATABASE_URL -e REDIS_HOST=$REDIS_HOST --name myApp -v /home/myProjects/:/root/.local/code myApp
2
Answers
you are using
cd /home/myProjects/myDocker/myApp/
in CI file andCOPY . /code
in docker build file.that means "whatever you see in host’s
/home/myProjects/myDocker/myApp/
folder, copy them all into/code
folder of the container". thus your...myApp/*
becomes/code/*
but your pain is not just that. from the app’ perspective, you are writing uploaded files into
../../uploads
but you don’t have such a folder in your container. also note that you have 2 parent levels in your host and your app is looking for such a path and won’t find inside the container anyways because your project is only 1 level deep from the root level.in this latter situation, change your code to read uploads folder path from the environment (
os.environ?
), set default to be../../uploads
for your development, create a folder inside your container such as/uploads
, add its path in your docker file withENV
then map host and container upload folder paths with volume command, possibly-v home/myProjects/uploads:/uploads
edit:
I have written a simple app doing the above last paragraph. please visit this address: https://github.com/yilmazdurmaz/basicbindmount
After our talk, I could see another and easier solution. parts of my other answer are still applying.
from your working folder structure, your app seems to try writing to
../../uploads
folder, 2 levels up in directory structure. thus we can create a folder structure in the container to suit this.in
Dockerfile
make these changes:COPY --from=compile-image /code/ /code
toCOPY --from=compile-image /code/ /code/app
WORKDIR /code
toWORKDIR /code/app
RUN mkdir /uploads
anywhere afterFROM .. AS build-image
and but beforeENTRYPOINT
we copy the app 2 level deep in the container and adding uploads folder into root.
now run change your run command into this:
we bind-mount an uploads folder in the host into the uploads folder in the container. you can have the bind as
-v path_to_your_uploads_folder_in_host:/uploads