authored by Wayne Witzel III

Deploying Pylons with nginx

On October 06, 2008 In python, pylons, deploy Permalink
In preparation for a production deployment of a new Pylons app, I've been looking in to different deployment methods. In an effort to to be /. safe and Diggable when the new application launches, we've decided on 4 server deployment.
  • 1 nginx server
  • 2 pylons (paster) servers
  • 1 postgresql server
I built nginx from the source without issues. The default install location of /usr/local/nginx works for me. You'll need to make any init scripts and install them, see your platform doucmentation for how to do this. You'll also want to be sure to add the new log dir to any log stats/consolidating/trimming jobs you run. Here is the important parts of the nginx configuration for proxying to the Paster servers. Also be sure you adjust your keep alive and connection timeout settings, if you have just a standard Ajaxy Web 2.0 application, you'll want to kick that down to 5 5 or 5 10. They default is way to high unless you're doing constant streaming of live updates or something to that degree.
worker_processes  2;
events {
    worker_connections  1024;
}
http {
    client_body_timeout   5;
    client_header_timeout 5;
    keepalive_timeout     5 5;
    send_timeout          5;
    
    tcp_nodelay on;
    tcp_nopush  on;

    gzip              on;
    gzip_buffers      16 8k;
    gzip_comp_level   1;
    gzip_http_version 1.0;
    gzip_min_length   0;
    gzip_types        text/plain text/html text/css;
    gzip_vary         on;

    upstream pasters {
        server 10.3.0.5:5010;
        server 10.3.0.6:5011;
    }
    server {
        listen       80;
        server_name  localhost;

        location / {
            proxy_pass http://pasters;
            proxy_redirect default;
        }
    }
The paster servers are setup like this, I put them both in the same .ini and setup them up in the tpl. This lets me do an easy_install , setup-app based deployment without having to manually edit the ini to change the port numbers, which is error prone. This also lets you adjust and tune per server, instead of deploying 1 server section and changing it for each. Example would be if one server was way more powerful, you could tune it and then use the weighting in nginx to prefer that server. All without having to edit the ini after deployment.
[server:main]
use = egg:Paste#http
host = 0.0.0.0
port = 5010
use_threadpool = True
threadpool_workers = 10

[server:main2]
use = egg:Paste#http
host = 0.0.0.0
port = 5011
use_threadpool = True
threadpool_workers = 10
Using 10 1000 on Apache bench gave me some good results. 85 requests per second to either of the standalone Paster servers. 185 requests per second when balanced with nginx. For fun, I deployed a third on my database server and was pleased to see 250 requests per second. Then I deployed 3 per server. So a total of 9 paster instances and was able to see 1080 requests per second. I also increased the thread of each from 10 to 25 , this uses more memory, but enables a higher RPS. Getting closer to the estimated 2,500 needed to survive a /. and should survive the estimated 1,000 from a high Digg.
Read and Post Comments

Fat models, skinny controllers

On October 05, 2008 In python, design, pylons Permalink
In the world of MVC and RESTful services, the old addage fat models, skinny controllers is something I'm sure you've constantly seen and read about. So what does it really mean? How do you benefit? Is it the silver bullet for MVC development? What are the draw backs? Using the latest versions of Pylons and SQLalchemy (0.9.7rc2 and 0.5.0rc1 respectivly) we can implement this methodology pretty easily. We'll use formencode schemas to handle the basic input validation and then keep our business logic in the controller itself. Here is what a controller method using this concept might look like. [sourcecode language='python'] class MemberController(BaseController): def __before__(self): if session.has_key('memberid'): c.memberid = session['memberid'] @validate(schema=model.forms.schema.SubscriptionSchema(), form='new') def create(self): subscription = model.Subscription(c.memberid, **self.form_result) meta.Session.save(subscription) meta.Session.commit() return redirect_to(controller='member', action='account') [/sourcecode] The schema validation affords us the luxury of being able to just pass our data directly to the model. The __before__ method checks the session for the memberid assigned at login and gives us access to it, further keeping our method nice and clean. The model would implement the business logic, in this case since this is creating a new subscription, it would just sum now() and deltatime(days=days) to determine the expired. This model could later be expanded upon, say for example you added an upgrade methods to your controller. Now, the same subscription model could be used with some added logic. The model could now have a static prorate method to expire the existing account and make room for creating the new subscription. I've pushed the example source to my github, hopefully this will get your brain juices flowing. If I get bored, I'll toss together a complete working example and check it in. Source for this post can be found at http://trac.pieceofpy.com/pieceofpy/browser/fat-models-skinny-controllers
Read and Post Comments
« Previous Page