As I mentioned in my previous post, I'm planning to set up an e-commerce site using the Satchmo shopping cart framework. Satchmo is built on the Django web framework which is written in the Python programming language. Satchmo has a lot of features built in which means it saves you a lot of work implementing them yourself. Check out this video introduction to Satchmo at this year's DjangoCon for more information.
After reading this discussion on the Satchmo mailing list, I decided to use Slicehost for hosting my site. Their cheapest plan provides 256MB of RAM for $20/month. Here are my notes on setting up Satchmo and Django with PostgreSQL, Apache, and mod_python on Ubuntu Intrepid at Slicehost. It seems lighttpd and nginx are popular lightweight alternatives to Apache on VPS setups. I do not know too much about this, but may explore these in the future.
Note, I don't describe how to setup a Satchmo project below. Maybe I will write another post about that later... For more information, see the Satchmo Installation documentation. Update 2008-12-12: I did write another blog post-- see Installing Satchmo, part 2.
- Sign up for an account at http://www.slicehost.com/ I chose Ubuntu Intrepid for my operating system and named my slice "toad".
- I followed the following two excellent Slicehost tutorials: Ubuntu Setup Part 1 and Part 2.
- An important step is setting the system locale. Since I'm in the United States,
I used the following commands (run as root):
locale-gen en_US.UTF-8 update-locale LANG=en_US.UTF-8
Install apache, mod_python, postgresql, and postgres python bindings
- Run as root (or use sudo):
apt-get update apt-get upgrade apt-get dist-upgrade apt-get install apache2 apache2-mpm-prefork apt-get install libapache2-mod-python apt-get install postgresql apt-get install python-psycopg2
- I used the following Slicehost tutorials: Install Apache, Configure Apache Part 1, Configure Apache Part 2
- Of particular note, when installing apache, I got this warning:
apache2: Could not reliably determine the server's fully qualified domain name, using 127.0.0.1 for ServerNameFix it by setting the ServerName in
ServerName yourdomain.com ServerTokens ProdSee the Apache docs for more information on ServerName.
Install Django 1.0.2
- Run the following as root:
cd /srv wget http://www.djangoproject.com/download/1.0.2/tarball/ tar zxvf Django-1.0.2-final.tar.gz mkdir python-packages cd python-packages ln -s ../Django-1.0.2-final/django
Test Django (optional)
- Run the following as root:
export PYTHONPATH=/srv/python-packages /srv/Django-1.0.2-final/django/bin/django-admin.py startproject testdjango
<location "/"> SetHandler python-program PythonHandler django.core.handlers.modpython SetEnv DJANGO_SETTINGS_MODULE testdjango.settings PythonPath "['/srv', '/srv/python-packages'] + sys.path" PythonDebug On </location>
- Restart apache:
/etc/init.d/apache2 restartNavigate to your slice's IP address in your browser and you should see the Django "It worked!" page.
Satchmo requires a number of external Python packages besides Django. Unfortunately, there isn't one standard way of installing Python packages. I decided to install most of them using Ubuntu's APT package management and one of them using easy_install, and one of them from source. Alternatively, I could have installed most of the packages with easy_install, and one of them with APT (and one of them from source). Or, I could have done it another way... sigh.
Note, I use the directory
store symbolic links to my needed python packages. Then I add this
directory to my Python Path in the PYTHONPATH environment variable or in
httpd.conf file. See my post,
Somewhere on your Python path for more information.
- Install Satchmo prerequisites. Run the following commands as root:
Install APT packages:
apt-get update apt-get upgrade apt-get install python-crypto apt-get install python-yaml apt-get install python-imaging apt-get install python-reportlab apt-get install python-trml2pdf
Install django-registration using easy_install:
apt-get install python-setuptools python-dev build-essential easy_install django-registration
Install comment_utils source:
apt-get install subversion mkdir -p /srv/python-packages/dist cd /srv/python-packages/dist svn co http://django-comment-utils.googlecode.com/svn/trunk/comment_utils/ mv comment_utils comment_utils_rev92 cd /srv/python-packages ln -s dist/comment_utils_rev92 comment_utils
- Install Satchmo 0.8. Run the following as root: Update 2008-12-12:
I changed this to use release 0.8 instead of the SVN trunk version.
cd /srv wget http://www.satchmoproject.com/snapshots/satchmo-0.8.tgz tar zxvf satchmo-0.8.tgz cd /srv/python-packages ln -s ../satchmo-0.8/satchmoTo install the SVN trunk version instead,
cd /srv svn co svn://satchmoproject.com/satchmo/trunk mv trunk satchmo_revXXXX cd /srv/python-packages ln -s ../satchmo_revXXXX/satchmo
I followed the instructions at Punteney.com for setting up Django and Postgres on Slicehost.
- If you didn't already install Postgres above, run the following as root:
apt-get install postgresql apt-get install python-psycopg2
- Change the password for the "postgres" Unix user. (run as root or use sudo)
passwd -d postgres su postgres -c passwd
- Change password for "postgres" user in the database.
(From Punteney's article, "It's convenient
to have the two passwords match, but not required.")
su postgres -c psql template1
ALTER USER postgres WITH PASSWORD 'postgres_user_password'; template1=\q
- Create django user:
su postgres createuser -P django_userPunteney says to answer no to all here. Remember the password to put in Django settings.py file. (For this example, I set the password to
- Create a database. Still
su'ed as the "postgres" user:
CREATE DATABASE django_db OWNER django_user ENCODING 'UTF8'; \q
exit(exit from postgres su)
- Configure access to the database. Edit
local all postgres ident sameuser local django_db django_user md5
- Restart the postgres server:
- In your Django project, use the following database settings in
DATABASE_ENGINE = 'postgresql_psycopg2' DATABASE_NAME = 'django_db' DATABASE_USER = 'django_user' DATABASE_PASSWORD = 'my_password' DATABASE_HOST = '' DATABASE_PORT = ''
cd /srv/yourproject python manage.py syncdb
Setup a domain name
- Register for a domain name. Google for "domain registration" for options.
- Follow the Slicehost directions for creating DNS records. This includes setting the nameservers at your domain registration service to use Slicehost's nameservers (ns1.slicehost.net, ns2.slicehost.net, ns3.slicehost.net).
Is there any reason you chose mod_python over mod_wsgi?
Andrew, is mod_wsgi the preferred way to go? I used mod_python because it was recommended by the Django docs and I had used it in the past. I haven't looked into mod_wsgi but will check it out later.
I'm no expert on the subject, but there is a page on the mod_wsgi wiki about integration with django. I don't see any reason not to at least check it out, it is in the Ibex repos. Django also has a page on the setup. I've also recently been benchmarking nginx's mod_wsgi (pre-beta code, not ready for production) and it looks quite promising. I have yet to do a comparison with Apache's mod_wsgi (or mod_python), but it is more than twice as fast as paster's internal server (I'm using pylons)
$ ab -n 1000 -c 50 localhost:5000/blog/ Server Software: PasteWSGIServer/0.5 Server Hostname: localhost Server Port: 5000 Document Path: /blog/ Document Length: 454 bytes Concurrency Level: 50 Time taken for tests: 52.590574 seconds Complete requests: 1000 Failed requests: 0 Write errors: 0 Total transferred: 656000 bytes HTML transferred: 454000 bytes Requests per second: 19.01 [#/sec] (mean) Time per request: 2629.529 [ms] (mean) Time per request: 52.591 [ms] (mean, across all concurrent requests) Transfer rate: 12.17 [Kbytes/sec] received Connection Times (ms) min mean[+/-sd] median max Connect: 0 144 918.7 0 9000 Processing: 186 2427 548.2 2638 3318 Waiting: 173 2381 544.2 2591 3228 Total: 199 2571 992.0 2662 11141 Longest request: 11141 ms
Nginx (2 processes) ::
$ ab -n 1000 -c 50 localhost:5500/blog/ Server Software: nginx/0.7.26 Server Hostname: localhost Server Port: 5500 Document Path: /blog/ Document Length: 454 bytes Concurrency Level: 50 Time taken for tests: 17.595767 seconds Complete requests: 1000 Failed requests: 0 Write errors: 0 Total transferred: 655000 bytes HTML transferred: 454000 bytes Requests per second: 56.83 [#/sec] (mean) Time per request: 879.788 [ms] (mean) Time per request: 17.596 [ms] (mean, across all concurrent requests) Transfer rate: 36.32 [Kbytes/sec] received Connection Times (ms) min mean[+/-sd] median max Connect: 0 0 1.9 0 11 Processing: 213 860 858.5 727 6392 Waiting: 212 860 858.4 726 6391 Total: 221 861 860.0 727 6400 Longest request: 6400 ms
Andrew, thanks for the information. I'll check it out.
I've read that WSGI is the recommended approach and even though not officially yet, Phusion Passenger supports it. I'm not sure how it compares to mod_wsgi though, but it's been working great for Ruby frameworks (in production on many high-volume sites) and I've heard nothing but praises about it.
I may have to start considering alternatives to Apache/mod_python... Even after moving my media files to S3, my site is still slow. I'm not sure if it is Slicehost or if I just need memcached. I briefly looked into nginx and it looks pretty good. I see from their wiki that they have quickly surpassed lighttpd in domains hosted.