I'm slowly trying to make my Django web servers conform to current best practices. I've set up an Nginx reverse proxy for serving static files, started using virtualenv to isolate my Python environments, and migrated my database to PostgreSQL. I ultimately want to implement memcached+Nginx caching in my reverse proxy, but the next task on my to-do list is switching from mod_python to mod_wsgi.
Within the past year (or maybe before), mod_wsgi has become the preferredmethod for serving Django applications. I also originally thought switching from mod_python to mod_wsgi would save me some much needed memory on my 256MB VPS. But after trying it out, running with a single Apache process in each case, the memory footprint was about the same. Even switching from mod_wsgi's embedded mode to daemon mode didn't make a significant difference. Likely the performance is better with mod_wsgi, though.
Here are my notes on installing mod_wsgi.
Configuration References
- Django docs: How to use Django with Apache and mod_wsgi
- mod_wsgi docs: Quick Configuration Guide
- A good blog article: Configs for nginx and Apache with mod_wsgi
Advice from mod_wsgi author Graham Dumpleton
- Load spikes and excessive memory usage in mod_python
- Using mod_wsgi when developing Django sites
- http://groups.google.com/group/django-users/browse_thread/thread/6d670b0fa7c0d733/4ff111a1f00f7629?q=worker+daemon#4ff111a1f00f7629
- http://groups.google.com/group/modwsgi/browse_thread/thread/cb21864a97d44ee9/38716433921a48cb?q=worker+daemon#38716433921a48cb
Install mod_wsgi and apache mpm-worker
I'm not 100% sure about prefork vs. worker mpm, but Graham Dumpleton favors worker mpm.
sudo apt-get install libapache2-mod-wsgi sudo apt-get install apache2-mpm-worker
Create .wsgi application file
My virtualenv is located at /srv/python-environments/saltycrane
.
My Django settings files is at /srv/SaltyCrane/iwiwdsmi/settings.py
.
/srv/SaltyCrane/saltycrane.wsgi
:
import os import sys import site site.addsitedir('/srv/python-environments/saltycrane/lib/python2.5/site-packages') os.environ['DJANGO_SETTINGS_MODULE'] = 'iwiwdsmi.settings' sys.path.append('/srv/SaltyCrane') import django.core.handlers.wsgi application = django.core.handlers.wsgi.WSGIHandler()
Edit Apache's httpd.conf file
I went back and forth between using embedded mode or daemon mode.
I've ended up with embedded mode for now since it seems to use a tad less
memory and is supposed to be a little bit faster. However, Graham Dumpleton seems to
recommend daemon mode for people on VPSs. I may change my mind again later.
To use daemon mode, I just need to uncomment the WSGIDaemonProcess
and WSGIProcessGroup
lines.
I have StartServers
set to 1 because I can only afford to have
one Apache process running.
This is assuming nginx is proxying requests to apache. For more
on my nginx setup, see
here.
Edit /etc/apache2/httpd.conf
:
<IfModule mpm_worker_module> StartServers 1 ServerLimit 1 ThreadsPerChild 5 ThreadLimit 5 MinSpareThreads 5 MaxSpareThreads 5 MaxClients 5 MaxRequestsPerChild 500</IfModule> KeepAlive Off NameVirtualHost 127.0.0.1:8080 Listen 8080 <VirtualHost 127.0.0.1:8080> ServerName www.saltycrane.com # WSGIDaemonProcess saltycrane.com processes=1 threads=5 display-name=%{GROUP} # WSGIProcessGroup saltycrane.com WSGIScriptAlias / /srv/SaltyCrane/saltycrane.wsgi</VirtualHost><VirtualHost 127.0.0.1:8080> ServerName supafu.com # WSGIDaemonProcess supafu.com processes=1 threads=5 display-name=%{GROUP} # WSGIProcessGroup supafu.com WSGIScriptAlias / /srv/Supafu/supafu.wsgi</VirtualHost><VirtualHost 127.0.0.1:8080> ServerName handsoncards.com # WSGIDaemonProcess handsoncards.com processes=1 threads=5 display-name=%{GROUP} # WSGIProcessGroup handsoncards.com WSGIScriptAlias / /srv/HandsOnCards/handsoncards.wsgi</VirtualHost>
Restart Apache
sudo /etc/init.d/apache2 restart