Posted on Thursday, March 3, 2016 by Sam
Part 1: Python, WSGI and IIS
In order to save some money for a client of mine I decided to run their low traffic website on my home server to avoid paying hosting costs. (They also don't need their site to have 99% uptime so if I lose power at home it's OK. 🙂) The website is a Python application using the Django web framework. I first wrote the site back in 2010 and only made minor modifications to it over the years.
The first hurdle is that the server I want to run this on is a Windows box running IIS. I had been running the site on a Linux server in production for all these years using Apache and mod_wsgi to execute the Python app.
Fortunately, Microsoft supports running Python WSGI apps on IIS and has provided an IIS-Python bridge module available here.
To install with pip type the following at a command prompt. (You might need administrator privileges to do this.)
pip install wfastcgi
And also enable it. (These instructions are available on the Python Package Index page as well.)
Here is an example of the web.config. In my experience I have found that he APPINSIGHTS_INSTRUMENTATIONKEY setting caused the IIS worker process to immediately consume 99% of the CPU on the first request to the site. For that reason I comment it out or remove it from the file since I did not have plans on using that feature anyway.
<configuration> <system.webserver> <handlers> <add name="Python FastCGI" path="*" verb="*" modules="FastCgiModule" scriptprocessor="C:\Python27\python.exe|C:\Python27\Lib\site-packages\wfastcgi.py" resourcetype="Unspecified" requireaccess="Script"></add> </handlers> </system.webserver> <appsettings> <!-- Required settings --> <add key="WSGI_HANDLER" value="my_app.wsgi_app"></add> <add key="PYTHONPATH" value="C:\MyApp"></add> <!-- Optional settings --> <add key="WSGI_LOG" value="C:\Logs\my_app.log"></add> <add key="WSGI_RESTART_FILE_REGEX" value=".*((\.py)|(\.config))$"></add> <!--<add key="APPINSIGHTS_INSTRUMENTATIONKEY" value="__instrumentation_key__" />--> <!--<add key="DJANGO_SETTINGS_MODULE" value="my_app.settings" />--> </appsettings> </configuration>
If this is a Django site also add a key for DJANGO_SETTINGS_MODULE (commented out here) in order to tell the app where the Django settings module lives.
The PYTHONPATH value should be pointing at the folder where the Python application module lives and any other folders that contain Python modules that need to be referenced and aren't already on the PATH. For example, if you are using virtualenv and have a site-packages folder with your dependencies in there. e.g.
Check your IIS Handler mappings:
If you see an entry for Python then you're golden!
Example WSGI app for testing:
def application(environ, start_response): start_response('200 OK', [('Content-Type', 'text/html')]) return ["<h1>Hi there!</h1>"]
Place this code in a file called pytest.py and place the file in the web root of your web application.
The web.config in this instance would look like this:
<?xml version="1.0" encoding="UTF-8"?> <configuration> <system.webserver> <handlers> <add name="Python FastCGI" path="*" verb="*" modules="FastCgiModule" scriptprocessor="C:\Python27\python.exe|C:\Python27\Lib\site-packages\wfastcgi.pyc" resourcetype="Unspecified" requireaccess="Script"></add> </handlers> </system.webserver> <appsettings> <!-- Required settings --> <add key="WSGI_HANDLER" value="pytest.application"></add> <add key="PYTHONPATH" value="C:\Dev\pytest"></add> <!-- Optional settings --> <!-- <add key="WSGI_LOG" value="C:\dev\pytest\logs\wsgi.log" /> --> <add key="WSGI_RESTART_FILE_REGEX" value=".*((\.py)|(\.config))$"></add> </appsettings> </configuration>
Logging is optional but a good idea for a production site. Just make sure that the user the site's worker process is running under has permission to write to the logging folder. For example, my site is running under the App Pool Identity so I gave the IIS_IUSRS group write access to the log folder.
If it is configured incorrectly you will see an error message. But thankfully the WSGI from Microsoft will provide a nice text/plain response with the stack trace for you. That's much more useful than seeing this:
Part 2: Django
Coming soon... Maybe.