Running Flask in Docker Using Volumes Causes Error on Reload - python

I'm setting up my dev environment for a flask app in a docker container and during dev I want to be able to put Flask in development mode so that I can get live reloads when I change a file. I'm using volumes so that I can edit the files outside of docker. I am getting the desired behavior from Flask, on editing the flask test file it detects the file change, however, it appears to be attempting to serve trunkated files instead of the files as edited resulting in errors from the app.
Dockerfile:
FROM python:3.8.1-alpine3.11
RUN apk update && apk upgrade && apk add bash
#Set ENV Variables
ENV FLASK_APP hello.py
#Add user.
RUN adduser -D eskimotv
USER eskimotv
#Set Work directory
WORKDIR /home/eskimotv
#Install Dependencies
COPY requirements requirements
RUN python -m venv venv
RUN venv/bin/pip install --upgrade pip
RUN venv/bin/pip install -r requirements/docker.txt
#Copy project files
COPY frontend frontend
COPY migrations migrations
COPY boot.sh ./
#Deploy Project
EXPOSE 5000
#ENTRYPOINT ["./boot.sh"]
Docker-compose.yml:
version: '3'
services:
frontend:
image: bentsea/eskimotv-frontend:latest
build:
dockerfile: frontend.dockerfile
context: .
ports:
- "8000:5000"
env_file: .env
volumes:
- './boot.sh:/home/eskimotv/boot.sh'
- './hello.py:/home/eskimotv/hello.py'
- './frontend:/home/eskimotv/frontend'
#entrypoint: ./boot.sh
restart: always
Simple flask app, hello.py:
from flask import Flask
app = Flask(__name__)
#app.route('/')
def index():
return '<h1>Hello World!</h1><p> Please work!</p>'
#app.route('/user/<name>')
def user(name):
return '<h1>Hello, {}!</h1>'.format(name)
Environtment variable FLASK_ENV is set to development. When starting up the app for the first time all my changes are displayed correctly:
If I make a very simple edit, such as changing line 10 of hello.py with a cosmetic change, such as the following:
return '<h1>Hello, {}, I made a small change to the text!</h1>'.format(name)
Flask will appropriately reload:
* Detected change in '/home/eskimotv/hello.py', reloading
* Restarting with stat
* Debugger is active!
* Debugger PIN: 234-654-899
172.21.0.1 - - [31/Dec/2019 20:00:20] "GET /user/David HTTP/1.1" 200 -
* Detected change in '/home/eskimotv/hello.py', reloading
* Restarting with stat
* Debugger is active!
* Debugger PIN: 234-654-899
However, the page will begin to produce an error rather than correctly display the page:
Sometimes it's just an EOL error that indicates that the file is truncated, sometimes it indicates this null error. Either restarting the Flask server or undoing the edits made to the file resolves the issue and the app will function normally. Restarting the server every time I make an edit is the action I was wanting to avoid.
Does anyone have any ideas what would cause the files in this volume configuration to load incorrectly and what I can do to resolve the issue to get the intended behavior of having the reload display the file as it is edited?

I don't know why the error is occurring, however it does appear to be caused by mounting a single file instead of a directory. By restructuring my app to mount only entire directories that include the files that need to be updated I was able to successfully use a workflow that allows for automatic Flask reload through Docker on file change.
For whatever reason Docker does not accurately update individual files that are mounted if they are outside of a mounted directory.

Try after you change your code to rebuild your docker.
Before making the change do
docker-compose stop
Then make the change by editing the line
return '<h1>Hello, {}, I made a small change to the text!</h1>'.format(name)
after making your change above, now do
docker-compose build
And finally
docker-compose up

Related

Uvicorn --reload using WatchFiles is not working for FastAPI in Docker container

I am developing a FastAPI app. It is running on Uvicorn in a Docker container using docker-compose.
I want to include some files other than *.py to trigger the auto reload while in development.
According to the docs Uvicorn needs the optional dependency WatchFiles installed to be able to use the --reload-include flag, which would enable me to include other file types to trigger a reload. However, when WatchFiles is installed (with Uvicorn confirming by printing this info at start up: Started reloader process [1] using WatchFiles) no auto reloads happen at all. Mind you, this is independent of changes to the run command, with or without the include flag.
Without WatchFiles installed, Uvicorn's default auto reload works as intended for just *.py files.
What I've got
This is the Dockerfile:
FROM python:3.10
WORKDIR /tmp
RUN pip install --upgrade pip
COPY requirements.txt .
RUN pip install --no-cache-dir --upgrade -r requirements.txt
WORKDIR /code
CMD ["uvicorn", "package.main:app", "--host", "0.0.0.0", "--port", "80", "--reload"]
This is the docker-compose.yml:
version: "3.9"
services:
fastapi-dev:
image: myimagename:${TAG:-latest}
build:
context: .
volumes:
- ./src:/code
- ./static:/static
- ./templates:/templates
restart: on-failure
ports:
- "${HTTP_PORT:-8080}:80"
(I need a docker-compose file because of some services required later on.)
The most basic FastAPI app:
from fastapi import FastAPI, HTTPException
app = FastAPI()
#app.get('/')
async def index():
raise HTTPException(418)
Mind you, this is probably of no concern as the problem does not seem to be related to FastAPI.
requirements.txt:
fastapi~=0.85
pydantic[email]~=1.10.2
validators~=0.20.0
uvicorn~=0.18
watchfiles
python-decouple==3.6
python-multipart
pyotp~=2.7
wheezy.template~=3.1
How did I try to resolve this issue?
I tried using command: uvicorn package.main:app --host 0.0.0.0 --port 80 --reload in docker-compose.yml instead of CMD [...] in the Dockerfile, which unsurprisingly changed nothing.
I created a file watch.py to test if WatchFiles works:
from watchfiles import watch
for changes in watch('/code', force_polling=True):
print(changes)
And…in fact it does work. Running it from the container in Docker CLI prints all the changes made. (python -m watch) And btw it works just as fine async/using asyncio. So it is probably nothing to do with the file system/share/mount within Docker.
So…
How do I fix it? What is wrong with Uvicorn(?) I need to check for other file types e.g. *.html in /templates. Do I have to get WatchFiles to work or are there other ways? If I do, how?
I just had the same problem and the problem is with WatchFiles.
In the watchfiles documentation it is understood that the detection relies on file system notifications, and I think that via docker its events are not launched when using a volume.
Notify will fall back to file polling if it can't use file system notifications
So you have to tell watchfiles to force the polling, that's what you did in your test python script with the parameter force_polling and that's why it works:
for changes in watch('/code', force_polling=True):
Fortunately in the documentation we are given the possibility to force the polling via an environment variable.
Add this environment variable to your docker-compose.yml and auto-reload will work:
services:
fastapi-dev:
image: myimagename:${TAG:-latest}
build:
context: .
volumes:
- ./src:/code
- ./static:/static
- ./templates:/templates
restart: on-failure
ports:
- "${HTTP_PORT:-8080}:80"
environment:
- WATCHFILES_FORCE_POLLING=true

Can't access my simple flask app running in docker [duplicate]

This question already has answers here:
Deploying a minimal flask app in docker - server connection issues
(8 answers)
Closed 1 year ago.
The app runs, but when I try to go to the link # http://172.17.0.2:5000/ it times out.
relevant code:
app = Flask(__name__)
#app.route('/')
def index():
return "hullo"
I have also tried it with and without this code underneath which is what other stackoverflow responses to questions similar to mine have suggested, but it doesn't work:
if __name__ == '__main__':
app.run(host ='0.0.0.0')
Dockerfile:
FROM python:3.9.6-slim-buster
WORKDIR /project
RUN python -m pip install --upgrade pip
RUN pip install -U Flask Flask-WTF Flask-SQLAlchemy DateTime python-dotenv mysql-connector-python
COPY . .
CMD [ "python3", "-m" , "flask", "run", "--host", "0.0.0.0"]
Run cmd:
docker run --publish 5000:5000 testbon5
This is practically copy and pasted from the official docs tutorial. Is there something I'm doing wrong?
figured it out. from a very nice person on slack.
I needed to type "localhost:/5000" into my address bar. It confused me because I have always access the site I'm working on through the flask log which in this case is:
* Serving Flask app 'project/app.py' (lazy loading)
* Environment: development
* Debug mode: on
* Running on all addresses.
WARNING: This is a development server. Do not use it in a production deployment.
* Running on http://172.17.0.2:5000/ (Press CTRL+C to quit)
* Restarting with stat
* Debugger is active!
I guess since it's running in the container that address doesn't work anymore. Hopefully this helps anyone else going through growing pains like me while learning docker.

Docker: How do I debug localhost "Internal Server Error"?

First time using Docker(version 19.03.5) and trying this tutorial
I'm stuck on step 2.3.4 Running an image
When I go to http://localhost:8888 I see
Internal Server Error
The server encountered an internal error and was unable to complete your request. Either the server is overloaded or there is an error in the application.
I updated Dockerfile to this to match my directory:
# our base image
FROM alpine:3.5
# Install python and pip
RUN apk add --update py2-pip
# install Python modules needed by the Python app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
# copy files required for the app to run
COPY app.py .
COPY templates/index.html templates
# tell the port number the container should expose
EXPOSE 5000
# run the application
CMD ["python", "app.py"]
On my command line I have
C:\Users\user\docker\flask-app>docker run -p 8888:5000 --name flask-app 11111111/flask-app
* Running on http://0.0.0.0:5000/ (Press CTRL+C to quit)
When I visit the page I see on the prompt
172.17.0.1 - - [05/Jan/2020 07:14:34] "GET / HTTP/1.1" 500 -
I have this in my app.py
from flask import Flask, render_template
import random
app = Flask(__name__)
# list of cat images
images = [
"http://img.buzzfeed.com/buzzfeed-static/static/2013-10/enhanced/webdr05/15/9/anigif_enhanced-buzz-26388-1381844103-11.gif",
"http://img.buzzfeed.com/buzzfeed-static/static/2013-10/enhanced/webdr01/15/9/anigif_enhanced-buzz-31540-1381844535-8.gif",
"http://img.buzzfeed.com/buzzfeed-static/static/2013-10/enhanced/webdr05/15/9/anigif_enhanced-buzz-26390-1381844163-18.gif",
"http://img.buzzfeed.com/buzzfeed-static/static/2013-10/enhanced/webdr06/15/10/anigif_enhanced-buzz-1376-1381846217-0.gif",
"http://img.buzzfeed.com/buzzfeed-static/static/2013-10/enhanced/webdr03/15/9/anigif_enhanced-buzz-3391-1381844336-26.gif",
"http://img.buzzfeed.com/buzzfeed-static/static/2013-10/enhanced/webdr06/15/10/anigif_enhanced-buzz-29111-1381845968-0.gif",
"http://img.buzzfeed.com/buzzfeed-static/static/2013-10/enhanced/webdr03/15/9/anigif_enhanced-buzz-3409-1381844582-13.gif",
"http://img.buzzfeed.com/buzzfeed-static/static/2013-10/enhanced/webdr02/15/9/anigif_enhanced-buzz-19667-1381844937-10.gif",
"http://img.buzzfeed.com/buzzfeed-static/static/2013-10/enhanced/webdr05/15/9/anigif_enhanced-buzz-26358-1381845043-13.gif",
"http://img.buzzfeed.com/buzzfeed-static/static/2013-10/enhanced/webdr06/15/9/anigif_enhanced-buzz-18774-1381844645-6.gif",
"http://img.buzzfeed.com/buzzfeed-static/static/2013-10/enhanced/webdr06/15/9/anigif_enhanced-buzz-25158-1381844793-0.gif",
"http://img.buzzfeed.com/buzzfeed-static/static/2013-10/enhanced/webdr03/15/10/anigif_enhanced-buzz-11980-1381846269-1.gif"
]
#app.route('/')
def index():
url = random.choice(images)
return render_template('index.html', url=url)
if __name__ == "__main__":
app.run(host="0.0.0.0")
I can't figure out why my page isnt loading. Any help would be appreciated.
Note: I have WAMP installed and this might be conflicting but not sure if thats the case and/or how to fix it.
Flask might be unable to locate your templates. Try changing
COPY templates/index.html templates
to
COPY templates templates
to copy everything inside ./templates to <WORKDIR>/templates.
Using COPY templates/index.html templates will copy index.html as a file at path <WORKDIR>/templates, not copy it under that directory.
How to debug your Flask application in Docker:
Turn on Flask debugger by adding ENV FLASK_DEBUG=1 to your Dockerfile
Try to run the Flask app outside of Docker. It may be easier to set breakpoints inside your IDE (VSCode or PyCharm) and debug the app.
Try pdb to debug the app inside the container. It may be hard for a beginner, but in general, it's an essential skill. Check Debugging a Python Flask Application in a Docker Container for a step-by-step guide.
Note: This is more aimed at the question "How do I debug". It was unclear that the OP actually wanted a solution, not a way to solve the problem.
First thing to do is to start the container without the app in it. For that, you replace the CMD ["python", "app.py"] with CMD ["sleep", "inf"]. Now, after starting the container, you can get a shell in the container using docker exec -it flask-app /bin/bash to get a shell. In the shell, you can then use the regular Python debugger to set a breakpoint in the / handler and then single-step through the code to track down what Python's doing.

ModuleNotFoundError: No module named 'verovio'

Hi there I would like to run my flask app in a container but I got stucked caused of a third party module. (I am using PyCharm)
This is my docker file:
FROM python:3-alpine
MAINTAINER foo
COPY app /app
WORKDIR /app
RUN pip install -r requirements.txt
CMD ["python3", "gateway.py"]
I run the container by this command:
docker run --name mei_converter -p 5000:5000 mei_converter:latest
And receives this:
ModuleNotFoundError: No module named 'verovio'
I already fixed the dependencies in order to run it on my local machine
Terminal: python3 gateway.py
['/usr/local/lib/python3.7/site-packages',
'/Users/max/PycharmProjects/flaskmicroservice/app',
'/usr/local/Cellar/python/3.7.4/Frameworks/Python.framework/Versions/3.7/lib/python37.zip',
'/usr/local/Cellar/python/3.7.4/Frameworks/Python.framework/Versions/3.7/lib/python3.7',
'/usr/local/Cellar/python/3.7.4/Frameworks/Python.framework/Versions/3.7/lib/python3.7/lib-
dynload', '/Users/max/Library/Python/3.7/lib/python/site-packages',
'/usr/local/lib/python3.7/site-packages', '/usr/local/lib/python3.7/site-packages/verovio-
2.0.0-py3.7-macosx-10.13-x86_64.egg']
* Serving Flask app "gateway" (lazy loading)
* Environment: production
WARNING: This is a development server. Do not use it in a production deployment.
Use a production WSGI server instead.
* Debug mode: off
* Running on http://0.0.0.0:5000/ (Press CTRL+C to quit)
This works well
I also checked the project interpreter in PyCharm and found the package as installed.
Maybe some information about this third party package could help:
It is a converter for music-encoding from these guys https://www.verovio.org/python.xhtml
and not "native python" (SWIG is necessary)
By the way.. I come from the java world and have in fact zero experience with python. I am using this flask app as an microservice for my spring boot app.
I would be grateful for any help!
You need to do in the container the same you did in your local host. That is, you need to add steps to the Dockerfile to compile verovio and use it in the container.

Containerized flask app does not load if port is Changed

I have created a simple flask app that is running on a this is the skeleton o the flask app, which by default runs at port 5000:
# Create the application instance
app = connexion.App(__name__, specification_dir="./")
# read the swagger.yml file to configure the endpoints
app.add_api("swagger.yml")
# Create a URL route in our application for "/"
#app.route("/")
def home():
"""
This function just responds to the browser URL
localhost:5000/
:return: the rendered template "home.html"
"""
return render_template("home.html")
if __name__ == "__main__":
app.run(debug=True)
In the Dockerfile I'm exposing the same port:
RUN python3 -m pip install -r requirements.txt
COPY . /app
EXPOSE 5000
Then I run the container as:
sudo docker run -d -p 5000:5000 my_app:latest
and once the container is up, I'm able to acces to app at:
http://localhost:5000
Now, I'm trying to change to port 5100, for that I'm changing:
a) In the Dockerfile:
COPY . /app
EXPOSE 5100
...
b) When I run the container:
sudo docker run -d -p 5100:5100 my_app:latest
But when I try to visit: http://localhost:5100/
The app is not running there
When I do Docker ps this is shown:
EDIT:
I tried changing the flask app:
app.run(host='0.0.0.0', port=5100)
Still not working, this is the screenshot from docker ps:
Not sure if the error is because still says 5000: at the begining:
5000/tcp, 0.0.0.0:5100->5100/tcp romantic_fermi
This is what I get from docker logs...
* Serving Flask app "server" (lazy loading)
* Environment: production
WARNING: Do not use the development server in a production environment.
Use a production WSGI server instead.
* Debug mode: on
* Running on http://0.0.0.0:5000/ (Press CTRL+C to quit)
You could technically change the default port assigned to the Flask object, but it's simpler to just change the docker mapping.
When you run a command like this:
$ docker run -d -p 5100:5100 my_app:latest
You are saying that you want to forward a port from inside the container (on the right) to your host machine (on the left).
# Left side is your host machine
# Right side is inside of the container
5100:5100
So you could update your run to map to 5000 inside of the container:
$ docker run -d -p 5100:5000 my_app:latest
Then you'll be able to access via http://localhost:5100
PS: If you haven't used docker-compose before, I would highly recommend setting it up after you've worked through this issue. It'll make your life easier in general.
On your .py script ou need to set 5100 port with:
app.run(debug=True,host='0.0.0.0', port=5100)
Everything else you did is correct!
If still your python are listening on port 5000, probably it's the old version.

Categories

Resources