dockerflask

Docker not saving a file created using python - Flask application


I created a Flask Application. This application receives a XML from a url and saves it:

  response = requests.get(base_url)
  with open('currencies.xml', 'wb') as file:
      file.write(response.content)

When I run the application without Docker, the file currencies.xml is correctly created inside my app folder. However, this behaviour does not occur when I use docker.

In docker I run the following commands:

docker build -t my-api-docker:latest .
docker run -p 5000:5000 my-api-docker ~/Desktop/myApiDocker # This is where I want the file to be saved: inside the main Flask folder

When I run the second command, I get:

docker: Error response from daemon: OCI runtime create failed: container_linux.go:345: starting container process caused "exec: \"/Users/name/Desktop/myApiDocker\": stat /Users/name/Desktop/myApiDocker: no such file or directory": unknown. ERRO[0001] error waiting for container: context canceled

But If I run:

docker build -t my-api-docker:latest .
docker run -p 5000:5000 my-api-docker # Without specifying the PATH

I can access the website (but it is pretty useless without the file currencies.xml

Dockerfile

FROM python:3.7
RUN pip install --upgrade pip

COPY ./requirements.txt /app/requirements.txt

WORKDIR /app

RUN pip install -r requirements.txt

COPY . /app

EXPOSE 5000

CMD [ "flask", "run", "--host=0.0.0.0" ]

Solution

  • When you

    docker run -p 5000:5000 my-api-docker ~/Desktop/myApiDocker
    

    Docker interprets everything after the image name (my-api-docker) as the command to run. It runs /Users/name/Desktop/myApiDocker as a command, instead of what you have as the CMD in the Dockerfile, and when that path doesn't exist in the container, you get the error you see.

    It's a little unlikely you'll be able to pass this path to your flask run command as a command-line argument. A typical way of dealing with this is by using an environment variable instead. In your code,

    download_dir = os.environ.get('DOWNLOAD_DIR', '.')
    currencies_xml = os.path.join(download_dir, 'currencies.xml')
    with open(currencies_xml, 'wb') as file:
      ...
    

    Then when you start your container, you can pass that as an environment variable with the docker run -e option. Note that this names a path inside the container; there's no particular need for this to match the path on the host.

    docker run \
      -p 5000:5000 \
      -e DOWNLOAD_DIR=/data \
      -v $HOME/Desktop/myApiDocker:/data \
      my-api-docker
    

    It's also fairly common to put an ENV statement in your Dockerfile or otherwise pick a fixed path for this, and just specify that your image's interface is that it will download the file into whatever is mounted on /data.