Ubuntu 18.04.1, ISPConfig3, Python 3, Flask, Apache 2, and mod_wsgi
Today I spent a lot of time trying to figure out how to get a Flask application started using Python 3 on Ubuntu 18.04.1. I had previously built an application using Python 2.7, Flask, and mod_wsgi, but it had been a while and the documentation I came across just wasn't connecting the dots properly. Here's my notes after the endeavor.
For the example, the root directory of the ISPConfig3 user is going to be /var/www/clients/client1/web1/.
# install
sudo apt install libapache2-mod-wsgi-py3 python3-pip
# disable mod_python if it's installed, or just uninstall it as mod_wsgi can't be loaded at the same time
# install python3 virtualenv globally
sudo pip3 install virtualenv
# change to the private folder where we'll create the project
cd /var/www/clients/client1/web1/private/
# create the project folder and change into it
mkdir project1 && cd project1
# create the virtual environment in the env directory, which will be created for us
virtualenv env
# activate the virtual environment
source env/bin/activate
# install flask
pip install flask
# setup some other directories and empty files
mkdir app log
touch wsgi.py run.py config.py app/__init__.py
wsgi.py
import sys
sys.path.insert(0, "/var/www/clients/client1/web1/private/project1")
from app import app as application
run.py
# WSGI Server for Development
# Use this during development vs. apache. Can view via [url]:8001
# Run using virtualenv. 'env/bin/python run.py'
from app import app
app.run(host='0.0.0.0', port=8001, debug=True)
app/__init__.py
from flask import Flask
app = Flask(__name__)
# Configurations
#app.config.from_object('config')
@app.route('/')
def hello_world():
return 'Hello, World!'
Apache virtual host configurationconfiguration. Because of the way the ISPConfig3 vhost template file is configured, I had to invert my logic and use IfDefine !ProtocolHTTPS instead of IfDefine ProtocolHTTP.
<VirtualHost *:80>
Define ProtocolHTTP
ServerName test.domain.com
ServerAdmin webmaster@test.domain.com
ErrorLog /var/log/ispconfig/httpd/test.domain.com/error.log
<IfDefine ProtocolHTTP!ProtocolHTTPS>
WSGIDaemonProcess client1web1 python-home=/var/www/clients/client1/web1/private/project1/env python-path=/var/www/clients/client1/web1/private/project1
</IfDefine>
WSGIProcessGroup client1web1
WSGIScriptAlias / /var/www/clients/client1/web1/private/project1/wsgi.py
WSGIPassAuthorization On
<Directory /var/www/clients/client1/web1/private/project1>
WSGIProcessGroup client1web1
WSGIApplicationGroup %{GLOBAL}
WSGIScriptReloading On
Require all granted
</Directory>
</VirtualHost>
<VirtualHost *:443>
Define ProtocolHTTPS
ServerName test.domain.com
ServerAdmin webmaster@test.domain.com
ErrorLog /var/log/ispconfig/httpd/test.domain.com/error.log
<IfModule mod_ssl.c>
SSLEngine on
SSLCertificateFile /var/www/clients/client1/web1/ssl/test.domain.com-le.crt
SSLCertificateKeyFile /var/www/clients/client1/web1/ssl/test.domain.com-le.key
SSLCertificateChainFile /var/www/clients/client1/web1/ssl/test.domain.com-le.bundle
</IfModule>
# The WSGIDaemonProcess directive can only defined once, and you don't want two daemons for the same application.
# This is currently what is preventing my from being able to configure this type of application from the ISPConfig3 web interface.
# I've attempted to use the IfDefine conditional but I still get the same error message "Name duplicates previous WSGI daemon definition."
# I don't think the parser is smart enought to realize that its not actually going to use this directive because in this VirtualHost we've define
# ProtocolHTTPS, NOT ProtocolHTTP.
#<IfDefine ProtocolHTTP!ProtocolHTTPS>
#WSGIDaemonProcessWSGIDaemonProcess client1web1 python-home=/var/www/clients/client1/web1/private/project1/env python-path=/var/www/clients/client1/web1/private/project1
#</IfDefine>
WSGIProcessGroup client1web1
WSGIScriptAlias / /var/www/clients/client1/web1/private/project1/wsgi.py
WSGIPassAuthorization On
<Directory /var/www/clients/client1/web1/private/project1>
WSGIProcessGroup client1web1
WSGIApplicationGroup %{GLOBAL}
WSGIScriptReloading On
Require all granted
</Directory>
UnDefine ProtocolHTTPS
</VirtualHost>
Diff of the vhost.conf.master file.
# diff -u vhost.conf.master.1 vhost.conf.master
--- vhost.conf.master.1 2018-08-23 03:50:52.539521052 -0500
+++ vhost.conf.master 2018-08-23 03:43:36.470905313 -0500
@@ -12,6 +12,9 @@
<tmpl_loop name='vhosts'>
<VirtualHost {tmpl_var name='ip_address'}:{tmpl_var name='port'}>
+<tmpl_if name='ssl_enabled'>
+Define ProtoHTTPS
+</tmpl_if>
<tmpl_hook name='apache2_vhost:vhost_header'>
<tmpl_if name='php' op='==' value='suphp'>
DocumentRoot <tmpl_var name='web_document_root'>
@@ -524,6 +527,9 @@
<tmpl_var name='apache_directives'>
<tmpl_hook name='apache2_vhost:vhost_footer'>
+<tmpl_if name='ssl_enabled'>
+UnDefine ProtoHTTPS
+</tmpl_if>
</VirtualHost>
<tmpl_if name='apache_version' op='>=' value='2.4' format='version'>
Still to do:
- Figure out how to configure this properly using just the ISPConfig3 control panel. I can make it work editing the config files by hand, but they eventually get overwritten by ISPConfig3. The big problem I have right now is the WSGIDaemonProcess directive can only exist once... not in both the http and https VirtualHost definitions. This was resolved as shown above.
Source material: