Hacker News new | ask | show | jobs
by wfn 4834 days ago
If the application in question is using WSGI to serve the content, I would e.g. advise to use nginx's built-in uwsgi protocol support (here 'uwsgi' denoting protocol and not the popular application server uWSGI itself) for performance reasons. I agree with sirn that uWSGI should not be easily overlooked. :)

uwsgi protocol is built-in in nginx >= 0.8.40; for older versions (and a general walkthrough), see http://uwsgi-docs.readthedocs.org/en/latest/Nginx.html

Here's the relevant bit for nginx's .conf:

  First of all copy the uwsgi_params file (available in the nginx directory of the uWSGI distribution [uWSGI here meaning the uWSGI application server, to be installed separately via your distro's package manager or e.g. pip]) into your Nginx configuration directory, then in a location directive in your Nginx configuration add:

    uwsgi_pass unix:///tmp/uwsgi.sock;
    include uwsgi_params;

  – or if you are using TCP sockets,

    uwsgi_pass 127.0.0.1:3031;
    include uwsgi_params;
(I would advise the former for performance reasons (unix sockets are faster))

  Then simply reload Nginx and you are ready to rock your uWSGI powered applications through Nginx.
Here are my nginx relevant directives - in server {} scope,

    set $server_root   /home/z/www/<basically,your_web_root>;
    set $uwsgi_socket  unix:$server_root/backend/var/uwsgi.sock;
Then (still in server {} scope),

    location /api { # my flask app serves <domain>.tld/api
      uwsgi_pass $uwsgi_socket;
      include uwsgi_params;
    }
That's it! That way I can reuse $uwsgi_socket as many times as is needed, etc.

The most simple way to launch the uWSGI app server would be to e.g.

  z@<hostname>:~/www/<web_root>/backend/lba$ cat test.sh

    #!/usr/bin/env bash
    uwsgi --post-buffering 1 --enable-threads -s /home/z/www/<web_root>/backend/var/uwsgi.sock -M -p 2 -w lba:app --chmod-socket 666 --pp ..

  uwsgi --help | grep "\-pp"
    --pp    add directory (or glob) to pythonpath
So we add a parent directory ("lba" containing the actual application files, e.g. __init__.py (can be empty) so that the directory could be imported (lba:app in uWSGI launch), etc.), tell uWSGI that this is to be the parent uwsgi process (-M), point it to its socket (-s), set number or processes (-p), tell it to actually load our 'WSGI module' (-w), and supply other per-application parameters which are not necessary if you just want to test things out.

app.py (lba:app) may simply contain (from http://flask.pocoo.org/docs/quickstart/) e.g.

  from flask import Flask
  app = Flask(__name__)

  @app.route('/')
  def hello_world():
    return 'Hello World!'

  if __name__ == '__main__':
    app.run()
That way you can test the sample app by simply sudo service nginx reload and ./test.sh (you might need sudo for the latter, too; also, might need to touch ../var/uwsgi.sock and look into uwsgi --help | grep "user") You can then put the test.sh (or the uwsgi call line itself) into e.g. supervisor - if the user privilege nuances are sorted out, you more or less have a production-worth setup (as far as nginx <-> uwsgi <-> your app is concerned), methinks.

edit sirn and antihero make a good point about Emperor mode

1 comments

Actually, for anything that resembles production environment, I'd recommend using uWSGI in Emperor mode[1] to manage its instance than using Supervisor. Emperor mode will gracefully reload the app if config file's modification time changed (via `touch`) or stop and start the process automatically if you remove or add a configuration file.

My setup usually uses Upstart[2] (Ubuntu) or init script (Debian, etc.) to run uWSGI Emperor and make it scan /etc/uwsgi. When I need to deploy a new version, I just push the code then `touch /etc/uwsgi/myapp.ini` and my app is live with the code I just pushed.

[1] http://uwsgi-docs.readthedocs.org/en/latest/Emperor.html

[2] http://uwsgi-docs.readthedocs.org/en/latest/Upstart.html