在本指南中,我们将设置一个由uWSGI提供服务的简单WSGI应用程序。我们将使用Nginx Web服务器作为应用程序服务器的反向代理,以提供强大的连接处理。我们将在CentOS 7服务器上安装和配置这些组件,没有服务器的用户可以购买和使用腾讯云服务器或者直接使用腾讯云实验室CentOS服务器。
先声明一些专业术语
在我们学习之前,我们应该了解一下概念相关的令人困惑的术语。这三个单独的术语看似可以互换,但实际上有不同的含义:
WSGI应用程序要求
WSGI规范定义了Web服务器和堆栈的应用程序部分之间的接口。在此文中,“Web服务器”指的是uWSGI服务器,它负责使用WSGI规范将客户端请求转换为应用程序。这简化了通信过程并创建了松散耦合的组件,因此您可以轻松地更换而不会有太多麻烦。
Web服务器(uWSGI)必须能够通过触发定义的“可调用”来向应用程序发送请求。可调用只是应用程序的入口点,Web服务器可以使用某些参数调用函数。预期参数是环境变量的字典和web服务器(uWSGI)组件提供的。
作为响应,应用程序返回一个迭代,该迭代将用于生成客户端响应的主体。还将调用它作为参数接收的Web服务器组件。触发Web服务器可调用时的第一个参数是HTTP状态代码,第二个参数是元组列表,每个元组定义一个响应头和值以发送回客户端。
通过uWSGI在此实例中提供的交互“Web服务器”组件,我们只需要确保我们的应用程序具有上述能力。我们还将设置Nginx来处理实际的客户端请求并将它们代理到uWSGI服务器。
首先,我们需要在CentOS 7服务器上安装必要的组件。我们主要可以使用yum
和pip
。
首先,我们需要安装EPEL存储库,以便我们可以访问更多的软件包。我们可以通过输入yum
命令轻松完成:
sudo yum install epel-release
现在,我们可以安装我们的组件。我们需要获得Python开发库和头文件,pipPython包管理器以及Nginx
Web服务器和反向代理。我们还需要一个编译器来暂时构建uWSGI二进制文件:
sudo yum install python-pip python-devel nginx gcc
程序包安装完成后,您将可以访问pipPython程序包管理器。我们可以使用它来安装virtualenv
包,我们将用它来隔离我们的应用程序的Python环境与系统上可能存在的任何其他环境:
sudo pip install virtualenv
一旦完成,我们就可以开始为我们的应用程序创建一般结构。我们将创建上面讨论的虚拟环境,并将在此环境中安装uWSGI应用程序服务器。
我们将首先为我们的应用程序创建一个文件夹。这可以在更完整的应用程序中保存包含实际应用程序代码的嵌套文件夹。出于我们的目的,这个目录将简单地保存我们的虚拟环境和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.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
函数的原因。application函数它需要两个参数。
environ
是一个像环境变量一样的键值字典。第二个被调用的start_response
,是应用程序将在内部使用的名称,用于引用发送的Web服务器(uWSGI)可调用。这两个参数名称都需要被选择,因为它们用于定义的PEP 333规范中的示例WSGI交互。
我们的应用程序必须获取此信息并执行两项操作。首先,它必须使用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服务器可以读取各种格式的配置,但为简单起见,我们将使用.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套接字代替网络端口。这更安全,更快捷。
我们将指定自己的用户名来运行uwsgi
服务器并拥有套接字文件。我们将在下面创建一个目录/run
来放置套接字文件,以便uWSGI和Nginx都可以访问它。我们将调用套接字myapp.sock
本身。我们将权限更改为“664”
,以便Nginx可以写入它(我们将使用Nginx的组www-data
启动uWSGI。我们还将添加vacuum
选项,这将在进程停止时删除套接字:
[uwsgi]
module = wsgi:application
master = true
processes = 5
uid = user
socket = /run/uwsgi/myapp.sock
chown-socket = user:nginx
chmod-socket = 660
vacuum = true
我们需要一个最终选项,因为我们将创建一个systemd文件来启动我们的应用程序。Systemd
和uWSGI对SIGTERM
信号怎样处理应用程序有不同的方式。为了解决这种差异,以便可以使用die-on-termSystemd
按预期处理进程,我们只需要添加一个调用的选项,以便uWSGI终止进程而不是重新加载它:
[uwsgi]
module = wsgi:application
master = true
processes = 5
uid = user
socket = /run/uwsgi/myapp.sock
chown-socket = user:nginx
chmod-socket = 660
vacuum = true
die-on-term = true
完成后保存并关闭文件。此配置文件现在设置为与Upstart脚本一起使用。
我们可以在启动时开启uWSGI实例,以便我们的应用程序始终可用。为此,我们可以创建一个systemd单元文件。我们将它放在用户创建的单元文件的/etc/systemd/system
目录中。我们将调用单元文件uwsgi.service
:
sudo nano /etc/systemd/system/uwsgi.service
首先,我们从[Unit]
章节开始,我们可以在这里调整元数据。我们唯一要放在这里的是对我们服务的描述:
[Unit]
Description=uWSGI instance to serve myapp
接下来,我们将打开该[Service]
部分。因为我们使用的是虚拟环境,所以我们的服务启动命令将比传统命令更复杂。我们将使用一个ExecStartPre
命令来确保我们的套接字目录是正确的。这将被允许失败(通过在等号后面加上-
)以防它们已经被设置过。这将被传递到一个单独调用的bash
。
我们也将ExecStart
启动uWSGI的实际命令传递给bash
。这允许我们执行一些不同的命令,因为bash
只能运行一个命令。我们将使用它来更改我们的应用程序目录,激活虚拟环境,并使用我们创建的.ini
文件启动uWSGI:
[Unit]
Description=uWSGI instance to serve myapp
[Service]
ExecStartPre=-/usr/bin/bash -c 'mkdir -p /run/uwsgi; chown user:nginx /run/uwsgi'
ExecStart=/usr/bin/bash -c 'cd /home/user/myapp; source myappenv/bin/activate; uwsgi --ini myapp.ini'
剩下要做的就是制定[Install]
部分。这将决定我们激活单元时会发生什么。基本上,它指定了单元应自动启动的状态。我们要指定启用时,只要服务器处于多用户模式,该单元就应该自动启动:
[Unit]
Description=uWSGI instance to serve myapp
[Service]
ExecStartPre=-/usr/bin/bash -c 'mkdir -p /run/uwsgi; chown user:nginx /run/uwsgi'
ExecStart=/usr/bin/bash -c 'cd /home/user/myapp; source myappenv/bin/activate; uwsgi --ini myapp.ini'
[Install]
WantedBy=multi-user.target
完成上述配置后,保存并关闭文件。
现在,我们可以通过键入以下命令启动服务:
sudo systemctl start uwsgi
键入以下命令检查它是否开始没有问题:
systemctl status uwsgi
如果没有错误,请通过键入以下内容启用该服务:
sudo systemctl enable uwsgi
您可以通过键入以下内容随时停止服务:
sudo systemctl stop uwsgi
我们有WSGI应用程序,并已验证uWSGI可以读取和提供它。我们已经创建了一个配置文件和Systemd
单元文件。我们的uWSGI进程将侦听套接字并使用uwsgi
协议进行通信。
我们现在需要将Nginx
配置为反向代理。Nginx能够使用uwsgi协议代理与uWSGI进行通信。这是一种比HTTP更快的协议,性能更好。
我们将要设置的Nginx配置非常简单。我们将修改现有nginx.conf
文件并添加新的服务器块。打开文件sudo
进行编辑:
sudo nano /etc/nginx/nginx.conf
在默认服务器块之前,我们将添加自己的服务器块:
http {
. . .
include /etc/nginx/conf.d/*.conf;
server {
}
server {
listen 80 default_server;
server_name localhost;
. . .
我们创建的块将保存uWSGI代理的配置。以下配置项的其余部分会放在此块中。服务器块应侦听端口80并响应服务器的域名或IP地址:
server {
listen 80;
server_name server_domain_or_IP;
}
之后,我们可以打开一个可以处理所有请求的位置块。在这个块中,我们将包含/etc/nginx/uwsgi_params
文件中的uwsgi参数,我们将流量传递给uWSGI正在侦听的套接字:
server {
listen 80;
server_name server_domain_or_IP;
location / {
include uwsgi_params;
uwsgi_pass unix:/run/uwsgi/myapp.sock;
}
}
对于更完整的应用程序,我们可以进行一些改进。例如,我们可能会在此块之外定义许多上游uWSGI服务器,然后将它们传递给它。我们可能会包含更多uWSGI参数。我们也可以直接处理来自Nginx的任何静态文件,并将动态请求传递给uWSGI实例。
我们的三行应用程序中不需要任何这些功能,因此我们可以保存并关闭该文件。
您可以通过键入以下内容进行测试以确保您的Nginx配置有效:
sudo nginx -t
如果返回没有任何错误,请键入以下命令启动服务:
sudo systemctl start nginx
启动服务时开启Nginx:
sudo systemctl enable nginx
您应该能够访问服务器的域名或IP地址并查看您配置的应用程序
您已经创建了一个简单的WSGI应用程序,并且可以深入了解如何设计更复杂的应用程序。我们已将uWSGI应用程序容器/服务器安装到专用虚拟环境中,以便为我们的应用程序提供服务。我们制作了一个配置文件和一个Systemd单元文件来自动执行此过程。在uWSGI服务器的前面,我们设置了一个Nginx反向代理,它可以使用uwsgi有线协议与uWSGI进程通信。
在设置实际生产环境时,您可以轻松了解如何扩展它。例如,uWSGI能够使用“emperor模式”管理多个应用程序。您可以扩展Nginx配置以在uWSGI实例之间进行负载平衡,或者为您的应用程序处理静态文件。在为多个应用程序提供服务时,根据您的需要,可以全局安装uWSGI来代替虚拟环境可能更好一些。这些组件都非常灵活,因此您能够调整其配置以适应许多不同的场景。
参考文献:《How To Set Up uWSGI and Nginx to Serve Python Apps on CentOS 7》
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。