I still get questions from time to time about how to deploy a python web application using Apache and not NGINX. Here is a quick tutorial to deploy your Flask application on Ubuntu 16.04 or any linux distribution (considering relevant changes) using Apache, Gunicorn and systemd. Until some weeks ago I used supervisord
instead of systemd
but nowadays I prefer to use systemd
because is already there, installed, part of system. And also the reason to look into systemd
and to switch was that I had to deploy an application on SLES (SUSE Linux Enterprise Server) and there is no supervisord
package available in repos.
Note: This is a very basic configuration to get everything running. It is just for learning and to get the idea how everything is connected.
So, let's start:
- we create a user which will run our Flask application
# adduser flaskappuser
- install and configure apache:
# apt-get install apache2
As result we should get the default apache webpage in browser (http://your-ip-here
)
- we have to enable proxy modules for apache:
# a2enmod
and give this list of modules to enable:
proxy proxy_ajp proxy_http rewrite deflate headers proxy_balancer proxy_connect proxy_html
- Add our application to apache web server config file. Add the following lines (inside
VirtualHost
block) to/etc/apache2/sites-available/000-default.conf
. Make a backup of this file before you modify it
<Proxy *>
Order deny,allow
Allow from all
</Proxy>
ProxyPreserveHost On
<Location "/flaskapp">
ProxyPass "http://127.0.0.1:5000/"
ProxyPassReverse "http://127.0.0.1:5000/"
</Location>
so, the final file should look like this:
<VirtualHost *:80>
# The ServerName directive sets the request scheme, hostname and port that
# the server uses to identify itself. This is used when creating
# redirection URLs. In the context of virtual hosts, the ServerName
# specifies what hostname must appear in the request's Host: header to
# match this virtual host. For the default virtual host (this file) this
# value is not decisive as it is used as a last resort host regardless.
# However, you must set it for any further virtual host explicitly.
#ServerName www.example.com
ServerAdmin webmaster@localhost
DocumentRoot /var/www/html
# Available loglevels: trace8, ..., trace1, debug, info, notice, warn,
# error, crit, alert, emerg.
# It is also possible to configure the loglevel for particular
# modules, e.g.
#LogLevel info ssl:warn
ErrorLog ${APACHE_LOG_DIR}/error.log
CustomLog ${APACHE_LOG_DIR}/access.log combined
# For most configuration files from conf-available/, which are
# enabled or disabled at a global level, it is possible to
# include a line for only one particular virtual host. For example the
# following line enables the CGI configuration for this host only
# after it has been globally disabled with "a2disconf".
#Include conf-available/serve-cgi-bin.conf
<Proxy *>
Order deny,allow
Allow from all
</Proxy>
ProxyPreserveHost On
<Location "/flaskapp">
ProxyPass "http://127.0.0.1:5000/"
ProxyPassReverse "http://127.0.0.1:5000/"
</Location>
</VirtualHost>
- restart apache to see if is working:
# service apache2 restart
http://your-ip-here
—> should give you the same standard html page for apache, as before
http://your-ip-here/flaskapp
—> should give you:
Service Unavailable
The server is temporarily unable to service your request due to maintenance downtime or capacity problems. Please try again later.
that’s because we still don’t have our Flask app running, but it seems that apache is trying to send the request to it, good.
- let’s take care of our Flask app:
we will run it in a virtual environment
, so let’s install virtualenv:
# apt-get install python3-venv
create and activate our new venv:
# cd /home/flaskappuser
# mkdir flaskapp
# cd flaskapp
# python3.5 -m venv flaskvenv
# source flaskvenv/bin/activate
install flask and gunicorn in our venv:
# pip install flask gunicorn
create out simple flask app (/home/flaskappuser/flaskapp/app.py
)
1 2 3 4 5 6 7 8 9 10 11 12 |
|
let’s run our app to see if is working:
(flaskvenv) root@apache-flask:~/flaskapp/# python app.py
* Running on http://127.0.0.1:5000/ (Press CTRL+C to quit)
127.0.0.1 - - [10/Oct/2016 13:58:56] "GET / HTTP/1.1" 200 -
127.0.0.1 - - [10/Oct/2016 13:58:59] "GET / HTTP/1.1" 200 -
in your web browser you should see: Hello from FLASK if you try: http://your-ip-here/flaskapp
- everything is ok right now, but we want to use
Gunicorn
as our application server, so let’s configure it
in the same place where your Flask application is (/home/flaskappuser/flaskapp/
in my case) create a gunicorn.conf
file with the following content:
accesslog = "/home/flaskappuser/flaskapp/logs/gunicorn_access.log"
errorlog = "/home/flaskappuser/flaskapp/logs/gunicorn_error.log"
we have to create our directory where to store the logs files:
# mkdir logs
now we can try to test our app again by running it using gunicorn
:
# gunicorn -c gunicorn.conf -b 0.0.0.0:5000 app:app
check the browser and see if you app is working. Should work 😃
make sure that everything in flaskappuser
home directory belongs to this user
# chown -R flaskappuser:flaskappuser /home/flaskappuser/
Now we have one more step. We want to monitor our Flask app and to restart it on crashing or to have nice start/stop commands for it. Or to have it started automatically on reboot. In order to do that we can use systemd
which is available already in Ubuntu 16.04.
For that we have to create a .service
file for our app. Here is my file: (/etc/systemd/system/flaskapp.service
):
[Unit]
Description=flaskapp
After=network.target
[Service]
User=flaskappuser
Restart=on-failure
WorkingDirectory=/home/flaskappuser/flaskapp/
ExecStart=/home/flaskappuser/flaskapp/flaskvenv/bin/gunicorn -c /home/flaskappuser/flaskapp/gunicorn.conf -b 0.0.0.0:5000 app:app
[Install]
WantedBy=multi-user.target
activate our .service
file
# systemctl daemon-reload
enable it at boot/restart
# systemctl enable flaskapp
start our app
# systemctl start flaskapp
Check if our app is running:
(flaskvenv) root@apache-flask:~/flaskapp# tail -f /var/log/syslog
Oct 10 14:25:59 guest systemd[1]: Started ACPI event daemon.
Oct 10 14:26:03 guest systemd[1]: Started flaskapp.
(flaskvenv) root@apache-flask:~/flaskapp# ps aux | grep gunicorn
flaskappuser 7263 0.2 2.9 64904 22492 ? Ss 14:26 0:00 /home/flaskappuser/flaskapp/flaskvenv/bin/python3.5 /home/flaskappuser/flaskapp/flaskvenv/bin/gunicorn -c /home/flaskappuser/flaskapp/gunicorn.conf -b 0.0.0.0:5000 app:app
check the app in your browser:
http://your-ip-here/flaskapp
you can stop your app with:
# systemctl stop flaskapp
Done!
Note: Here is my repository with the files: GitHub