Server Requirements
Command:
sudo yum install epel-release
sudo yum install python-pip python-devel gcc nginx python36
This installs the centos7 packages and the base packages to get pipenv running. I am not Pipenv is a game changer over Virtualenv but it is a good time.
Create Pipenv
Command:
python36 -m pip install pipenv
python36 -m pipenv install gunicorn flask
This installs pipenv packages gunicorn and flask. Put any other packages you need into the 2nd command. I am just doing a base flask app so it only needs this in the virtual environment.
Pipenv Environment location
Command:
cd /home/user/.local/share/virtualenvs/flask-test-Ve2HkatZ
source bin/activate
mkdir ~/projectname
cd ~/projectname
Pipenv by default saves the location of the virtual environment in a random environment like the first command where the user is the user that did the pipenv install command. If you issue pipenv shell it should put you in this same environment or you can issue the source /home/user/.local/share/virtualenvs/flask-test-Ve2HkatZ/bin/activate to get in this environment. Next I created a directory for the flask project and move to it.
No more requirements.txt, in comes Pipfile
Command:
vi Pipfile
The Pipfile is similar to the requirements.txt, you can add all of your packages with the versions to this and then just issue a pipenv install and it will make sure all these packages are added. The lock file will also be added to keep a hashing of your packages to make sure they are the same. Is this a security feature? Sure, I guess?
Load Flask Files
Command:
vi myproject.py
This is where we will load in our flask config. You can put it where you want but I put it in my flask app users directory I have built for this project. You can do it by placing the info below.
myproject.py
###Paste this in for a very basic flask experience###
from flask import Flask
application = Flask(__name__)
@application.route("/")
def hello():
return "<h1 style='color:blue'>Hello There!</h1>"
if __name__ == "__main__":
application.run(host='0.0.0.0')
Create the wsgi.py for Gunicorn
Command:
vi wsgi.py
This will be the file that gunicorn uses to serve to nginx. In this template the from is whatever your project.py is. Follow the template below:
wsgi.py
###Paste the below into wsgi.py###
from myproject import application
if __name__ == "__main__":
application.run()
Test that gunicorn can serve this content
Command:
gunicorn --bind 0.0.0.0:8000 wsgi
You Pipenv needs to be activated for this to work and the command needs to be ran from where the wsgi.py and flask app are located.
Create Service
Command:
vi /etc/systemd/system/projectname.service
Whatever you name this file will but what systemd starts the service up as. Follow the template below to build this service. It will need to follow to the location of pipenv environment.
project.service
###Paste the below into project.service###
[Unit]
Description=Gunicorn instance to serve myproject
After=network.target
[Service]
User=ProjectUser
Group=nginx
WorkingDirectory=/home/user/project
Environment="PATH=/home/user/.local/share/virtualenvs/flask-test-Ve2HkatZ/bin"
ExecStart=/home/user/.local/share/virtualenvs/flask-test-Ve2HkatZ/bin/gunicorn --workers 3 --bind unix:project.sock -m 007 wsgi
[Install]
WantedBy=multi-user.target
Set the permissions on the project directory
Command:
chown -R user:nginx /home/user/project
Make sure the user running the service and nginx has permissions to access the unix socket. Gunicorn make this socket and then nginx will need to be pointed to it.
Start the service
Command:
systemctl start project
systemctl status project
systemctl enable project
Start the project, check to make sure there are no errors and then enable it for server restarts. This will allow the socket to be built for nginx to serve. We do not have to do a unix socket we can feed it over tcp but I find that it is better performance with the unix socket.
Configure the Nginx Server Block
Command:
vi /etc/nginx/nginx.conf
Open the default nginx server block. It will need to scroll down to where you find the server block and match the template below changing where the proxy looks for the socket to where you have told gunicorn to create it.
nginx.conf server block
###Paste the below into the server block of nginx###
server {
add_header X-Frame-Options "SAMEORIGIN";
add_header X-XSS-Protection "1; mode=block";
server_name yourdomain.com;
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:/home/user/project/project.sock;
}
}
Reload nginx
Command:
sudo systemctl restart nginx
If all went well nginx should restart. If not check the /var/log/nginx/error.log or wherever you are storing nginx errors.
Conclusion
Command:
curl yourdomain.com
You should have a functioning basic flask applicaiton telling you Hello There in the most hideous blue font. If not make sure your firewall rules are set and dns is working for the domain you have set in the nginx server block. Good luck!