Deploying Flash + Gunicorn + Nginx + https + domain
By Javier Ideami - March 2, 2020
The jupyter notebook to train the model used by the flask app can be accessed here: Jupyter Notebook
The objective of this project is to deploy a Flask app that uses a model trained with the Fast.ai v2 library following an example in the upcoming book "Deep Learning for Coders with fastai and PyTorch: AI Applications Without a PhD" by Jeremy Howard and Sylvain Gugger.
The most important part of the project is testing a deployment process that combines a Flask app, the Gunicorn server, the Nginx server and a custom domain name with an SSL certificate, all installed on a dedicated server.
Below I explain the different deployment stages to deploy this repo combining the pieces mentioned above.
This workflow has been tested on:
Server OS: Centos 7
Name of your flask app: app
User installing this: root (you can use any other)
_________________________________________________
Install Python and nginx
If python and ningx have not been installed:
sudo yum install epel-release
sudo yum install python-pip python-devel gcc nginx
_________________________________________________
Install virtualenv
sudo pip install virtualenv
_________________________________________________
Clone our repo or create it
Create your flask app, or clone a repo where you already have it
_________________________________________________
Create a virtual environment inside the repo
virtualenv myprojectenv
source myprojectenv/bin/activate
_________________________________________________
Install flask, Gunicorn and any other dependencies
pip install gunicorn flask
Or more generally: pip install -r requirements.txt
_________________________________________________
Give instructions to the Gunicorn server to find our app
Create a wsgi entry point, file wsgi.py in the root of the repo
The file contains just this:
from app import application
if __name__ == "__main__":
application.run()
_________________________________________________
Create a service to automatically start the gunicorn server and our app when server starts
Create a systemd unit file called app.service in /etc/systemd/system
It contains this:
[Unit]
Description=Gunicorn instance to serve my app
After=network.target
[Service]
User=root
Group=nginx
WorkingDirectory=path-to-your-app
Environment="PATH=path-to-your-app/myprojectenv/bin"
ExecStart=path-to-your-app/myprojectenv/bin/gunicorn --workers 3 --bind unix:app.sock -m 007 wsgi
[Install]
WantedBy=multi-user.target
_________________________________________________
Start the system process which will create a unix socket file in our app folder and bind to it.
sudo systemctl start app
sudo systemctl enable app
_________________________________________________
Configure nginx to proxy web requests
If you are using plesk, go to the nginx settings of the domain where you want to install the app, and select the option that makes nginx not work as a proxy of apache, so that it works standalone.
Open the nginx configuration file of your server, or of the domain where you want to put the app.
If you are using plesk, you will find the file here:
/var/www/vhosts/system/domain/conf/nginx.conf
You can add a brand new section above the standard one:
server {
listen 80;
server_name server_domain_or_IP;
location / {
proxy_set_header Host $http_host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_pass http://unix:path-to-your-app/app.sock;
}
}
Or you can also just add the location section to an existing server section.
This would be the beginning of a server section for the https ssl access of the domain:
server {
listen x.x.x.x:443 ssl http2;
server_name whatever.com;
server_name www.whatever.com;
server_name ipv4.whatever.com;
…………………
And this would be the beginning of a server section for the http access of the domain:
server {
listen x.x.x.x:80;
server_name whatever.com;
server_name www.whatever.com;
server_name ipv4.whatever.com;
…………………
And you could add just the location part inside one of them:
location / {
proxy_set_header Host $http_host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_pass http://unix:path-to-your-app/app.sock;
}
This way you point either the root of the domain or another path within the domain to the flask app.
_________________________________________________
Give nginx permissions if necessary
If you are not using the root user, you may have to give permissions to nginx by doing:
sudo usermod -a -G user nginx
chmod 710 /home/user (or wherever the app is installed)
_________________________________________________
Test nginx conf file
Test that the syntax of your conf file changes are correct: sudo nginx -t
_________________________________________________
Launch or relaunch nginx
sudo systemctl stop nginx
sudo systemctl start nginx
sudo systemctl enable nginx
sudo systemctl status nginx
_________________________________________________
Now you can go to your domain address and access the app
Dockerizing bear app
You can dockerize your bear app.
This allows you to quickly launch it in any environment or operating system, making it truly portable.
To dockerize the app, create a Dockerfile at the root of the project:
FROM python:3
WORKDIR /usr/src/app
COPY requirements.txt ./
RUN pip install --no-cache-dir -r requirements.txt
COPY model ./model/
COPY resources/utils.py ./resources/
COPY *.py ./
COPY templates ./templates/
RUN mkdir resources/tmp
CMD ["gunicorn" , "-b", "0.0.0.0:8500", "wsgi"]
Then create a docker-compose.yml file
version: '3.3'
services:
app:
image: javismiles/beardetector:latest
build:
context: .
ports:
- 8500:8500
Delete existing containers
docker container rm -f $(docker container ls -aq)
Build the image and launch it all at once with docker-compose
docker-compose up -d --build
At this point you can access the app on:
Attach the app to a domain with an ssl certificate
If you want to attach the app to domain name with an ssl certificate you can do the same we did in the deployment instructions for Flask and Gunicorn, editing the nginx.conf file of the domain name you want to use.
Upload the image to your account un hub.docker.com, so that you can pull it from anywhere else and launch it anywhere else:
docker image push javismiles/beardetector:latest
You can pull the example image I created (or another that you create and upload to hub.docker.com) doing this:
docker image pull javismiles/beardetector:latest
And run it with:
docker container run -d --rm -p 8500:8500 javismiles/beardetector:latest
Then you can access it on: