介绍

Python的Web服务器比较 文章中,我们已经说过,uWSGI是一个庞大的项目,比单纯的 Web应用 功能要复杂得多。然而,正是因为其强大的各种功能,可以容易地将通过配置就满足我们的各种需求,尤其是和 nginx 结合的时候。

在这篇文章中,我们要讨论的 uWSGI 深度不仅仅局限于以前常说的安装服务器,这次我们将会实际部署一个简单的 WSGI Python应用。同时我们也会聊一下 Nginx,因为 Nginx 和 uWSGI 的合作是那么自然,可以说是配合完美的部署栈。

了解 uWSGI 和 Nginx 的使用

uWSGI是一个有着壮志雄心的项目。它提供的工具集不仅仅是简单地作为Web应用程序这么多。由于它在性能等方面表现得非常优异,因此,多年来,它已经成为许多系统管理员和开发人员部署应用的首选工具。

Nginx的,因为从 0.8.40 版本依赖开始支持 uwsgi 协议。这成为使用 uWSGI 运行的 WSGI 应用与 Nginx 通信的最好方式。这也就意味着对你来说,你可以很简单得通过高度灵活的配置就能够获得一个优化的部署。总之,这使得 uWSGI 加 Nginx 成为许多部署方案的最佳选择。

uWSGI 简介

以下是从 DigitalOcean Python服务器比较的文章中提取的内容。

“尽管它的命名规则很混乱,但是 uWSGI 本身就是一个包含很多组件浩大的工程,旨在为软件服务提供一个完整的软件栈。作为组成部分的 uWSGI服务器, 可以运行 Python WSGI应用程序。它支持多种协议,包括自己的 uwsgi 协议(类似于SCGI)。为了满足在应用服务器的前面使用独立的HTTP服务器的需求,NGINX 和 Cherokee Web服务器都提供了模块支持 uWSGIuwsgi协议”。

uWSGI亮点

使用 Nginx 部署 Web应用程序

Nginx的是一个非常高性能的 Web服务器/反向代理。因为它的轻量级以及轻松扩展。由于出色的架构,能够处理大量的请求(几乎不受限制),只取决于你的应用程序或网站负载,可能在以前的应用中难以找到对手。

请注意: “处理”连接意味着不会丢弃他们,并能与为他们提供服务。你仍然需要保证你的应用程序和数据库运作良好,才能使 Nginx为客户服务的响应不是错误消息。

Nginx 反向代理 uWSGI

很多框架和应用程序服务器可以从实际应用中的反应提供静态文件(如,JavaScript,CSS,图像等)一起使用。然而,更好的事情是让(反向代理)服务器,如Nginx的处理提供这些文件,并管理连接(任务要求)。这减轻了很多的负荷的从应用服务器的,授予你一个更好的整体性能。

随着应用程序的增长,你会想优化它,并在时机成熟时,分发跨服务器(VPS),以便能够同时处理更多的连接,并具有一般比较健壮的体系结构。在应用程序服务器(S)的前面有一个反向代理可以帮助你这个从一开始。

Nginx的的可扩展性(与故障转移和其它机制一起如本地缓存)也是一个伟大的壮举,造福不像(简单)的应用程序服务器的Web应用程序。

一个基本的服务器架构的例子:

Client Request ----> Nginx (Reverse-Proxy)
                        |
                       /|\                           
                      | | `-> App. Server I.   127.0.0.1:8081
                      |  `--> App. Server II.  127.0.0.1:8082
                       `----> App. Server III. 127.0.0.1:8083

注意:当一个应用程序设置为侦听传入连接127.0.0.1,这将只可能在本地访问它。如果使用0.0.0.0,那么它将可以接受来自外部的连接。

准备 droplet

在本节中,我们将准备我们使用的虚拟服务器(即部署我们的应用程序的 droplet)。

我们将首先:

注意:这里给出的说明保持简短。要了解更多信息,请访问我们关于的 pip 的 how-to以及 virtualenv的文章:常用的Python工具:使用 virtualenv 安装 pip 和管理软件包

更新默认的操作系统

注意:我们将在一个新的VPS进行以下设置和准备使用的操作系统的最新版本。从理论上说,你不应该有你的服务器上尝试他们的问题。但是,如果您已经在积极使用它,我们强烈建议切换到新系统,你尝试了。

为了确保我们拥有的默认应用程序的最新可用版本,我们需要更新我们的系统。

对于基于Debian的系统(如Ubuntu的,Debian的),运行以下命令:

1
2
aptitude    update
aptitude -y upgrade

设置的Python,PIP和的virtualenv

1
2
3
4
5
aptitude install python-dev
curl https://bitbucket.org/pypa/setuptools/raw/bootstrap/ez_setup.py | python -
curl https://raw.github.com/pypa/pip/master/contrib/get-pip.py | python -
export PATH="/usr/local/bin:$PATH"
sudo pip install virtualenv

创建一个独立的虚拟(Python)的环境

拥有所有必要的工具,在我们的处置准备好了,我们可以创建一个环境中部署我们的应用程序。

记住:如果你没有得到你的发展的virtualenv(本地)机为你的项目,你应该考虑创建一个,内移动应用程序(及其依赖)。

让我们创造这将同时包含一个文件夹开始虚拟环境和应用程序模块:

你可以使用任何名字在这里,以满足您的需求。

mkdir my_app 我们可以继续进入这个文件夹里面,并创建一个新的虚拟环境:

您也可以选择你喜欢的虚拟环境中的任何名称。

cd my_app virtualenv my_app_venv 让我们创建一个新的文件夹有包含你的Python应用程序模块,以及:

这是你的应用程序模块的文件夹。

mkdir app 并激活虚拟环境中解释使用它:

请确保使用您选择为您的虚拟环境中,如果你去比“我的其他事物的名称应用 venv”。

source my_app_venv/bin/activate 最后,这是你的主要应用程序的部署目录应该怎么样子:

my_app              # Main Folder to Contain Everything Together
  |
  |=== my_app_venv  # V. Env. folder with the Python Int.
  |=== app          # Your application module
  |..
  |.

如果你觉得每次都要使用 virtualenv my_app_venv 进行环境切换的话,我之前写过一篇文章 virtualenv wrapper 可以帮助你更方便得管理虚拟环境。

下载并安装uWSGI

我的推荐就是在一个项目中,尽可能将所有的依赖都包括在虚拟环境中,所以对于 uWSGI 来说也不例外,所以我也将在这个虚拟环境中下载并安装 uWSGI。

安装 uWSGI 还是老套路,还是要使用 pip,运行以下命令

1
pip install uwsgi

Tips:要了解更多关于 pip 的内容,例如它的用法和功能,可以参考这篇文章:常用的Python工具:使用 virtualenv 安装 pip 和管理软件包

下载并安装Nginx

因为 Nginx 不是 Python 的库了,所以也就不能安装在虚拟环境中了,因此我们就得用系统包管理工具来安装 Nginx 了,运行以下命令来使用默认的系统包管理套件安装Nginx:

1
sudo aptitude install nginx

注意:要了解更多关于 Ubuntu 上的 Nginx 的信息,可以参阅文章:如何在Ubuntu 12.04安装Nginx

使用 uWSGI 运行 Python WSGI应用

在本节中,我们将示例一个Python WSGI应用程序是如何在 uWSGI Web容器中运行的。在 uWSGI 中运行 Python 的 WSGI应用与在其他应用容易并没有太大区别。和其他服务器一样,uWSGI 需要你的应用程序为它提供一个入口点(可调用对象/函数)。启动时,这个入口点,包括配置变量,传递给 uWSGI 并开始运行。当一个请求到达时,这个可调用对象需要处理它,并把它传递给你的应用程序来处理。

结构:

........
      /|\                           
     | | `-> App. Server I.   127.0.0.1:8080  <--> Application
     |  `--> App. Server II.  127.0.0.1:8081  <--> Application
      .....

WSGI

WSGI简而言之就是一个Web服务器和应用程序本身之间的接口。关于 WSGI 的介绍我以前写过一篇文章:wsgi 简单介绍,有需要可以参考一番。

为了后面的需要,这边需要创建一个简单的WSGI应用对象:wsgi.py。前面说过了,使用 WSGI运行的Web服务器需要一个应用对象(即您的应用程序)。与大多数框架的应用一样,这是由一个的wsgi.py 提供的可调用的应用程序对象。

那现在就让我们创建一个wsgi.py文件,并包含一个基本的WSGI应用程序开始。

运行以下命令使用文本编辑器 nano 创建 wsgi.py:

nano wsgi.py

然后在文本编辑器中敲入我们的 WSGI 应用程序代码:

1
2
3
def application(env, start_response):
    start_response('200 OK', [('Content-Type', 'text/html')])
    return ["Hello!"]

当编辑完毕之后,我们就需要按下快捷键:Ctrl + X 然后键入 'Y' 确定保存文件。需要注意的是这个文件的位置是在 my_app 目录下,在全局中与你的虚拟环境目录平级,你的应用程序的部署目录应该是这个样子:

my_app              # Main Folder to Contain Everything Together
  |
  |=== my_app_venv  # V. Env. folder with the Python Int.
  |=== app          # Your application module
  |
  |--- wsgi.py      # File containing application callable
  |..
  |.

运行服务器

uWSGI 有很多配置和选项参数,也正是因为这些参数和配置才使得它灵活而强大,但是,作为初次使用,我们不需要关注太多,首先来个简单的用法示例:

1
uwsgi [option] [option 2] .. -w [wsgi file with app. callable]

那么对应到运行我们的 wsgi.py 应用,那么就应该是:

1
uwsgi --socket 127.0.0.1:8080 --protocol=http -w wsgi

这将在前台运行服务器,如果你想停止它,请按 Ctrl + C

重要事项[!]:如果你是按照我们的 Nginx配置 一节中的 Nginx 来运行服务器的话,请务必移除 --protocol=http 这个启动参数,否则 Nginx 和 uWSGI 将不能彼此交谈。

管理uWSGI服务器和处理信号

管理 uWSGI 包括运行时所采取的操作, uWSGI 有操纵进程的各种不同的命令集:

信号管理的例子:

使用 SIGHUP 重启服务器

这条命令会优雅得重新启动服务器。也就是说,它会等待当前 worker 的当前任务完成,然后再次终止他们,接着继承设置重新启动。

用法: kill -HUP [PID]

如果你不想指定PID,则可以使用 pidfile 选项启动uWSGI,uWSGI 会写入一个文件,然后您可以用它来管理的进程。

Nginx的配置

建立uWSGI来运行我们的应用程序后,我们现在需要做的Nginx同样为它与uWSGI服务器(S)交谈。对于这一点,我们需要修改Nginx的配置文件:nginx.conf

运行以下命令来打开 nginx.conf 并使用 nano 文本编辑器编辑:

1
sudo nano /etc/nginx/nginx.conf

然后,你可以用下面的示例配置替换文件里面的内容,让 nginx 成为一个反向代理,与你的应用程序进行通信。

Tips:如果想了解如何提供 SSL支持,可以看看这篇文章:在 Nginx 上支持 SSL验证

nginx 的示例配置:

# nginx.conf
worker_processes 1;

events {

    worker_connections 1024;

}

http {

    sendfile on;

    gzip              on;
    gzip_http_version 1.0;
    gzip_proxied      any;
    gzip_min_length   500;
    gzip_disable      "MSIE [1-6]\.";
    gzip_types        text/plain text/xml text/css
                      text/comma-separated-values
                      text/javascript
                      application/x-javascript
                      application/atom+xml;

    # Configuration containing list of application servers
    upstream uwsgicluster {

        server 127.0.0.1:8080;
        # server 127.0.0.1:8081;
        # ..
        # .

    }

    # Configuration for Nginx
    server {

        # Running port
        listen 80;

        # Settings to by-pass for static files 
        location ^~ /static/  {

            # Example:
            # root /full/path/to/application/static/file/dir;
            root /app/static/;

        }

        # Serve a static file (ex. favico) outside static dir.
        location = /favico.ico  {

            root /app/favico.ico;

        }

        # Proxying connections to application servers
        location / {

            include            uwsgi_params;
            uwsgi_pass         uwsgicluster;

            proxy_redirect     off;
            proxy_set_header   Host $host;
            proxy_set_header   X-Real-IP $remote_addr;
            proxy_set_header   X-Forwarded-For $proxy_add_x_forwarded_for;
            proxy_set_header   X-Forwarded-Host $server_name;

        }
    }
}

当您完成修改配置,按 CTRL + X,并按 Y 确认保存并退出。您将需要重新启动 Nginx 是使更改生效。

运行重启Nginx的命令如下:

sudo service nginx stop
sudo service nginx start

或者直接这样也行:

sudo nginx -s reload

注意:要了解更多有关Nginx的内容,可以参阅文章:如何在VPS上配置Nginx Web服务器

配置uWSGI

当启动uWSGI服务于应用时,有几种方式可以应用于例如 socket 运行的必要的配置,例如进程数,主进程设置等。在本节中,我们将讨论其中三种:

注: uWSGI支持各种协议和方式来设置配置文件,如标准输入和HTTP。

选项1:传递结构作为参数:

虽然很容易变得混乱,并且难以重用多次,但是这确实是运行uWSGI最基本的方法,就像任何其他的shell脚本,通过参数提供其必要的配置。

用法示例:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
# uwsgi [option] [option 2] .. -w [wsgi.py with application callable]

# Simple server running *wsgi*
uwsgi --socket 127.0.0.1:8080 -w wsgi

# Running Pyramid (Paster) applications
uwsgi --ini-paste production.ini

# Running web2py applications
uwsgi --pythonpath /path/to/app --module wsgihandler

# Running WSGI application with specific module / callable names
uwsgi --module wsgi_module_name --callable application_callable_name
uwsgi -w wsgi_module_name:application_callable_name

选项2:使用.ini文件进行配置

提供 uWSGI配置的另一个(可能)更好的方式是通过 .ini文件。这些文件有一个简单的结构(见下例),并且需要每次执行uWSGI启动脚本的时候明确得指定位置。

.ini结构示例(example_config.ini):

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
[uwsgi]
# -------------
# Settings:
# key = value
# Comments >> #
# -------------

# socket = [addr:port]
socket = 127.0.0.1:8080

# Base application directory
# chdir = /full/path
chdir  = /my_app

# WSGI module and callable
# module = [wsgi_module_name]:[application_callable_name]
module = app:application

# master = [master process (true of false)]
master = true

# processes = [number of processes]
processes = 5

用法示例:

1
uwsgi --ini example_config.ini

选项#3:使用.json文件结构

使用 .json文件 运行,除了结构之外,大体上与上述的例子相同。

.json结构示例(example_config.json):

1
2
3
4
5
6
7
8
{
    "uwsgi": {
        "socket": ["127.0.0.1:8080"],
        "module": "my_app:app",
        "master": true,
        "processes": 5,
    }
}

用法示例:

1
uwsgi --json example_config.json

Tips: 要了解更多关于配置uWSGI,可以阅读其配置文档

添加并发和监控

1
2
uwsgi --http :9090 --wsgi-file foobar.py --master --processes 4 --threads 2
uwsgi --http :9090 --wsgi-file foobar.py --master --processes 4 --threads 2 --stats 127.0.0.1:9191

更多...

防火墙

保护 SSH

创建告警

监控和观察服务器访问日志日报:

Reference