Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

gunicorn, nginx (v 1.3.14), django, and gevent-socket.io, on dotcloud

I am trying to deploy gunicorn + gevent behind nginx (v 1.3.14) on dotcloud. I have a few questions about it. I am aiming to adapt the python-on-dotcloud example. So far, I have not been able to get the websockets portion working with this setup. In other words, I must refresh my pages manually to get updates, rather than via socket.io. This is all pretty new to me, so it may be a total noob error. Here is a related question.

Here are the changes that I made to the python-on-dotcloud example.

  1. Apparently nginx (since version 1.3.13) supports web sockets. I updated the builder script from the python-on-dotcloud example to point to this development version: nginx_download_url="http://nginx.org/download/nginx-1.3.14.tar.gz"

  2. I set up gunicorn to bind to a unix socket and then set up proxy_pass from nginx.conf to send traffic upstream to the gunicorn with proxy_pass http://appserver; where I have defined appserver.

  3. I am running a django app with gevent-socket.io that works fine without nginx running. (I just bind gunicorn to 0.0.0.0:$PORT_WWW in the dotcloud.yml)

my questions are these.

  1. Am I trying to solve a non-problem?

    a. I've done a fair amount of reading where it's advised to put gunicorn behind nginx. In the context of dotcloud's load balancers on the front line, is this still true?

    b. If I don't expect that I'll be subject to a DoS attack, is it still important to put gunicorn behind nginx?

  2. Is it possible to run websockets through a unix socket as I have attempted to setup?

  3. Will the unix sockets break scaling on dotcloud?

  4. If I need to use ports instead, how do set that up? I don't think I can allocate two http ports in the same app. If I split it into two apps, then I'm not sure how to pass the PORT_WWW environment variable from the gunicorn app into the nginx app so that it ends up available to the nginx postinstall script and thus in the resulting nginx.conf.

  5. Any ideas as to why this isn't working as is?

I've included three config files below. Let me know if others would help. Thanks!

dotcloud.yml

www:
    type: custom
    buildscript: python/builder
    systempackages:
        # needed for the Nginx rewrite module
        - libpcre3-dev
        # needed to support python versions 2.7, 3.1, 3.2.
        - python3-all
    ports:
        www: http
    processes:
        nginx: nginx
        app: /home/dotcloud/env/bin/gunicorn -c /home/dotcloud/gunicorn.conf -b unix:/tmp/gunicorn.sock wsgi:application
        #app: /home/dotcloud/env/bin/gunicorn -c /home/dotcloud/gunicorn.conf -b 0.0.0.0:$PORT_WWW wsgi:application
    config:
        # python_version can have one of the following values (2.6, 2.7, 3.1, 3.2). 2.6 is the default if no value is entered.
        python_version: 2.7
data:
  type: redis
db:
  type: postgresql

nginx.conf.in (same as regular nginx.conf, only with PORT_WWW waiting to get swapped for real port number)

# template for nginx.conf file.
# the file will be processed by the postinstall script
# it will insert the correct value for PORT_WWW and then put this
# file in /home/dotcloud/nginx/conf/nginx.conf when done.

# nginx will be managed by supervisord when it starts so we don't need daemon mode
daemon off;

worker_processes    1;

events {
    worker_connections  1024;
}


http {

 include       mime.types;
 default_type  application/octet-stream;

 sendfile        on;
 #tcp_nopush     on;

 keepalive_timeout  65;

 log_format combined-realip '$remote_addr ($http_x_real_ip) - $remote_user [$time_local] "$request" $status $body_bytes_sent "$http_referer" "$http_user_agent"';
 access_log  /var/log/supervisor/nginx_access.log combined-realip;
 error_log  /var/log/supervisor/nginx_error.log error;

 gzip  on;


    upstream appserver {
        server unix:/tmp/gunicorn.sock;
        # For a TCP configuration:
        # server 192.168.0.7:8000 fail_timeout=0;
    }


  server {
    # PORT_WWW value is added via postinstall script.
    listen  @PORT_WWW@ default;

    server_name localhost;

    root    /home/dotcloud/current/;

    location / {
        if ( -f /home/dotcloud/current/maintenance) {
            return 503;
        }
        proxy_pass http://appserver;
        proxy_http_version 1.1;
        proxy_set_header upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";

    }

    error_page 404 @404;
    error_page 500 @500;
    error_page 502 @502;
    error_page 503 @503;
    error_page 504 @504;

    location @404 {
        rewrite ^ /static/404.html;
    }
    location @500 {
        rewrite ^ /static/500.html;
    }
    location @502 {
        rewrite ^ /static/502.html;
    }
    location @503 {
        rewrite ^ /static/503.html;
    }
    location @504 {
        rewrite ^ /static/504.html;
    }

    location /static {
        alias /home/dotcloud/current/static;
    }

    location /robots.txt {
        alias /home/dotcloud/current/static/robots.txt;
    }

    location /favicon.ico {
        alias /home/dotcloud/current/static/favicon.ico;
    }

  }
}

gunicorn.conf

workers = 1
worker_class = 'socketio.sgunicorn.GeventSocketIOWorker'
pidfile = '/tmp/gunicorn.pid'
debug = True
loglevel = 'debug'
errorlog = '/var/log/supervisor/gunicorn.log'
django_settings='/home/dotcloud/settings.py'

def post_fork(server, worker):
    from psycogreen.gevent import patch_psycopg
    patch_psycopg()

# MySQL
#def post_fork(server, worker):
#    import pymysql
#    pymysql.install_as_MySQLdb()
like image 223
t1m0 Avatar asked Mar 08 '13 15:03

t1m0


1 Answers

I asked 5 related questions above, and I'll try to answer the first 3 here. (I don't know enough of dotcloud underpinning to answer the last two). I also expect most folks finding this questions are focused mostly on websockets with gunicorn and nginx (and less on dotcloud particulars). For those folks, you can jump to a reference setup here: gunicorn deployment scheme with nginx

  1. Am I trying to solve a non-problem?

    a. I've done a fair amount of reading where it's advised to put gunicorn behind nginx. In the context of dotcloud's load balancers on the front line, is this still true?

    b. If I don't expect that I'll be subject to a DoS attack, is it still important to put gunicorn behind nginx?

From Ken Cochrane's comment above, I believe the the dotcloud infrastructure itself provides the safety that nginx would normally provide in a DIY setup. Therefore, in this special case this was indeed a "non-problem." However, in general, you do want gunicorn behind nginx, and you can definitely run websockets with that setup.

  1. Is it possible to run websockets through a unix socket as I have attempted to setup?

Yes. Here is a good reference on gunicorn deployment scheme with nginx. For websockets, be sure to read that whole section and include proxy_buffering off;

  1. Will the unix sockets break scaling on dotcloud?

My understanding is that sockets and ports should both work equally well.

like image 193
t1m0 Avatar answered Sep 22 '22 19:09

t1m0