A Docker image contains the code used to create a Docker container, such as creating a Nginx web server, or a mySQL server, or a home grown app, and the list goes on. In this way, an image is like a template used to create a container. An image is kind of like a virtual machine, but much more light weight, using significantly less storage a memory (containers are usually megabytes in size).
There are two Docker repo's that are commonly used for Flask:
- tiangolo/uwsgi-nginx-flask
- tiangolo/meinheld-gunicorn-flask (this article)
According to https://hub.docker.com/r/tiangolo/uwsgi-nginx-flask:
"tiangolo/meinheld-gunicorn-flask will give you about 400% (4x) the performance of this tiangolo/uwsgi-nginx-flask"
So unless there is some reason you must use tiangolo/uwsgi-nginx-flask, I would go with tiangolo/meinheld-gunicorn-flask.
When I would try to start a container using the meinheld-gunicorn-flask image, I was getting this traceback.
[Traceback (most recent call last):
File "/usr/local/lib/python3.9/site-packages/gunicorn/util.py", line 71, in load_class
return pkg_resources.load_entry_point(dist, section, name)
File "/usr/local/lib/python3.9/site-packages/pkg_resources/__init__.py", line 474, in load_entry_point
return get_distribution(dist).load_entry_point(group, name)
File "/usr/local/lib/python3.9/site-packages/pkg_resources/__init__.py", line 2846, in load_entry_point
return ep.load()
File "/usr/local/lib/python3.9/site-packages/pkg_resources/__init__.py", line 2449, in load
self.require(*args, **kwargs)
File "/usr/local/lib/python3.9/site-packages/pkg_resources/__init__.py", line 2472, in require
items = working_set.resolve(reqs, env, installer, extras=self.extras)
File "/usr/local/lib/python3.9/site-packages/pkg_resources/__init__.py", line 777, in resolve
raise VersionConflict(dist, req).with_context(dependent_req)
pkg_resources.VersionConflict: (greenlet 3.0.0 (/usr/local/lib/python3.9/site-packages), Requirement.parse('greenlet<0.5,>=0.4.5'))
]
{"loglevel": "info", "workers": 4, "bind": "0.0.0.0:80", "workers_per_core": 2.0, "host": "0.0.0.0", "port": "80"}
According to https://github.com/tiangolo/meinheld-gunicorn-docker/issues/22, greenlet version 0.4.5 or higher up to version 0.5 must be installed so it probably makes sense to create a Dockerfile that includes greenlet, perhaps something like this.
FROM tiangolo/meinheld-gunicorn-flask:python3.9
RUN apt-get update -y
RUN pip install --upgrade pip
RUN pip install greenlet==3.0.0
It is also noteworthy that I was able to get a container up and running just fine with just these minimal settings. But when I tried to create a container with Flask-SQLAlchemy version 3.0.5, the container failed to start because Flask-SQLAlchemy version 3.0.5 includes greenlet version 3.0.0, and the meinheld-gunicorn-flask image is incompatible with greenlet version 3.0.0.
Then use the docker build command to create the image, running this command in the same directory as the Dockerfile.
docker build --tag meinheld-gunicorn-flask-python3.9 .
The docker images command can be used to display the image.
~]$ sudo docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
tiangolo/meinheld-gunicorn-flask python3.9 9b7b1a3b4eff 4 days ago 1.01GB
Create a file somewhere on your Docker system such as /usr/local/docker/flask/main.py that contains the following.
from flask import Flask
app = Flask(__name__)
@app.route("/")
def hello():
return "Hello World"
if __name__ == "__main__":
app.run(host='0.0.0.0', debug=True, port=80)
The following command can then be used to create and start the ngninx container. Let's break down this command.
- The docker run command is used to create and start the nginx container.
- The --detach flag is used to run the container in the background.
- The --publish option is used both the Docker server and nginx container to listen on HTTP port 80, which adds a rule to iptables to allow connections between the Docker system and container on port 80.
- The --volume option is used to mount the main.py file on your Docker system to /app/main.py in the container.
- The --name option is used to give the container a specific name.
- The --restart unless-stopped option is used so that the container is started if the Docker server is restarted
- The meinheld-gunicorn-flask image is used.
sudo docker run --detach --restart unless-stopped --publish 0.0.0.0:80:80 --volume /usr/local/docker/main.py:/app/main.py --name flask tiangolo/meinheld-gunicorn-flask:latest
The docker container ls command can be used to ensure the container is running.
~]$ sudo docker container ls
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
be0cc8e9aa4d meinheld-gunicorn-flask "/entrypoint.sh /sta…" About a minute ago Up About a minute 443/tcp, 0.0.0.0:80->80/tcp flask
The docker logs command should return something like this.
~]# sudo docker logs uwsgi-nginx-flask
2022-12-17 03:47:14,238 INFO success: quit_on_failure entered RUNNING state, process has stayed up for > than 1 seconds (startsecs)
If running Docker on a Linux system, ensure connections are allowed in firewalld or iptables on ports 80 and 443.
firewall-cmd --add-service=http --permanent
firewall-cmd --add-service=https --permanent
firewall-cmd --reload
If running Docker on an Amazon Web Services (AWS) EC2 Instance, ensure the Security Group Inbound Rules allow connections on ports 80 and 443.
You should then be able to access main.py should be returned to return Hello World at http://<hostname or IP address of your Docker system>.
Did you find this article helpful?
If so, consider buying me a coffee over at