nginx + pserve + supervisord

This setup can be accomplished simply and is capable of serving a large amount of traffic. The advantage in deployment is that by using pserve , it is not unlike the basic development environment you're probably using on your local machine.

nginx 是一个高度优化的HTTP服务器,非常能够提供静态内容,并充当其他应用程序和外部世界之间的代理。作为代理,它还很好地支持应用程序多个实例之间的基本负载平衡。

Client <---> nginx [0.0.0.0:80] <---> (static files)
              /|\
               |-------> WSGI App [localhost:5000]
               `-------> WSGI App [localhost:5001]

我们的目标设置是一个nginx服务器,监听端口80和两个pserver进程之间的负载平衡。它还将服务于项目目录中的静态文件。

Let's assume a basic project setup:

 1/home/example/myapp
 2 |
 3 |-- env (your virtualenv)
 4 |
 5 |-- myapp
 6 |   |
 7 |   |-- __init__.py (defining your main entry point)
 8 |   |
 9 |   `-- static (your static files)
10 |
11 |-- production.ini
12 |
13 `-- supervisord.conf (optional)

Step 1: Configuring nginx

nginx needs to be configured as a proxy for your application. 配置示例如下:

 1# nginx.conf
 2
 3user www-data;
 4worker_processes 4;
 5pid /var/run/nginx.pid;
 6
 7events {
 8    worker_connections 1024;
 9    # multi_accept on;
10}
11
12http {
13
14    ##
15    # Basic Settings
16    ##
17
18    sendfile on;
19    tcp_nopush on;
20    tcp_nodelay on;
21    keepalive_timeout 65;
22    types_hash_max_size 2048;
23    # server_tokens off;
24
25    # server_names_hash_bucket_size 64;
26    # server_name_in_redirect off;
27
28    include /etc/nginx/mime.types;
29    default_type application/octet-stream;
30
31    ##
32    # Logging Settings
33    ##
34
35    access_log /var/log/nginx/access.log;
36    error_log /var/log/nginx/error.log;
37
38    ##
39    # Gzip Settings
40    ##
41
42    gzip on;
43    gzip_disable "msie6";
44
45    ##
46    # Virtual Host Configs
47    ##
48
49    include /etc/nginx/conf.d/*.conf;
50    include /etc/nginx/sites-enabled/*;
51}
 1# myapp.conf
 2
 3upstream myapp-site {
 4    server 127.0.0.1:5000;
 5    server 127.0.0.1:5001;
 6}
 7
 8server {
 9    listen 80;
10
11    # optional ssl configuration
12
13    listen 443 ssl;
14    ssl_certificate /path/to/ssl/pem_file;
15    ssl_certificate_key /path/to/ssl/certificate_key;
16
17    # end of optional ssl configuration
18
19    server_name  example.com;
20
21    access_log  /home/example/env/access.log;
22
23    location / {
24        proxy_set_header X-Forwarded-Proto $scheme;
25        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
26        proxy_set_header X-Forwarded-Host $host:$server_port;
27        proxy_set_header X-Forwarded-Port $server_port;
28
29        client_max_body_size    10m;
30        client_body_buffer_size 128k;
31        proxy_connect_timeout   60s;
32        proxy_send_timeout      90s;
33        proxy_read_timeout      90s;
34        proxy_buffering         off;
35        proxy_temp_file_write_size 64k;
36        proxy_pass http://myapp-site;
37        proxy_redirect          off;
38    }
39}

备注

myapp.conf is actually included into the http {{}} 主截面 nginx.conf 文件。

可选的 listen directive, as well as the 2 following lines, are the only configuration changes required to enable SSL from the Client to nginx. You will need to have already created your SSL certificate and key for this to work. More details on this process can be found in the OpenSSL wiki for Command Line Utilities . You will also need to update the paths that are shown to match the actual path to your SSL certificates.

这个 upstream directive sets up a round-robin load-balancer between two processes. The proxy is then configured to pass requests through the balancer with the proxy_pass 指令。重要的是调查许多其他设置的影响,因为它们可能是特定于应用程序的。

这个 proxy_set_header directives inform our application of the exact deployment setup. They will help the WSGI server configure our environment's SCRIPT_NAMEHTTP_HOST , and the actual IP address of the client.

Step 2: Starting pserve

警告

Be sure to create a production.ini

此配置使用 waitress to automatically convert the X-Forwarded-Proto 到WSGI环境中正确的HTTP方案中。这一点很重要,这样应用程序生成的url可以区分不同的域,HTTP和HTTPS。

 1#---------- App Configuration ----------
 2[app:main]
 3use = egg:myapp#main
 4
 5pyramid.reload_templates = false
 6pyramid.debug_authorization = false
 7pyramid.debug_notfound = false
 8pyramid.default_locale_name = en
 9
10#---------- Server Configuration ----------
11[server:main]
12use = egg:waitress#main
13host = 127.0.0.1
14port = %(http_port)s
15
16trusted_proxy = 127.0.0.1
17trusted_proxy_count = 1
18trusted_proxy_headers = x-forwarded-for x-forwarded-host x-forwarded-proto x-forwarded-port
19clear_untrusted_proxy_headers = yes
20
21#---------- Logging Configuration ----------
22# ...

运行pserve进程:

$ pserve production.ini\?http_port=5000
$ pserve production.ini\?http_port=5001

备注

pserver的守护进程为 deprecated in Pyramid 1.6 然后 removed in Pyramid 1.8 .

Step 3: Serving Static Files with nginx (Optional)

假设静态文件位于 Pyramid 应用程序的子目录中,那么可以使用nginx高度优化的Web服务器轻松地提供这些文件。这将大大提高性能,因为对该内容的请求不需要代理到WSGi应用程序,并且可以直接提供服务。

警告

如果您的静态内容是公开的,那么这只是一个好主意。它将不尊重您在此目录上放置的任何视图权限。

 1location / {
 2    # all of your proxy configuration
 3}
 4
 5location /static {
 6    root                    /home/example/myapp/myapp;
 7    expires                 30d;
 8    add_header              Cache-Control public;
 9    access_log              off;
10}

奇怪的是, root 不是指 static 目录,但它起作用,因为nginx将把实际的url附加到指定的路径上。

步骤4:使用Supervisor管理pserver进程(可选)

打开所有的 pserve processes manually and daemonizing them works for the simplest setups, but for a really robust server, you're going to want to automate the startup and shutdown of those processes, as well as have some way of managing failures.

进入 supervisord

$ pip install supervisor

这是一个很好的程序,可以管理任意的进程,在它们失败时重新启动它们,在事情发生变化时提供发送电子邮件等的Hook,甚至公开用于确定系统状态的XML-RPC接口。

Below is an example configuration that starts up two instances of the pserve process, automatically filling in the http_port 基于 process_num 因此,5000和5001。

这只是一个精简版的 supervisord.conf ,请阅读文档,了解所提供的所有伟大选项的完整细目。

 1[unix_http_server]
 2file=%(here)s/env/supervisor.sock
 3
 4[supervisord]
 5pidfile=%(here)s/env/supervisord.pid
 6logfile=%(here)s/env/supervisord.log
 7logfile_maxbytes=50MB
 8logfile_backups=10
 9loglevel=info
10nodaemon=false
11minfds=1024
12minprocs=200
13
14[rpcinterface:supervisor]
15supervisor.rpcinterface_factory = supervisor.rpcinterface:make_main_rpcinterface
16
17[supervisorctl]
18serverurl=unix://%(here)s/env/supervisor.sock
19
20[program:myapp]
21autorestart=true
22command=%(here)s/env/bin/pserve %(here)s/production.ini?http_port=50%(process_num)02d
23process_name=%(program_name)s-%(process_num)01d
24numprocs=2
25numprocs_start=0
26redirect_stderr=true
27stdout_logfile=%(here)s/env/%(program_name)s-%(process_num)01d.log