如何设置uWSGI和Nginx以在Ubuntu14.04上为Python应用程序提供服务

来自菜鸟教程
跳转至:导航、​搜索

介绍

在本指南中,我们将设置一个由 uWSGI 服务的简单 WSGI 应用程序。 我们将使用 Nginx Web 服务器作为应用程序服务器的反向代理,以提供更强大的连接处理。 我们将在 Ubuntu 14.04 服务器上安装和配置这些组件。

定义和概念

澄清一些条款

在我们开始之前,我们应该解决一些与我们将要处理的相互关联的概念相关的令人困惑的术语。 这三个独立的术语看起来可以互换,但实际上具有不同的含义:

  • WSGIPython 规范,定义了应用程序或框架与应用程序/Web 服务器之间通信的标准接口。 这是为了简化和标准化这些组件之间的通信,以实现一致性和可互换性。 这基本上定义了一个可以在其他协议上使用的 API 接口。
  • uWSGI:一个应用程序服务器容器,旨在为开发和部署 Web 应用程序和服务提供完整的堆栈。 主要组件是一个应用服务器,可以处理不同语言的应用程序。 它使用 WSGI 规范定义的方法与应用程序通信,并通过各种其他协议与其他 Web 服务器通信。 这是将来自传统 Web 服务器的请求转换为应用程序可以处理的格式的部分。
  • uwsgi:由 uWSGI 服务器实现的快速二进制协议,用于与功能更全面的 Web 服务器进行通信。 这是 有线协议 ,而不是传输协议。 这是与代理 uWSGI 请求的 Web 服务器通信的首选方式。

WSGI 应用要求

WSGI 规范定义了 Web 服务器和堆栈的应用程序部分之间的接口。 在此上下文中,“Web 服务器”指的是 uWSGI 服务器,它负责将客户端请求转换为使用 WSGI 规范的应用程序。 这简化了通信并创建了松散耦合的组件,因此您可以轻松地交换任何一方而不会遇到太多麻烦。

Web 服务器 (uWSGI) 必须能够通过触发定义的“可调用”来向应用程序发送请求。 可调用对象只是应用程序的入口点,Web 服务器可以在其中调用带有一些参数的函数。 预期参数是环境变量字典和 Web 服务器 (uWSGI) 组件提供的可调用对象。

作为响应,应用程序返回一个可用于生成客户端响应正文的可迭代对象。 它还将调用作为参数接收的 Web 服务器组件可调用。 触发 Web 服务器可调用的第一个参数是 HTTP 状态代码,第二个参数是元组列表,每个元组定义一个响应头和值以发送回客户端。

在这种情况下,使用 uWSGI 提供的这种交互的“Web 服务器”组件,我们只需要确保我们的应用程序具有上述质量。 我们还将设置 Nginx 来处理实际的客户端请求并将它们代理到 uWSGI 服务器。

安装组件

首先,我们需要在我们的 Ubuntu 14.04 服务器上安装必要的组件。 我们主要可以使用 aptpip 来做到这一点。

首先,刷新您的 apt 包索引,然后安装 Python 开发库和头文件、pip Python 包管理器、Nginx Web 服务器和反向代理:

sudo apt-get update
sudo apt-get install python-dev python-pip nginx

包安装完成后,您将可以访问 pip Python 包管理器。 我们可以使用它来安装 virtualenv 包,我们将使用它来隔离我们应用程序的 Python 环境与系统上可能存在的任何其他环境:

sudo pip install virtualenv

一旦完成,我们就可以开始为我们的应用程序创建一般结构。 我们将创建上面讨论的虚拟环境,并将在此环境中安装 uWSGI 应用程序服务器。

设置 App Directory 和 Virtualenv

我们将从为我们的应用程序创建一个文件夹开始。 这可以包含一个嵌套文件夹,其中包含更完整应用程序中的实际应用程序代码。 出于我们的目的,这个目录将简单地保存我们的虚拟环境和我们的 WSGI 入口点:

mkdir ~/myapp/

接下来,进入目录,以便我们可以为我们的应用程序设置环境:

cd ~/myapp

使用 virtualenv 命令创建虚拟环境。 为简单起见,我们将其称为 myappenv

virtualenv myappenv

在名为 myappenv 的目录下将建立一个新的 Python 环境。 我们可以通过键入以下内容来激活此环境:

source myappenv/bin/activate

您的提示应更改以指示您现在正在虚拟环境中操作。 它看起来像这样:

(myappenv)username@host:~/my_app$

如果您想随时离开此环境,只需键入:

deactivate

如果您已停用您的环境,请再次重新激活它以继续使用该指南。

激活此环境后,安装的任何 Python 包都将包含在此目录层次结构中。 它们不会干扰系统的 Python 环境。 考虑到这一点,我们现在可以使用 pip 将 uWSGI 服务器安装到我们的环境中。 用于此的包称为 uwsgi(这仍然是 uWSGI 服务器,而不是 uwsgi 协议):

pip install uwsgi

您可以通过键入以下内容来验证它现在是否可用:

uwsgi --version

如果它返回一个版本号,那么 uWSGI 服务器就可以使用了。

创建一个 WSGI 应用程序

接下来,我们将使用我们之前讨论过的 WSGI 规范要求创建一个非常简单的 WSGI 应用程序。 重申一下,我们必须提供的应用程序组件应该具有以下属性:

  • 它必须通过可调用(可以调用的函数或其他语言构造)提供接口
  • 可调用对象必须将包含类似环境变量的键值对的字典和可在服务器上访问的可调用对象 (uWSGI) 作为参数。
  • 应用程序的可调用对象应返回一个可迭代对象,该可迭代对象将生成发送给客户端的主体。
  • 应用程序应该使用 HTTP 状态和请求标头调用 Web 服务器的可调用对象。

我们将在我们的应用程序目录中的一个名为 wsgi.py 的文件中编写我们的应用程序:

nano ~/myapp/wsgi.py

在这个文件中,我们将创建最简单的 WSGI 兼容应用程序。 与所有 Python 代码一样,请务必注意缩进:

def application(environ, start_response):
    start_response('200 OK', [('Content-Type', 'text/html')])
    return ["<h1 style='color:blue'>Hello There!</h1>"]

上面的代码构成了一个完整的 WSGI 应用程序。 默认情况下,uWSGI 将寻找一个名为 application 的可调用对象,这就是我们调用函数 application 的原因。 如您所见,它需要两个参数。

第一个我们称为 environ 因为它将是一个类似环境变量的键值字典。 第二个称为 start_response 并且是应用程序将在内部使用的名称来引用发送的 Web 服务器 (uWSGI) 可调用文件。 之所以选择这两个参数名称,是因为它们在定义 WSGI 交互的 PEP 333 规范中的示例中使用。

我们的应用程序必须获取这些信息并做两件事。 首先,它必须调用它接收到的带有 HTTP 状态代码的可调用对象以及它想要发回的任何标头。 在这种情况下,我们发送“200 OK”响应并将 Content-Type 标头设置为 text/html

其次,它需要返回一个可用作响应主体的可迭代对象。 在这里,我们刚刚使用了一个包含单个 HTML 字符串的列表。 字符串也是可迭代的,但在列表中,uWSGI 将能够通过一次迭代来处理整个字符串。

在现实世界的场景中,这个文件很可能被用作到你的应用程序代码的其余部分的链接。 例如,Django 项目默认包含一个 wsgi.py 文件,该文件将来自 Web 服务器 (uWSGI) 的请求转换为应用程序 (Django)。 无论实际应用程序代码多么复杂,简化的 WSGI 接口都保持不变。 这是界面的优势之一。

完成后保存并关闭文件。

为了测试代码,我们可以启动 uWSGI。 我们将告诉它暂时使用 HTTP 并监听端口 8080。 我们将向它传递脚本的名称(删除后缀):

uwsgi --socket 0.0.0.0:8080 --protocol=http -w wsgi

现在,如果您在 Web 浏览器中访问服务器的 IP 地址或域名,然后是 :8080,您应该会在 wsgi.py 文件中看到我们作为正文传递的第一级标题文本:

当您确认这有效时,使用 CTRL-C 停止服务器。

至此,我们已经完成了实际应用程序的设计。 如果您愿意,您可以停用我们的虚拟环境:

deactivate

配置 uWSGI 配置文件

在上面的例子中,我们手动启动了 uWSGI 服务器,并在命令行中传递了一些参数。 我们可以通过创建配置文件来避免这种情况。 uWSGI 服务器可以读取多种格式的配置,但为了简单起见,我们将使用 .ini 格式。

为了继续我们迄今为止一直使用的命名,我们将调用文件 myapp.ini 并将其放在我们的应用程序文件夹中:

nano ~/myapp/myapp.ini

在内部,我们需要建立一个名为 [uwsgi] 的部分。 这部分是我们所有配置项所在的地方。 我们将从识别我们的应用程序开始。 uWSGI 服务器需要知道应用程序的可调用对象在哪里。 我们可以在里面给出文件和函数:

[uwsgi]
module = wsgi:application

我们希望将初始的 uwsgi 进程标记为主进程,然后生成许多工作进程。 我们将从五个工人开始:

[uwsgi]
module = wsgi:application

master = true
processes = 5

我们实际上要改变 uWSGI 用来与外界对话的协议。 当我们测试我们的应用程序时,我们指定了 --protocol=http 以便我们可以从 Web 浏览器中看到它。 由于我们将在 uWSGI 之前将 Nginx 配置为反向代理,因此我们可以更改它。 Nginx 实现了 uwsgi 代理机制,这是一种快速的二进制协议,uWSGI 可以使用它与其他服务器通信。 uwsgi 协议实际上是 uWSGI 的默认协议,因此只需省略一个协议规范,它就会回退到 uwsgi

由于我们正在设计此配置以与 Nginx 一起使用,因此我们还将从使用网络端口更改为使用 Unix 套接字。 这更安全、更快捷。 如果我们使用相对路径,套接字将在当前目录中创建。 我们称之为 myapp.sock。 我们将权限更改为“664”,以便 Nginx 可以对其进行写入(我们将使用 Nginx 使用的 www-data 组启动 uWSGI。 我们还将添加 vacuum 选项,它将在进程停止时删除套接字:

[uwsgi]
module = wsgi:application

master = true
processes = 5

socket = myapp.sock
chmod-socket = 664
vacuum = true

我们需要最后一个选项,因为我们将创建一个 Upstart 文件以在启动时启动我们的应用程序。 Upstart 和 uWSGI 对于 SIGTERM 信号应该对应用程序做什么有不同的想法。 为了解决这种差异,以便使用 Upstart 可以按预期处理进程,我们只需要添加一个名为 die-on-term 的选项,以便 uWSGI 将终止进程而不是重新加载它:

[uwsgi]
module = wsgi:application

master = true
processes = 5

socket = myapp.sock
chmod-socket = 664
vacuum = true

die-on-term = true

完成后保存并关闭文件。 此配置文件现在设置为与 Upstart 脚本一起使用。

创建一个 Upstart 文件来管理应用程序

我们可以在启动时启动一个 uWSGI 实例,以便我们的应用程序始终可用。 我们将把它放在 Upstart 检查的 /etc/init 目录中。 我们将称之为 myapp.conf

sudo nano /etc/init/myapp.conf

首先,我们可以从服务的描述开始,然后选择它应该自动运行的系统运行级别。 标准用户运行级别为 2 到 5。 我们将告诉 Upstart 在该组之外的任何运行级别上停止服务(例如当系统重新启动或处于单用户模式时):

description "uWSGI instance to serve myapp"

start on runlevel [2345]
stop on runlevel [!2345]

接下来,将告诉 Upstart 以哪个用户和组的身份运行该进程。 我们想在我们自己的帐户下运行应用程序(我们在本指南中使用 demo,但您应该替换您自己的用户)。 然而,我们想将该组设置为 Nginx 使用的 www-data 用户。 这是必要的,因为 Web 服务器需要能够读取和写入我们的 .ini 文件将创建的套接字:

description "uWSGI instance to serve myapp"

start on runlevel [2345]
stop on runlevel [!2345]

setuid demo
setgid www-data

接下来,我们将运行实际的命令来启动 uWSGI。 由于我们将 uWSGI 安装到了虚拟环境中,因此我们还有一些额外的工作要做。 我们可以只提供 uWSGI 可执行文件的完整路径,但我们将激活虚拟环境。 如果我们依赖安装在环境中的其他软件,这将变得更容易。

为此,我们将使用 script 块。 在里面,我们将切换到我们的应用程序目录,激活虚拟环境(我们必须在脚本中使用 . 而不是 source),并启动指向我们的 .ini 的 uWSGI 实例] 文件:

description "uWSGI instance to serve myapp"

start on runlevel [2345]
stop on runlevel [!2345]

setuid demo
setgid www-data

script
    cd /home/demo/myapp
    . myappenv/bin/activate
    uwsgi --ini myapp.ini
end script

这样,我们的 Upstart 脚本就完成了。 完成后保存并关闭文件。

现在,我们可以通过键入以下内容来启动服务:

sudo start myapp

我们可以通过键入以下内容来验证它是否已启动:

ps aux | grep myapp
demo   14618  0.0  0.5  35868  5996 ?        S    15:02   0:00 uwsgi --ini myapp.ini
demo   14619  0.0  0.5  42680  5532 ?        S    15:02   0:00 uwsgi --ini myapp.ini
demo   14620  0.0  0.5  42680  5532 ?        S    15:02   0:00 uwsgi --ini myapp.ini
demo   14621  0.0  0.5  42680  5532 ?        S    15:02   0:00 uwsgi --ini myapp.ini
demo   14622  0.0  0.5  42680  5532 ?        S    15:02   0:00 uwsgi --ini myapp.ini
demo   14623  0.0  0.5  42680  5532 ?        S    15:02   0:00 uwsgi --ini myapp.ini
demo   15520  0.0  0.0  11740   936 pts/0    S+   15:53   0:00 grep --color=auto myapp

这将在启动时自动启动。 您可以随时通过键入以下内容来停止服务:

sudo stop myapp

将 Nginx 配置为代理到 uWSGI

至此,我们有了一个 WSGI 应用程序,并且验证了 uWSGI 可以读取和服务它。 我们已经创建了一个配置文件和一个 Upstart 脚本。 我们的 uWSGI 进程将监听一个套接字并使用 uwsgi 协议进行通信。

我们现在可以将 Nginx 配置为反向代理。 Nginx 能够使用 uwsgi 协议代理与 uWSGI 通信。 这是一个比 HTTP 更快的协议,并且性能更好。

我们将要设置的 Nginx 配置非常简单。 在 Nginx 的配置层次结构中的 sites-available 目录中创建一个新文件。 我们将调用我们的文件 myapp 来匹配我们一直使用的应用名称:

sudo nano /etc/nginx/sites-available/myapp

在此文件中,我们可以指定此服务器块应响应的端口号和域名。 在我们的例子中,我们将使用默认端口 80:

server {
    listen 80;
    server_name server_domain_or_IP;
}

由于我们希望将此域或 IP 地址上的所有请求发送到我们的 WSGI 应用程序,我们将为以 / 开头的请求创建一个单独的位置块,它应该匹配所有内容。 在内部,我们将使用 include 指令从 Nginx 配置目录中的文件中包含许多具有合理默认值的参数。 包含这些的文件称为 uwsgi_params。 之后,我们将通过 uwsgi 协议将流量传递给我们的 uWSGI 实例。 我们将使用我们之前配置的 unix 套接字:

server {
    listen 80;
    server_name server_domain_or_IP;

    location / {
        include         uwsgi_params;
        uwsgi_pass      unix:/home/demo/myapp/myapp.sock;
    }
}

这实际上就是我们需要的一个简单应用程序。 对于更完整的应用程序,可以进行一些改进。 例如,我们可能会在该块之外定义许多上游 uWSGI 服务器,然后将它们传递给该块。 我们可能会包含更多的 uWSGI 参数。 我们也可以直接处理来自 Nginx 的任何静态文件,并且只将动态请求传递给 uWSGI 实例。

不过,我们的三行应用程序中不需要任何这些功能,因此我们可以保存并关闭文件。

通过将其链接到 sites-enabled 目录来启用我们刚刚创建的服务器配置:

sudo ln -s /etc/nginx/sites-available/myapp /etc/nginx/sites-enabled

检查配置文件的语法错误:

sudo service nginx configtest

如果它报告未检测到任何问题,请重新启动服务器以实施您的更改:

sudo service nginx restart

一旦 Nginx 重新启动,您应该能够转到服务器的域名或 IP 地址(没有端口号)并查看您配置的应用程序:

结论

如果您已经做到了这一步,那么您已经创建了一个简单的 WSGI 应用程序,并且对需要如何设计更复杂的应用程序有了一些了解。 我们已将 uWSGI 应用程序容器/服务器安装到一个专用的虚拟环境中来为我们的应用程序提供服务。 我们制作了一个配置文件和一个 Upstart 脚本来自动化这个过程。 在 uWSGI 服务器前面,我们设置了一个 Nginx 反向代理,它可以使用 uwsgi 有线协议与 uWSGI 进程通信。

在设置实际生产环境时,您可以轻松了解如何扩展它。 例如,uWSGI 能够使用称为“帝王模式”的东西来管理多个应用程序。 您可以扩展 Nginx 配置以在 uWSGI 实例之间进行负载平衡,或者为您的应用程序处理静态文件。 在为多个应用程序提供服务时,根据您的需要,全局安装 uWSGI 而不是在虚拟环境中安装可能更符合您的利益。 这些组件都相当灵活,因此您应该能够调整它们的配置以适应许多不同的场景。