skip to Main Content

I have a basic REST API implemented in Flask. I want to try using Docker to containerize it. I’m completely new to Docker, but based on what I was able to figure out on various forums, this is what I have set up.

Dockerfile

FROM python:3.11
WORKDIR     /app
COPY ./requirements.txt /app/requirements.txt
COPY .env /app/.env
COPY . /app
RUN python3 -m pip install -r /app/requirements.txt

EXPOSE 5000
ENTRYPOINT ["python3"]
CMD ["app.py", "--host=0.0.0.0"]

requirements.txt

aiohttp==3.8.6
aiohttp-retry==2.8.3
aiosignal==1.3.1
async-timeout==4.0.3
attrs==23.1.0
blinker==1.6.3
certifi==2023.7.22
charset-normalizer==3.3.1
click==8.1.7
distlib==0.3.7
filelock==3.12.4
Flask==2.3.0
Flask-Cors==4.0.0
flask-marshmallow==0.14.0
Flask-MySQL==1.5.2
Flask-MySQLdb==2.0.0
Flask-SQLAlchemy==3.1.1
frozenlist==1.4.0
idna==3.4
itsdangerous==2.1.2
Jinja2==3.1.2
MarkupSafe==2.1.3
marshmallow-sqlalchemy==0.29.0
multidict==6.0.4
mysqlclient==2.2.0
packaging==23.2
platformdirs==3.11.0
PyJWT==2.8.0
PyMySQL==1.1.0
python-dotenv==1.0.0
requests==2.31.0
six==1.16.0
SQLAlchemy==2.0.22
twilio==8.10.0
typing_extensions==4.8.0
urllib3==2.0.7
virtualenv==20.24.5
Werkzeug==3.0.0
yarl==1.9.2

app.py

from flask import Flask, request, jsonify, json
from flask_sqlalchemy import SQLAlchemy
from sqlalchemy.event import listens_for
from flaskext.mysql import MySQL
from flask_cors import CORS
from dataclasses import dataclass
from sqlalchemy import text
from urllib.parse import quote

app = Flask(__name__)
CORS(app, origins=["http://localhost:3000", "http://localhost:3000"])


db = SQLAlchemy()

mysql =MySQL()

@dataclass
class User(db.Model):

    __tablename__ = 'user'

    id = db.Column(db.Integer, primary_key=True)
    firstname = db.Column(db.String(46), nullable=False)#1
    lastname = db.Column(db.String(46), nullable=False)#1
    

    def __init__(self, firstname, lastname):
        self.firstname = firstname
        self.lastname = lastname

    def as_dict(self):
        excluded_fields = ['id']
        return {field.name:getattr(self, field.name) for field in self.__table__.c if field.name not in excluded_fields}

@dataclass
class User(db.Model):

    __tablename__ = 'user'
    __table_args__ = {'extend_existing': True} 

    id = db.Column(db.Integer, primary_key=True)
    firstname = db.Column(db.String(46), nullable=False)#1
    lastname = db.Column(db.String(46), nullable=False)#1

    def __init__(self, firstname, lastname):
        self.firstname = firstname
        self.lastname = lastname

    def as_dict(self):
        excluded_fields = ['id']
        return {field.name:getattr(self, field.name) for field in self.__table__.c if field.name not in excluded_fields}



app.config['SQLALCHEMY_DATABASE_URI'] = 'mysql://username:[email protected]/test'

db.init_app(app)
with app.app_context():
    db.create_all()


@app.route('/users', methods=['GET'])
def get_user():
    users = User.query.all()
    return jsonify(users)

@app.route('/user/<firstname>', methods=['GET'])
def user_byfirstname(firstname):
    user = User.query.filter_by(firstname = firstname).first()
    return jsonify(user.as_dict())

if __name__ == '__main__':
   app.run(debug=True)

App tree:

enter image description here

I then go to the Terminal, and run $ docker build -t myapp:latest .

The build is successful, and I can see my app listed in the Docker Desktop app

I then run

$ docker run --rm -it -p 8080:5000 myapp:latest
 * Running on http://127.0.0.1:5000
Press CTRL+C to quit
 * Restarting with stat
/usr/local/lib/python3.11/site-packages/flask_sqlalchemy/model.py:144: SAWarning: This declarative base already contains a class with the same class name and module name as __main__.User, and will be replaced in the string-lookup table.
  super().__init__(name, bases, d, **kwargs)
 * Debugger is active!
 * Debugger PIN: 581-248-767

Docker Desktop shows myapp is in use.

So far, so good. But this is where I start running into issues.

From host machine (through Postman) I cannot access the app on port 8080 with:

127.0.0.1:8080/users 

Postman throws the error : Error: read ECONNRESET

I really don’t know what to do or where to go from here, every source I’ve tried has given me a slight variation on what I have already attempted, and I’m no closer to getting this to work. Please help, thanks.

2

Answers


  1. Use gunicorn or another production grade WSGI server to start flask apps.
    https://flask.palletsprojects.com/en/3.0.x/deploying/gunicorn/

    https://flask.palletsprojects.com/en/3.0.x/deploying/

    command should be something like:
    /path/to/python/install/or/venv/python -m gunicorn -b :5000 –access-logfile – –error-logfile – wsgi:app

    Login or Signup to reply.
  2. The output of the Flask server points out that its only running on the containers local network. (127.0.0.1) and not on all interfaces.

    That is because you are running the flask development server with python3 app.py --host=0.0.0.0. When you run it like that, Flask run with the default values and with the values given through the function app.run().

    The recommended way is to use the flask cli command to run your app so you can configure flask externally and not hard code variables or modify your app.run() with the variables you need.

    • The recommended way would be to change your dockerfile and use flask cli like this:

      FROM python:3.11-alpine
      # Virtual envs not needed inside container
      WORKDIR /app
      COPY ./requirements.txt /app/requirements.txt
      COPY . /app
      RUN python3 -m pip install -r /app/requirements.txt
      
      EXPOSE 5000
      CMD ["flask", "--app=app", "run", "--host=0.0.0.0"]
      
    • The other option is modifying your app.run() and add the host parameter:

      if __name__ == '__main__':
          app.run(debug=True, host="0.0.0.0")
      
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search