Slide 1

Slide 1 text

Deploying Flask (WSGI) Applications

Slide 2

Slide 2 text

Matt Wright @mattupstate Engineer @ChatID ● Python, Ruby, CoffeeScript ● Flask, Chef, AngularJS Open Source ● Flask-Security ● Flask-Social ● Flask-Mail + a few others

Slide 3

Slide 3 text

WSGI (Web Server Gateway Interface)

Slide 4

Slide 4 text

PEP 333 http://legacy.python.org/dev/peps/pep-0333/

Slide 5

Slide 5 text

# ~/hello.py def application(environ, start_response): start_response('200 OK', [('Content-Type','text/html')]) return ["Hello World"]

Slide 6

Slide 6 text

# ~/app.py from flask import Flask app = Flask(__name__) @app.route('/') @app.route('/') def catch_all(path=''): return 'Hello World'

Slide 7

Slide 7 text

Browser WSGI Gateway WSGI App 1 2 4 3 Request Flow

Slide 8

Slide 8 text

Browser WSGI Gateway WSGI App 1 1. Browser sends an HTTP request to the gateway server Request Flow

Slide 9

Slide 9 text

Browser WSGI Gateway WSGI App 2 2. Gateway server prepares and sends the request to the WSGI compatible application 1 Request Flow

Slide 10

Slide 10 text

Browser WSGI Gateway WSGI App 1 2 3 3. Application processes the request and returns a response Request Flow

Slide 11

Slide 11 text

Browser WSGI Gateway WSGI App 1 2 4 3 4. The gateway server returns the response to the browser Request Flow

Slide 12

Slide 12 text

Browser WSGI Gateway WSGI Middle- ware WSGI App Middleware

Slide 13

Slide 13 text

Browser WSGI Gateway WSGI Middle- ware WSGI App WSGI Middle- ware WSGI Middle- ware WSGI Middle- ware WSGI Middle- ware MOAR MIDDLEWARE!

Slide 14

Slide 14 text

Gateway Servers (sometimes called “containers”)

Slide 15

Slide 15 text

http://www.tornadoweb.org/ Tornado

Slide 16

Slide 16 text

1. Written in Python! 2. Web application framework 3. Asynchronous networking library 4. WSGI support Tornado

Slide 17

Slide 17 text

# ~/tor.py from tornado.wsgi import WSGIContainer from tornado.httpserver import HTTPServer from tornado.ioloop import IOLoop from app import app http_server = HTTPServer(WSGIContainer(app)) http_server.listen(8000) IOLoop.instance().start()

Slide 18

Slide 18 text

matt @ pc in ~/ $ mkvirtualenv wsgi matt @ pc in ~/ workon:wsgi $ pip install flask tornado matt @ pc in ~/ workon:wsgi $ python tor.py

Slide 19

Slide 19 text

matt @ pc in ~/ $ curl localhost:8000 Hello World matt @ pc in ~/ workon:wsgi $ pip install tornado matt @ pc in ~/ workon:wsgi $ python tor.py

Slide 20

Slide 20 text

http://www.gevent.org/ gevent

Slide 21

Slide 21 text

1. Written in Python! 2. Coroutine networking library 3. 3rd party module support gevent

Slide 22

Slide 22 text

# ~/ge.py from gevent.wsgi import WSGIServer from app import app server = WSGIServer(('', 8000), app) server.serve_forever()

Slide 23

Slide 23 text

matt @ pc in ~/ workon:wsgi $ pip install gevent matt @ pc in ~/ workon:wsgi $ python ge.py

Slide 24

Slide 24 text

matt @ pc in ~/ $ curl localhost:8000 Hello World matt @ pc in ~/ workon:wsgi $ pip install gevent matt @ pc in ~/ workon:wsgi $ python ge.py 127.0.0.1 - - [2014-06-24...

Slide 25

Slide 25 text

http://gunicorn.org/ Gunicorn

Slide 26

Slide 26 text

Gunicorn 1. Written in Python! 2. Easy configuration 3. Broadly compatible 4. Extensible 5. Fairly speedy

Slide 27

Slide 27 text

matt @ pc in ~/ workon:wsgi $ pip install gunicorn matt @ pc in ~/ workon:wsgi $ gunicorn app:app

Slide 28

Slide 28 text

matt @ pc in ~/ $ curl localhost:8000 Hello World matt @ pc in ~/ $ mkvirtualenv wsgi matt @ pc in ~/ workon:wsgi $ pip install gunicorn flask matt @ pc in ~/ workon:wsgi $ gunicorn app:app

Slide 29

Slide 29 text

https://uwsgi-docs.readthedocs.org/ uWSGI

Slide 30

Slide 30 text

uWSGI 1. Support for many platforms and languages 2. Multiple configuration formats 3. Flexible logging 4. Extensible 5. Speaks HTTP and uwsgi

Slide 31

Slide 31 text

matt @ pc in ~/ workon:wsgi $ pip install uwsgi matt @ pc in ~/ workon:wsgi $ uwsgi --http :8000 -w app:app ... spawned uWSGI worker 1 ...

Slide 32

Slide 32 text

matt @ pc in ~/ $ curl localhost:8000 Hello World matt @ pc in ~/ workon:wsgi $ pip install uwsgi matt @ pc in ~/ workon:wsgi $ uwsgi --http :8000 -w app:app ... spawned uWSGI worker 1 ... [pid: 42176|app: 0|req: ...

Slide 33

Slide 33 text

(how to start your app) Process Management

Slide 34

Slide 34 text

http://upstart.ubuntu.com/ upstart

Slide 35

Slide 35 text

# /etc/init/hello-world.conf description "Hello World app" start on runlevel [2345] stop on runlevel [06] chdir /srv/hello-world exec /usr/local/bin/uwsgi \ --die-on-term \ --http :8000 \ -w app:app

Slide 36

Slide 36 text

http://www.freedesktop.org/wiki/Software/systemd/ systemd

Slide 37

Slide 37 text

# /etc/systemd/system/hello-world.service [Unit] Description=Hello World app [Service] ExecStart=/usr/local/bin/uwsgi \ --die-on-term \ --http :8000 \ -w app:app [Install] WantedBy=multi-user.target

Slide 38

Slide 38 text

http://supervisord.org/ supervisord

Slide 39

Slide 39 text

# /etc/supervisor.d/hello-world.conf [program:hello-world] directory=/srv/hello-world command=/usr/local/bin/uwsgi \ --die-on-term \ --http :8000 \ -w app:app

Slide 40

Slide 40 text

(some things are better than others) Reverse Proxies

Slide 41

Slide 41 text

Why? ● Static files ● URL rewrites ● Routing ● Header mods ● App aggregation ● and many more! ● Network security ● Authentication ● Load balancing ● SSL termination ● Compression ● Caching

Slide 42

Slide 42 text

http://www.nginx.org/ Apache

Slide 43

Slide 43 text

# /etc/apache2/sites-enabled/hello-world.conf ServerName hello-world.com ProxyPreserveHost On ProxyPass /static ! Alias /static "/srv/hello-world/static" ProxyPass / http://127.0.0.1:8000/ ProxyPassReverse / http://127.0.0.1:8000/

Slide 44

Slide 44 text

http://www.nginx.org/ Nginx

Slide 45

Slide 45 text

# /etc/nginx/sites-enabled/hello-world.conf server { listen 80; server_name hello-world.com; location / { proxy_pass http://127.0.0.1:8000; } location ^~ /static/ { root /srv/hello-world/static; } }

Slide 46

Slide 46 text

(is hard) Concurrency

Slide 47

Slide 47 text

Concurrency Models callback/generator (auto) lightweight threads (auto) processor/thread (config) processor/thread (config) Tornado gevent Gunicorn uWSGI

Slide 48

Slide 48 text

Increasing Concurrency processes processes workers + processes workers + processes Tornado gevent Gunicorn uWSGI

Slide 49

Slide 49 text

Increasing Workers Gunicorn uWSGI --workers --workers

Slide 50

Slide 50 text

DISCLAIMER!

Slide 51

Slide 51 text

(when one server isn’t enough) Load Balancing

Slide 52

Slide 52 text

Machine Level App Server 192.168.1.101 App Server 192.168.1.102 App Server 192.168.1.102 Primary Load Balancer 192.168.1.100

Slide 53

Slide 53 text

# /etc/nginx/sites-enabled/load-balancer.conf upstream app_upstream { least_conn; server 192.168.1.101:8000 server 192.168.1.102:8000 server 192.168.1.103:8000 } server { listen 80; server_name hello-world.com; location / { proxy_pass http://app_upstream; } }

Slide 54

Slide 54 text

Machine + Process Level App Server 192.168.1.101 App Server 192.168.1.102 App Server 192.168.1.102 Primary Load Balancer 192.168.1.100 t 127.0.0.1:8001 127.0.0.1:8002 127.0.0.1:8000 127.0.0.1:8000 127.0.0.1:8001 127.0.0.1:8002 127.0.0.1:8000 127.0.0.1:8001 127.0.0.1:8002

Slide 55

Slide 55 text

# /etc/supervisor.d/hello-world.conf [program:hello-world] numprocs=3 process_name=hello-world%(process_num) directory=/srv/hello-world command=/usr/local/bin/uwsgi \ --die-on-term \ --http :80%(process_num)02d \ -w app:app

Slide 56

Slide 56 text

# /etc/nginx/sites-enabled/hello-world.conf upstream local_upstream { least_conn; server 127.0.0.1:8000 server 127.0.0.1:8001 server 127.0.0.1:8002 } server { listen 80; server_name hello-world.com; location / { proxy_pass http://local_upstream; } }

Slide 57

Slide 57 text

(I’ve failed at this a lot) Recommendations

Slide 58

Slide 58 text

1. Use virtualenv and pip 2. Don’t use virtualenvwrapper 3. Use a `requirements.txt` file Python

Slide 59

Slide 59 text

1. Provide default settings 2. Enable settings to be overridden a. Environment Variable(s) b. Default file locations 3. Raise an error at startup when required settings are missing 4. Generate a `SECRET_KEY` with `os. urandom(16)` Configuration

Slide 60

Slide 60 text

1. Prefer the uwsgi protocol over HTTP 2. Prefer TCP sockets over Unix sockets 3. Lots of useful options a. virtualenv + pythonpath b. enable-threads + lazy-apps c. need-app d. stats Use uWSGI

Slide 61

Slide 61 text

1. Pass custom headers 2. Lots of useful modules a. ngx_http_geoip_module b. ngx_http_gzip_module c. ngx_http_memcached_module d. ngx_http_status_module Use Nginx

Slide 62

Slide 62 text

(putting it all together) Automation

Slide 63

Slide 63 text

http://www.ansible.com/ Ansible

Slide 64

Slide 64 text

(because what could go wrong?) Demo

Slide 65

Slide 65 text

Thank You! mattupstate.com gittip.com/mattupstate github.com/mattupstate twitter.com/mattupstate