介绍
在本教程中,我们将设置一个由uWSGI提供服务的简单WSGI应用程序。我们将使用Nginx Web服务器作为应用程序服务器的反向代理,以提供更强大的连接处理。我们将在Ubuntu 14.04服务器上安装和配置这些组件。
要完成本教程,您需要具备一台已经设置好可以使用sudo
命令的非root账号的Ubuntu服务器,并且已开启防火墙。没有服务器的同学可以在这里购买,不过我个人更推荐您使用免费的腾讯云开发者实验室进行试验,学会安装后再购买服务器。
定义和概念
澄清一些条款
在我们进入之前,我们应该解决一些与我们将要处理的相互关联的概念相关的令人困惑的术语。这三个单独的术语看似可以互换,但实际上有不同的含义:
- WSGI:Python规范,定义了应用程序或框架与应用程序/ 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服务器上安装必要的组件。我们主要可以使用apt
和pip
来完成。
首先,刷新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入口点:
代码语言:javascript复制mkdir ~/myapp/
接下来,进入目录,以便我们可以为我们的应用程序设置环境:
代码语言:javascript复制cd ~/myapp
使用virtualenv
命令创建虚拟环境。我们将myappenv
简单地称之为:
virtualenv myappenv
将在名为myappenv
的目录下设置新的Python环境。我们可以通过输入以下命令激活此环:
source myappenv/bin/activate
您的提示应更改为表明您现在正在虚拟环境中运行。它看起来像这样:
代码语言:javascript复制(myappenv)username@host:~/my_app$
如果您希望随时离开此环境,只需键入:
代码语言:javascript复制deactivate
如果您已停用环境,请重新将其重新激活以继续使用教程。
在此环境处于活动状态时,安装的任何Python包都将包含在此目录层次结构中。它们不会干扰系统的Python环境。考虑到这一点,我们现在可以使用pip
将uWSGI服务器安装到我们的环境中。调用uwsgi
包(这仍然是uWSGI服务器而不是uwsgi
协议):
pip install uwsgi
您可以通过键入以下内容来验证它现在是否可用:
代码语言:javascript复制uwsgi --version
如果它返回版本号,则uWSGI服务器可供使用。
创建WSGI应用程序
接下来,我们将使用前面讨论过的WSGI规范要求创建一个非常简单的WSGI应用程序。重申一下,我们必须提供的应用程序组件应具有以下属性:
- 它必须通过可调用(可以调用的函数或其他语言结构)提供接口
- callable必须将包含类似环境变量的键值对的字典和可在服务器上访问的可调用字符(uWSGI)作为参数。
- 应用程序的可调用应该返回一个迭代,它将生成发送客户端的主体。
- 应用程序应使用HTTP状态和请求标头调用Web服务器的可调用对象。
我们将在应用程序目录中调用的文件wsgi.py
中编写应用程序:
nano ~/myapp/wsgi.py
在这个文件中,我们将创建最简单的WSGI兼容应用程序。与所有Python代码一样,请务必注意缩进:
代码语言:javascript复制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)可调用。这两个参数名称都被简单选择,因为它们用于定义的PEP 333规范中的示例WSGI交互。
我们的应用程序必须获取此信息并执行两项操作。首先,它必须使用HTTP状态代码和它想要发回的任何头来调用它收到的可调用对象。在这种情况下,我们发送“200 OK”响应并将Content-Type
标头设置为text/html
。
其次,它需要返回一个iterable来用作响应体。在这里,我们刚刚使用了一个包含单个HTML字符串的列表。字符串也是可迭代的,但是在列表内部,uWSGI将能够通过一次迭代处理整个字符串。
在现实世界中,此文件可能会用作其他应用程序代码的链接。例如,Django项目默认包含一个文件wsgi.py
,用于将来自Web服务器(uWSGI)的请求转换为应用程序(Django)。无论实际应用程序代码有多复杂,简化的WSGI接口都保持不变。这是界面的优势之一。
完成后保存并关闭文件。
要测试代码,我们可以启动uWSGI。我们将告诉它暂时使用HTTP并监听端口8080
。我们将传递脚本的名称(后缀已删除):
uwsgi --socket 0.0.0.0:8080 --protocol=http -w wsgi
现在,如果您在Web浏览器中访问以:8080
结尾的服务器的IP地址或域名,则应该看到我们在wsgi.py
文件中作为正文传递的第一级标题文本:
验证确实有效后,使用CTRL-C停止服务器。
我们已完成设计我们的实际应用程序。如果您愿意,可以停用我们的虚拟环境:
代码语言:javascript复制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可以写入它(我们将www-data
使用Nginx使用的组启动uWSGI 。我们还将添加vacuum
选项,这将在进程停止时删除套接字:
[uwsgi]
module = wsgi:application
master = true
processes = 5
socket = myapp.sock
chmod-socket = 664
vacuum = true
我们需要一个最终选项,因为我们将创建一个Upstart文件以在启动时启动我们的应用程序。关于SIGTERM信号应该对应用程序做什么,Upstart和uWSGI有不同的想法。为了解决这种差异,以便可以使用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在该组之外的任何运行级别上停止服务(例如系统重启或单用户模式时):
代码语言:javascript复制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脚本就完成了。完成后保存并关闭文件。
现在,我们可以通过键入以下命令启动服务:
代码语言:javascript复制sudo start myapp
我们可以通过输入以下内容来验证它是否已启动:
代码语言:javascript复制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
这将在启动时自动启动。您可以通过键入以下内容随时停止服务:
代码语言:javascript复制sudo stop myapp
将Nginx配置为代理到uWSGI
此时,我们有一个WSGI应用程序,并已验证uWSGI可以读取和提供它。我们已经创建了一个配置文件和一个Upstart脚本。我们的uWSGI进程将侦听套接字并使用uwsgi
协议进行通信。
我们现在正处于将Nginx配置为反向代理的地步。Nginx能够使用uwsgi
协议代理与uWSGI进行通信。这是一种比HTTP更快的协议,性能更好。
我们将要设置的Nginx配置非常简单。sites-available
在Nginx的配置层次结构中的目录中创建一个新文件。我们将调用我们的文件myapp
以匹配我们一直使用的应用名称:
sudo nano /etc/nginx/sites-available/myapp
在此文件中,我们可以指定此服务器块应响应的端口号和域名。在我们的例子中,我们将使用默认端口80:
代码语言:javascript复制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
检查配置文件是否存在语法错误:
代码语言:javascript复制sudo service nginx configtest
如果它报告未检测到任何问题,请重新启动服务器以实施更改:
代码语言:javascript复制sudo service nginx restart
一旦Nginx重新启动,您应该可以转到服务器的域名或IP地址(没有端口号)并查看您配置的应用程序:
结论
如果您已经做到这一点,那么您已经创建了一个简单的WSGI应用程序,并且可以深入了解如何设计更复杂的应用程序。我们已将uWSGI应用程序容器/服务器安装到专用虚拟环境中,以便为我们的应用程序提供服务。我们制作了一个配置文件和一个Upstart脚本来自动执行此过程。在uWSGI服务器的前面,我们设置了一个Nginx反向代理,它可以使用uwsgi
有线协议与uWSGI进程通信。
在设置实际生产环境时,您可以轻松了解如何扩展它。例如,uWSGI能够使用称为“emperor模式”的东西管理多个应用程序。您可以扩展Nginx配置以在uWSGI实例之间进行负载平衡,或者为您的应用程序处理静态文件。在为多个应用程序提供服务时,根据您的需要,全局安装uWSGI而不是虚拟环境可能符合您的最佳利益。这些组件都非常灵活,因此您应该能够调整其配置以适应许多不同的场景。
想要了解更多关于设置uWSGI和Nginx以服务Python应用程序的相关教程,请前往腾讯云 社区学习更多知识。
参考文献:《How To Set Up uWSGI and Nginx to Serve Python Apps on Ubuntu 14.04》