Posted Under: , , ,

Don’t remember how, but I ran across the “explicit vs implicit self” argument again while lurking on the internet. I realize this is an old argument, but it sparked up my thought machine and I wanted to share my opinion for the hell of it, dead horse be damned.

To me it’s like a noise your car makes that you never notice because it’s made it for so long. However, when other people ride in your car with you, they point it out. You quickly brush it off with “oh, it’s always done that, doesn’t really bother me.”. Like a crotchety old man, I like things the way they are and aint a damn thing you’re gonna do to change my mind!

Every single language has one(or more!) of those “car noises”. Suck it up and be practical, hippies, and get off my lawn!

Ok, I’m done :)

3 comments | 0 pingbacks

New Year’s Python Meme

28.12.2009 23:31

posted by: Benjamin Smith

Posted Under: , , , , ,

From Tarek via pyDanny

1. What’s the coolest Python application, framework or library you have discovered in 2009?

I did a lot of playing with asyncore and am currently having bit of fun with ply. As far as the coolest goes, I’d have to give a nod to Dingus from Gary Bernhardt. I really like the record-then-assert model for mocking, and the name just takes the cake.

2. What new programming technique did you learn in 2009?

Well, this year I completely changed my development methods. More accurately I actually started practicing some sort of development method where I previously had none. Prior to this year, I just kinda vomited code and didn’t pay much mind to quality. Though I always tried to make my code presentable and maintainable, that was typically an afterthought.

This year I started writing unit tests, then I started writing them first. Before I knew it I had read Clean Code and developed a mental work flow that really helped me focus.

The result (I think) has been higher quality code, and a better understanding of what I’m doing! Also, I’m pretty sure I cranked out more code this year than I ever had before even with less time to do so! Naysayers of TDD/BDD be damned, I am proof dammit! To be fair, this might not work for everyone :).

3. What’s the name of the open source project you contributed the most in 2009? What did you do?

Likely my own projects, specifically stompy and NginxMgr. I wrote them both from the ground up..

4. What was the Python blog or website you read the most in 2009?

Pythonistas on Twitter and Planet Python

5. What are the top three things you want to learn in 2010?

  1. Python Concurrency (and multiprocessing)
  2. More meta programming tricks
  3. A functional programming language (erlang or MAYBE Haskell, though it scared me a tad the last time I tried)
0 comments | 0 pingbacks

Nginxmgr — Nginx upstream pool manager.

21.10.2009 20:32

posted by: Benjamin Smith

Posted Under: ,

What?

Nginxmgr is an application for managing pools of upstream servers in an Nginx deployment. It allows you to dynamically allocate, deallocate and monitor servers in an upstream pool via health checks and an XML-RPC interface.

Why?

I wrote this software because I use Nginx as a web application load balancer and have often found the need to monitor the upstream servers and have it react like a real load balancer would. I was also looking for a way to be able to manage the upstream servers programmatically with Python and monitor the health of the upstream pools by being aware of the number of servers available.

Use Case

Typical use case for Nginxmgr is in a large environment, with a large number of upstream pools and servers. Of course, that’s not a requirement. You could also use it on a pool of just a few servers.

Checks/Handlers

Out of the box, Nginxmgr comes with one check named url_ping. It’s job is to check the status of an upstream member every ‘n’ seconds and if it fails, call the appropriate handler. The handler will then remove the server from the upstream pool and reload Nginx. The url_ping check is also configurable, allowing checks on a port other than 80 and also status codes other than 200 (in case you want to get a 404 or something back).

Checks and handlers are Python modules and functions that allow for flexibility and ease of development were new checks/handlers needed.

Interoperability With Nginx

Currently Nginxmgr runs along side Nginx as a separate daemon, however it will not attempt to start if Nginx is not running. It also does a check in the main loop before the health checks are executed to determine if Nginx was shut down or died since the last check. When that check happensm, if Nginx isn’t running, Nginxmgr will shut itself down.

Installing Nginxmgr

First, you need to get the package. You can find links to the code below in the section titled “Get The Code”. Once you have the source tarball, you should extract it, then cd into the newly created nginxmgr directory. Once there you will then run: python setup.py install. This should install all the library code and the command line script. If you’re cloning the code (hg clone), all you will need to do is run the setup script as above in the root directory of your newly acquired repository.

Configuring Nginxmgr

There are two different configuration files for Nginxmgr. One is for the core code, and one is for the url_ping check command. You should only need to mess with the core config. Below are example configs with comments explaining what is going on:

/etc/nginxmgr/nginxmgr.cfg:

[core]
# Should I run in  the background?
daemon           = True
# Should I enable debug level logging?
debug            = True
# Path to handler scripts
handler_path     = '/etc/nginxmgr/handlers/'
# Path to check scripts
checks_path      = '/etc/nginxmgr/checks/'
# Health check config definitions
healthcheck_defs = '/etc/nginxmgr/health_check_defs.cfg'
# How do you want the log to look?
log_format       = '%(asctime)s %(levelname)s %(threadName)s %(filename)s LINE %(lineno)s: %(message)s'
# Where should I log to?
log_file         = '/var/log/nginxmgr.log'
# Where should I deposit my pid?
pid_file         = '/var/run/nginxmgr.pid'
# Location of nginx base config
ngx_base_config  = '/etc/nginx/nginx.conf'
# Location of nginx binary
ngx_bin          = '/usr/sbin/nginx'
# Location of nginx upstreams config
ngx_config       = '/etc/nginx/conf.d/upstreams.conf'
# Location of nginx pid file
ngx_pid_file     = '/var/run/nginx.pid'
# Interface for XML-RPC to listen on
xml_rpc_ip       = "0.0.0.0"
# Port for XML-RPC to bind to
xml_rpc_port     = 9000

    [healthcheck]
    # Enable health checks?
    enabled = True

        [[checks]]
        # Inidividual checks

            [[[url_ping_test_upstreams]]]
            # Type maps to a python module in etc/checks
            type     = url_ping
            # Upstream maps to an upstream pool in nginx upstream.conf
            upstream = test-upstreams 
            # Handler maps to a python module:function in etc/handlers
            handler  = url_ping_handler:badupstream
                # Custom stuff to define for custom checks (defined by check module)
                [[[[extras]]]]
                # port — If port is defined, any existing port in the upstream
                # will be stripped and replaced.
                # port     = 2000
                url      = "http://%s/"
                status   = 200
            [[[url_ping_test_upstreams-two]]]
            # Type maps to a python module in etc/checks
            type     = url_ping
            # Upstream maps to an upstream pool in nginx upstream.conf
            upstream = test-upstreams-two
            # Handler maps to a python module:function in etc/handlers
            handler  = url_ping_handler:badupstream
                # Custom stuff to define for custom checks (defined by check module)
                [[[[extras]]]]
                # port — If port is defined, any existing port in the upstream
                # will be stripped and replaced.
                # port     = 2000
                url      = "http://%s/setup"
                status   = 404

/etc/nginxmgr/health_check_defs.cfg:

[url_ping]
# Check a url ever little while to make sure it's up
# Corresponds to ./checks/check_name.py:function_name
check=url_ping:ping
# Timeout (in seconds)
schedule=2

Running, Stopping and Reloading Nginxmgr

To start Nginxmgr, you would run this:

nginxmgr -c /etc/nginxmgr/nginxmgr/cfg

It will then fork off into the background and write a pid file to wherever you specify in the config (default is /var/run/nginxmgr.pid).

To shut it down properly, get the pid from the pid file and send it a kill signal(SIGTERM, SIGQUIT or SIGABRT) . If not running in daemon mode, just hit CTRL-C.

To reload the config and the health checks send the pid a SIGHUP.

You could also use Nginxmgr in an init script with out difficulty.

XML-RPC Interface

The XML-RPC interface is ideal for managing upstream pools and nodes on the fly, without having to mess with Nginx itself. It is also nice because it provides a language agnostic way to automate operations on upstream pools and provide a way to monitor the health of the pools.

XML-RPC Exposed Methods

Methods exposed to the XML-RPC interface are:

  • get_pools() — lists all of the pools available
  • get_members('pool_name') — get all members available in specified pool
  • disable_member('pool_name', 'IPADDR:PORT') — Temporarily disable member in pool
  • remove_member('pool_name', 'IPADDR:PORT') — Remove member from pool
  • enable_member('pool_name', 'IPADDR:PORT') — Enable disabled member in pool
  • add_member('pool_name', 'IPADDR:PORT') — Add new member to pool

Things to Note

I made some assumptions when developing this and as such it should be noted that Nginxmgr has only been tested on Linux(Centos and Ubuntu) with Python > 2.5. Nginxmgr makes some calls to utilities common to these systems such as ‘ps’, ‘kill’ and ‘pgrep’. The paths are currently hard coded, but that could easily be changed and/or stuffed in the configuration file.

I also make some assumptions about your Nginx setup. Mainly I assume that you separate your Nginx upstreams in it’s own file. That’s how I prefer to manage it, but It’s not required.

I have a major rewrite planned as an exercise in Test Driven Development as well as taking advantage of practices outlined in Clean Code by Robert (Uncle Bob) Martin.

In this rewrite I will also be experimenting with the Python multiprocessing module rather than threads. I’ve also considered REST instead of XML-RPC.

Documentation is non-existent. Sorry, that is one of the things I would like to work on :)

Purpose of This Blog Post

The purpose of this post is to introduce the software see if there is even any interest for such a thing in the community. My hope is that someone else will have a use for it and wish to improve upon it.

Get The Code

I use mercurial for DVCS, so you can find the code on bitbucket here: https://bitbucket.org/benjaminws/nginxmgr/

From there you can either clone the repository or download a tarball of the source.

If for whatever reason there is a problem getting the code from bitbucket, I also have a copy of the code here: http://nginxmgr.code.just-another.net/

From there you can clone the repository.

Hope this software helps someone. Thanks for reading!

8 comments | 1 pingback

JabberCat — Vaporware, materialized!

02.10.2009 11:33

posted by: Benjamin Smith

Posted Under: , , , , ,

JabberCar

UPDATE: Looks like bitbucket is having some issues, see my local repository if you’d like to browse or clone the code.

JabberCat is a jabber bot that can be fed messages via HTTP to enable scripts and other processes the ability to communicate via Jabber. Communicating via JabberCat is meant to be language agnostic. If your favorite language has an HTTP library(as most do), you won’t need to understand jabber to get your messages to a jabber chat or user.

This project started out as nothing more than vaporware, incubating in the brain of a fellow Pythonista named Mike Pirnat. It materialized shortly after Mike presented a talk at ClePy about ideas he had that never came to life.

JabberCat is ideal for plugging into your continuous integration environment to broadcast statistics(code coverage, etc), or even statistics/events/alerts within your network.

Currently there are two tools for passing messages to JabberCat. A web interface, and a command line tool. The web interface allows you to post messages from your browser. The command line client is ideal for piping messages to, like a few lines of a log file, or source code.

JabberCat has SSL support, but it seems to be a bit buggy at the moment due to it dropping connections unexpectedly. There are plans in the works to implement some new features and spruce up the web interface.

Currently the software is very green (read: alpha), and is not recommended for any kind of production use. You should help this project get some traction, really. Why not hop over to Bitbucket and clone/fork the main repository?

JabberCat on Bitbucket: http://bitbucket.org/benjaminws/jabbercat/

JabberCat is written in Python and is comprised of the following components:

If you think this software could be useful to you, and would like to see it evolve, please don’t hesitate to submit patches or enhancement ideas. You could also fork the repository or report issues on the issue tracker

I have submitted a talk to CodeMash to speak about JabberCat, hopefully it will be accepted! I plan on having a polished stable release by then :)

Thanks for reading!

Note: Image credit goes to Dan Buch

0 comments | 0 pingbacks

Python STOMP Updates

20.09.2009 10:29

posted by: Benjamin Smith

Posted Under: , , , , , , ,

Due to a flurry of activity(like, you know, a couple people) on bitbucket and twitter, it became apparent to me that people were actually using the library I created for working with message brokers that utilize the STOMP protocol. This is exciting news for me! ;-)

In response I decided it would be a good idea to, you know, maintain the software and polish it up a bit. There were three forks of the python-stomp repository on
bitbucket, so I waded through diffs to see what ideas/suggestions/improvements others had. Everything I saw looked great, so I pulled in and merged all the changes in to the main repository. Thankfully this was a trivial task because mercurial is FREAKING AWESOME. The next step was to polish it all up. So, I went to PEP8ifying where needed(thankfully others had done a great deal of that work for me), and unit testing new functionality introduced by the other developers.

There are many improvements/changes in the latest iteration of this library, so I’d like to outline some of them here.

  • Renamed to stompy
  • Fully documented with Sphinx
  • Simplified client interface with automatic transaction support (abstracted stompy.stomp as stompy.simple)
  • Intermediate message queue layer
  • Simplified authentication
  • More resilient and informative error handling
  • Polling/Non-blocking support
  • Now able to subscribe to multiple destinations at a time
  • Tested and known to work well with ActiveMQ and RabbitMQ
  • Now available on pypi!

This is just a high level overview of the major changes to stompy. For more detailed information, have a look at the source and documentation.

I’d like to thank a few people that have submitted patches and new functionality.

In closing, I’d like to point out that this “project” was an exercise in TDD(Test Driven Development) for me. I wrote the tests first, then the actual code to make the tests pass. This was also the first time I had used the mocking library Dingus. This has been a pleasurable and enlightening development experience for me!

Thanks for reading!

2 comments | 0 pingbacks

PyOhio 2009: Call for Proposals

09.04.2009 17:32

posted by: Benjamin Smith

Posted Under: , , ,

PyOhio 2009: Call for Proposals

PyOhio 2009, the second annual Python programming mini-conference for Ohio and surrounding areas, will take place Saturday-Sunday, July 25-26, 2009 at the Ohio State University in Columbus, Ohio. A variety of activities are planned, including tutorials, scheduled talks, Lightning Talks, and Open Spaces.

PyOhio invites all interested people to submit proposals for scheduled talks and tutorials. PyOhio will accept abstracts on any topics of interest to Python programmers.

Standard presentations are expected to last 40 minutes with a 10 minute question-and-answer period. Other talk formats will also be considered, however; please indicate your preferred format in your proposal. Hands-on tutorial sessions are also welcomed. Tutorial instructors should indicate the expected length

PyOhio is especially interested in hosting a Beginners’ Track for those new to Python or new to programming in general. If your proposal would be suitable for inclusion in the Beginners’ Track, please indicate so. Organizers will work with speakers and instructors in the Beginners’ Track to help them coordinate their talks/tutorials into a smooth, coherent learning curve for new Python users.

All proposals should include abstracts no longer than 500 words in length. Abstracts must include the title, summary of the presentation, the expertise level targeted, and a brief description of the area of Python programming it relates to.

All proposals should be emailed to cfp@pyohio.org for review. Please submit proposals by May 15, 2009. Accepted speakers will be notified by June 1.

You can read more about the conference at http://pyohio.org

If you have questions about proposals, please email cfp@pyohio.org. You can also contact the PyOhio organizers at pyohio-organizers@python.org.

2 comments | 0 pingbacks

Python STOMP Implementation

03.04.2009 19:42

posted by: Benjamin Smith

Posted Under: , , ,

Everyone else is doing it!

Quite seriously though… In my dealings with JMS Message Queuing, specifically with ActiveMQ, I felt the lack of a simple, straight-forward and easy to use Python implementation of the protocol that was also well documented and hackable.

More often than not, I found myself using Perl’s Net::Stomp module because it was so much quicker to get going. Gasp you say, a Perl module that is easier to use than a Python module?! BLASPHEMY! I implore you to believe me!

I don’t think I’ve reinvented the wheel, merely rounded it to my specifications. So, if you choose, check out my implementation of STOMP in Python over at Bitbucket: python-stomp.

It is feature-complete according to the STOMP Protocol, 100% Unit test covered, documented, comes with example usage and is simple to use.

Feedback is appreciated in the form of issues on the Bitbucket issue tracker or this blog if you want to say something nice, or mean about it!

Documentation:

Code:

0 comments | 0 pingbacks

Psyched for Pycon 2009

25.03.2009 10:24

posted by: Benjamin Smith

Posted Under: ,

I’m jazzed to be heading to Chicago again this year for pycon. Last year I had a blast and learned a lot. I even got to apply some of what I learned in practice at work and on personal projects! I’m hoping that I’ll take away as much value from the conference this year. A quick look at the talks suggest that won’t be a problem.

I’m going to try to blog while I’m there, but most likely you’ll get more out of me via twitter, so why not follow me there?

Here’s the list of talks I really want to attend:

There is no order here, scheduling is the tricky part..

There’s more, but those are the ones that stuck out to me.. Don’t think I’m in too much danger of missing anything this year.

A few of us at AG Interactive are planning a night of getting together for what we’re dubbing as #pyconbeer on twitter, so keep a search up: Twitter #pyconbeer I’m thinking we’re going to shoot for Friday evening, the 27th.

Hope to see many of you there and put names to faces!

Pycon 2009

0 comments | 0 pingbacks

Django debug toolbar FTW.

30.01.2009 12:08

posted by: Benjamin Smith

Posted Under: , , ,

If you do any development with Django, then this is a must have.

Not only is it awesomely useful, but it also has a great abbreviation that could be a throwback to WWF days of old. Any child of the mid-to-late 80’s remember the DDT?! That’s kind of what this is does, help you drive problems with your app head first into the mat - nice.

Google code page here: DDT on google code

Github here: DDT on github

Like any other python package, this one is simple to install.

Get a tarball, unpack it, cd into it..

# python setup.py build
# sudo python setup.py install

Now, use it in your django app by adding something similar to your settings.py:

# This should be appended to MIDDLEWARE_CLASSES:
'debug_toolbar.middleware.DebugToolbarMiddleware',

# This should be appended to INSTALLED_APPS:
'debug_toolbar',

# Set this to localhost if developing locally
# If not developing locally, add your remote IP..
INTERNAL_IPS = ('127.0.0.1')

# Various apps available
DEBUG_TOOLBAR_PANELS = (
    'debug_toolbar.panels.version.VersionDebugPanel',
    'debug_toolbar.panels.timer.TimerDebugPanel',
    'debug_toolbar.panels.headers.HeaderDebugPanel',
    'debug_toolbar.panels.request_vars.RequestVarsDebugPanel',
    'debug_toolbar.panels.sql.SQLDebugPanel',
    'debug_toolbar.panels.cache.CacheDebugPanel',
    'debug_toolbar.panels.logger.LoggingPanel',
)

See here: README for more detail on usage/install.

Get it, use it, love it.

EDIT: Added some quick bits on how to install/use

2 comments | 1 pingback

byteflow/django+supervisord+nginx = WIN

18.01.2009 23:06

posted by: Benjamin Smith

Posted Under: , , , ,

Recently I made the final step in converting my website over to nginx. I decided to manage my django application, byteflow, with supervisor. I have had great success with this over at AGI and thought I should advocate the success I’ve had to the public by extending it to my own site.

I first heard about supervisor at pycon last year and thought it could be useful in many ways, especially at the office. In a nutshell, supervisor ‘supervises’ processes, and allows you to manage them with a simple interface, but I’ll go into more detail about it later. Around the same time that I discovered supervisor I had also started experimenting with nginx and fastcgi to run my blog. I ended up going with lighty and fastcgi instead, however, mainly due to familiarity. Things change, and so do my opinions on technology. Nginx sold me on it’s performance and simplistic configuration, plain and simple. So now, here’s the meat and potatoes.

To break it down, here is what we’re looking at.

INTARWEBS -> nginx -> fastcgi -> django/byteflow(managed by supervisor)

Everything below assumes you have installed nginx, django, byteflow and supervisor.

So how about a look at the actual setup.

First, let’s take a look at the code to fire up the django app. I’ve dubbed it runserver.py and put it in my byteflow code tree.

#!/usr/bin/env python
if __name__ == '__main__':
    from flup.server.fcgi_fork import WSGIServer
    from django.core.handlers.wsgi import WSGIHandler
    WSGIServer(WSGIHandler()).run()

This keeps the django devs happy by not relying on manage.py to keep up with the server stuffs.

It’s pretty simple.. Starts up flups’ WSGIServer and uses django’s WSGIHandler to do the dirty work. Not much to it, really.

Now, lets take a look at the supervisor setup. You can get a lot of what you need by running:

# sudo echo_supervisord_conf > /etc/supervisord.conf

That sets up a basic (but verbose!) supervisor config. I’ve trimmed it down to this:

[unix_http_server]
file=/tmp/supervisor.sock   ; (the path to the socket file)

[supervisord]
logfile=/var/log/supervisord/supervisord.log ; (main log file;default $CWD/supervisord.log)
logfile_maxbytes=50MB       ; (max main logfile bytes b4 rotation;default 50MB)
logfile_backups=10          ; (num of main logfile rotation backups;default 10)
loglevel=info               ; (log level;default info; others: debug,warn,trace)
pidfile=/var/run/supervisord.pid ; (supervisord pidfile;default supervisord.pid)
nodaemon=false              ; (start in foreground if true;default false)
minfds=1024                 ; (min. avail startup file descriptors;default 1024)
minprocs=200                ; (min. avail process descriptors;default 200)
user=nobody                 ; (default is current user, required if root)
childlogdir=/var/log/supervisord/            ; ('AUTO' child log dir, default $TEMP)

[rpcinterface:supervisor]
supervisor.rpcinterface_factory = supervisor.rpcinterface:make_main_rpcinterface

[supervisorctl]
serverurl=unix:///tmp/supervisor.sock ; use a unix:// URL  for a unix socket

; Production setup
[fcgi-program:django_fcgi]
socket=tcp://127.0.0.1:8080  ; We reference this later in nginx
command = /home/bsmith/Dev/byteflow/runserver.py  ; Calls the above code
environment=PYTHONPATH=/home/bsmith/Dev/byteflow  ; Setup needed environment
environment=DJANGO_SETTINGS_MODULE=settings

; Development setup
[fcgi-program:django_dev_fcgi]
socket=tcp://127.0.0.1:8081
command = /home/bsmith/Dev/byteflow_new/runserver.py
environment=PYTHONPATH=/home/bsmith/Dev/byteflow_new
environment=DJANGO_SETTINGS_MODULE=settings

Simple enough, eh? Comments in the configuration should explain what’s going on.

Now you can crank it up by running:

$ sudo supervisord

and check the status…

$ sudo supervisorctl status
django_dev_fcgi:django_dev_fcgi_0 RUNNING    pid 15949, uptime 0:00:08
django_fcgi:django_fcgi_0        RUNNING    pid 15950, uptime 0:00:08

Could it be more simple? I doubt it. This barely begins to scratch the surface of what supervisor can do. Process groups/pools, XML-RPC interface for remote management, built-in web interface for process management(utilizing XML-RPC interface), tons of process management options (priority, umask, user/group, capture std* pipes, environment variables, auto restart/start, process naming), event listeners/handling and a simple configuration ta boot! All I’m sayin’ is, it’s awesome…I’m just sayin’.

Keeping with our simple but awesome theme, enter nginx…

My main config:

user www-data;
# Could vary by number of processors available.
worker_processes  1;

error_log  /var/log/nginx/error.log;
pid        /var/run/nginx.pid;

events {
    worker_connections  1024;
    use epoll;
}

http {
    include       /etc/nginx/mime.types;
    default_type  application/octet-stream;

    access_log      /var/log/nginx/access.log;

    sendfile        on;

    keepalive_timeout  65;
    tcp_nodelay        on;

    gzip  on;

    include /etc/nginx/sites/*;

}

and my various site-specific configurations that I keep /etc/nginx/sites:

server {
        # We listen on port 80
        listen 80;
        server_name just-another.net;

        # access and error logs for our site
        access_log /var/log/nginx/my_site_access_log;
        error_log /var/log/nginx/my_site_error_log;

        # Configure redirect for our fastcgi server
        # The fastcgi server later runs on localhost Port 8080
        location / {
                expires 10d;
                # to fastcgi server could use socket..
                #fastcgi_pass unix:{project_location}/log/django.sock;
                fastcgi_pass 127.0.0.1:8080;
                fastcgi_param PATH_INFO $fastcgi_script_name;
                fastcgi_param REQUEST_METHOD $request_method;
                fastcgi_param QUERY_STRING $query_string;
                fastcgi_param CONTENT_TYPE $content_type;
                fastcgi_param CONTENT_LENGTH $content_length;
                fastcgi_param REMOTE_ADDR $remote_addr;
                fastcgi_pass_header Authorization;
                fastcgi_intercept_errors on;
        }

        location /images {
    # For my own images..
            alias /home/bsmith/public_html/images;
            expires 10d;
        }

        # Alias for static content like themes
        location /static {
            alias /home/bsmith/Dev/byteflow/static;
            expires 10d;
        }

        # Alias for python contrib.admin stuff, needed for admin interface
        location /admin-media {
    # Point to my most recent install of django.
            alias /home/bsmith/Dev/django_trunk/django/contrib/admin/media;
            expires 10d;
        }
        # Point to media
        location /media {
            alias /home/bsmith/public_html/media;
            expires 10d;
        }
        # Use feedburner for my feeds.
        rewrite ^/rpc(.*) http://feeds.feedburner.com/Just-anothernetBlogPosts$1 permanent;
}

Pretty simple, eh? I have the same server setup for my ‘dev’ site, only with a volatile code base that I can hack on.

Now when I make code changes all I need to do is reload the supervisor process like so:

$ sudo supervisorctl restart django_fcgi:django_fcgi_0

for the production site or replacing the process name with django_dev_fcgi:django_dev_fcgi_0 to restart the dev site. I could also restart everything by replacing a process name with all as an argument.

The possibilities here for process management endless and I couldn’t be happier with how simple it has become to run what I have.

Basically, this setup is easy to get running and easy to keep running. Highly recommended for any lazy django site maintainer like myself!

17 comments | 2 pingbacks
More Posts: »