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

1

13

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()
Jonette answered 8/3, 2013 at 15:13 Comment(8)
To make things easier, I would remove nginx from the mix unless you really need it. Nginx isn't really needed since, the dotCloud gateways will serve the same purpose. To make things even easier, you could do the same thing with the native python-worker, and you wouldn't need to use the custom service at all. you just need to run the gunicorn process, and make sure you ask for an http port the same way you do for the custom service.Sisera
That's the answer I was hoping for! Thanks, Ken. -TimJonette
Not to mention websockets aren't completely supported in anything below version 1.4 in nginx, so that could be your problem right there. Other than that your config looks good.Agrology
@RonE nginx 1.3.13 and up should support websockets.Subtilize
@Jonette are you still having the issue? If so, have you tried setting proxy_buffering off; in your location block? Or else, if you resolved the issue, please post your answer for other users to use.Subtilize
@Subtilize This was two years ago, but if memory serves, I followed the advice of Ken Cochrane above and remove nginx from the setup as in the dot cloud environment it isn't necessary. This was before dot cloud was reborn as "the next dot cloud" so today, YMMV. I will also say, I have gotten this setup to work outside the dot cloud environment, so as folks mention here, websockets definitely can work with port forwarding from nginx to gunicorn.Jonette
You have an opportunity to collect the information into an answer. Question is quite popular and you solved your problem.Fossette
@Beltiras Thank you, that's a good point. I will do that.Jonette
J
1

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.

Jonette answered 12/11, 2015 at 13:42 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.