如何在Ubuntu16.04上使用uWSGI和Nginx服务Flask应用程序

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

介绍

在本指南中,我们将在 Ubuntu 16.04 上使用 Flask 微框架设置一个简单的 Python 应用程序。 本文的大部分内容将是关于如何设置 uWSGI 应用程序服务器来启动应用程序和 Nginx 作为前端反向代理。

先决条件

在开始阅读本指南之前,您应该在服务器上配置一个非 root 用户。 此用户需要具有 sudo 权限,才能执行管理功能。 要了解如何设置,请按照我们的 初始服务器设置指南

要了解有关 uWSGI、我们的应用程序服务器和 WSGI 规范的更多信息,您可以阅读 本指南 的链接部分。 了解这些概念将使本指南更易于理解。

当您准备好继续时,请继续阅读。

从 Ubuntu 存储库安装组件

我们的第一步是从存储库中安装我们需要的所有部分。 我们将安装 Python 包管理器 pip,以便安装和管理我们的 Python 组件。 我们还将获得构建 uWSGI 所需的 Python 开发文件,我们现在也将安装 Nginx。

我们需要更新本地包索引,然后安装包。 您需要的包取决于您的项目是使用 Python 2 还是 Python 3。

如果您使用 Python 2,请键入:

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

相反,如果您使用的是 Python 3,请键入:

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

创建 Python 虚拟环境

接下来,我们将设置一个虚拟环境,以便将我们的 Flask 应用程序与系统上的其他 Python 文件隔离开来。

首先使用 pip 安装 virtualenv 包。

如果您使用 Python 2,请键入:

sudo pip install virtualenv

如果您使用的是 Python 3,请键入:

sudo pip3 install virtualenv

现在,我们可以为我们的 Flask 项目创建一个父目录。 创建后进入目录:

mkdir ~/myproject
cd ~/myproject

我们可以通过键入以下内容来创建一个虚拟环境来存储我们的 Flask 项目的 Python 需求:

virtualenv myprojectenv

这会将 Python 和 pip 的本地副本安装到项目目录中名为 myprojectenv 的目录中。

在我们在虚拟环境中安装应用程序之前,我们需要激活它。 您可以通过键入:

source myprojectenv/bin/activate

您的提示将更改以指示您现在正在虚拟环境中操作。 它看起来像这样 (myprojectenv)user@host:~/myproject$

设置 Flask 应用程序

现在您已进入虚拟环境,我们可以安装 Flask 和 uWSGI 并开始设计我们的应用程序:

安装 Flask 和 uWSGI

我们可以使用 pip 的本地实例来安装 Flask 和 uWSGI。 键入以下命令以获取这两个组件:

笔记

无论你使用的是哪个版本的 Python,在激活虚拟环境时,都应该使用 pip 命令(而不是 pip3)。


pip install uwsgi flask

创建示例应用程序

现在我们有了可用的 Flask,我们可以创建一个简单的应用程序。 Flask 是一个微型框架。 它不包括功能更全面的框架可能提供的许多工具,主要作为一个模块存在,您可以将其导入项目以帮助您初始化 Web 应用程序。

虽然您的应用程序可能更复杂,但我们将在单个文件中创建 Flask 应用程序,我们将其称为 myproject.py

nano ~/myproject/myproject.py

在这个文件中,我们将放置我们的应用程序代码。 基本上,我们需要导入 Flask 并实例化一个 Flask 对象。 我们可以使用它来定义在请求特定路由时应该运行的函数:

~/myproject/myproject.py

from flask import Flask
app = Flask(__name__)

@app.route("/")
def hello():
    return "<h1 style='color:blue'>Hello There!</h1>"

if __name__ == "__main__":
    app.run(host='0.0.0.0')

这基本上定义了访问根域时要呈现的内容。 完成后保存并关闭文件。

如果您遵循初始服务器设置指南,则应该启用 UFW 防火墙。 为了测试我们的应用程序,我们需要允许访问端口 5000。

通过键入以下命令打开端口 5000:

sudo ufw allow 5000

现在,您可以通过键入以下内容来测试您的 Flask 应用程序:

python myproject.py

在网络浏览器中访问服务器的域名或 IP 地址,后跟 :5000

http://server_domain_or_IP:5000

您应该看到如下内容:

完成后,在终端窗口中按 CTRL-C 几次以停止 Flask 开发服务器。

创建 WSGI 入口点

接下来,我们将创建一个文件作为应用程序的入口点。 这将告诉我们的 uWSGI 服务器如何与应用程序交互。

我们将调用文件 wsgi.py

nano ~/myproject/wsgi.py

该文件非常简单,我们可以简单地从我们的应用程序中导入 Flask 实例,然后运行它:

~/myproject/wsgi.py

from myproject import app

if __name__ == "__main__":
    app.run()

完成后保存并关闭文件。

配置 uWSGI

我们的应用程序现在已经编写完成,并且我们的入口点已经建立。 我们现在可以继续使用 uWSGI。

测试 uWSGI 服务

我们要做的第一件事是测试以确保 uWSGI 可以为我们的应用程序服务。

我们可以通过简单地将入口点的名称传递给它来做到这一点。 这是由模块的名称(减去 .py 扩展名,像往常一样)加上应用程序中可调用的名称构成的。 在我们的例子中,这将是 wsgi:app

我们还将指定套接字,以便它将在公开可用的接口和协议上启动,以便它将使用 HTTP 而不是 uwsgi 二进制协议。 我们将使用之前打开的相同端口号:

uwsgi --socket 0.0.0.0:5000 --protocol=http -w wsgi:app

再次在您的网络浏览器中访问您的服务器的域名或 IP 地址,并在末尾附加 :5000

http://server_domain_or_IP:5000

您应该再次看到应用程序的输出:

确认它运行正常后,在终端窗口中按 CTRL-C。

我们现在已经完成了我们的虚拟环境,所以我们可以停用它:

deactivate

任何 Python 命令现在都将再次使用系统的 Python 环境。

创建 uWSGI 配置文件

我们已经测试了 uWSGI 能够为我们的应用程序提供服务,但我们想要更健壮的东西来长期使用。 我们可以使用我们想要的选项创建一个 uWSGI 配置文件。

让我们把它放在我们的项目目录中并命名为 myproject.ini

nano ~/myproject/myproject.ini

在内部,我们将从 [uwsgi] 标头开始,以便 uWSGI 知道应用设置。 我们将通过引用我们的 wsgi.py 文件来指定模块,减去扩展名,并且文件中的可调用对象称为“app”:

~/myproject/myproject.ini

[uwsgi]
module = wsgi:app

接下来,我们将告诉 uWSGI 以主模式启动并生成五个工作进程来服务实际请求:

~/myproject/myproject.ini

[uwsgi]
module = wsgi:app

master = true
processes = 5

当我们进行测试时,我们在一个网络端口上暴露了 uWSGI。 但是,我们将使用 Nginx 来处理实际的客户端连接,然后将请求传递给 uWSGI。 由于这些组件在同一台计算机上运行,因此首选 Unix 套接字,因为它更安全、更快。 我们将调用套接字 myproject.sock 并将其放在此目录中。

我们还必须更改套接字的权限。 稍后我们将授予 uWSGI 进程的 Nginx 组所有权,因此我们需要确保套接字的组所有者可以从中读取信息并对其进行写入。 当进程停止时,我们还将通过添加“vacuum”选项来清理套接字:

~/myproject/myproject.ini

[uwsgi]
module = wsgi:app

master = true
processes = 5

socket = myproject.sock
chmod-socket = 660
vacuum = true

我们需要做的最后一件事是设置 die-on-term 选项。 这有助于确保 init 系统和 uWSGI 对每个进程信号的含义有相同的假设。 设置此项可以对齐两个系统组件,实现预期的行为:

~/myproject/myproject.ini

[uwsgi]
module = wsgi:app

master = true
processes = 5

socket = myproject.sock
chmod-socket = 660
vacuum = true

die-on-term = true

您可能已经注意到,我们没有像在命令行中那样指定协议。 这是因为默认情况下,uWSGI 使用 uwsgi 协议,这是一种旨在与其他服务器通信的快速二进制协议。 Nginx 可以原生地使用这个协议,所以最好使用它而不是通过 HTTP 强制通信。

完成后,保存并关闭文件。

创建一个 systemd 单元文件

我们需要处理的下一个部分是 systemd 服务单元文件。 创建一个 systemd 单元文件将允许 Ubuntu 的 init 系统在服务器启动时自动启动 uWSGI 并为我们的 Flask 应用程序提供服务。

在 /etc/systemd/system 目录中创建一个以 .service 结尾的单元文件以开始:

sudo nano /etc/systemd/system/myproject.service

在里面,我们将从 [Unit] 部分开始,它用于指定元数据和依赖项。 我们将在此处描述我们的服务,并告诉 init 系统仅在达到网络目标后才启动它:

/etc/systemd/system/myproject.service

[Unit]
Description=uWSGI instance to serve myproject
After=network.target

接下来,我们将打开 [Service] 部分。 我们将指定我们希望进程在其下运行的用户和组。 由于它拥有所有相关文件,因此我们将授予我们常规用户帐户对该过程的所有权。 我们将组所有权授予 www-data 组,以便 Nginx 可以轻松地与 uWSGI 进程通信。

然后,我们将绘制工作目录并设置 PATH 环境变量,以便 init 系统知道我们的进程可执行文件的位置(在我们的虚拟环境中)。 然后我们将指定启动服务的命令。 Systemd 要求我们提供 uWSGI 可执行文件的完整路径,该可执行文件安装在我们的虚拟环境中。 我们将传递我们在项目目录中创建的 .ini 配置文件的名称:

/etc/systemd/system/myproject.service

[Unit]
Description=uWSGI instance to serve myproject
After=network.target

[Service]
User=sammy
Group=www-data
WorkingDirectory=/home/sammy/myproject
Environment="PATH=/home/sammy/myproject/myprojectenv/bin"
ExecStart=/home/sammy/myproject/myprojectenv/bin/uwsgi --ini myproject.ini

最后,我们将添加一个 [Install] 部分。 如果我们允许它在启动时启动,这将告诉 systemd 将该服务链接到什么。 我们希望在常规多用户系统启动并运行时启动此服务:

/etc/systemd/system/myproject.service

[Unit]
Description=uWSGI instance to serve myproject
After=network.target

[Service]
User=sammy
Group=www-data
WorkingDirectory=/home/sammy/myproject
Environment="PATH=/home/sammy/myproject/myprojectenv/bin"
ExecStart=/home/sammy/myproject/myprojectenv/bin/uwsgi --ini myproject.ini

[Install]
WantedBy=multi-user.target

至此,我们的 systemd 服务文件就完成了。 现在保存并关闭它。

我们现在可以启动我们创建的 uWSGI 服务并启用它,以便它在启动时启动:

sudo systemctl start myproject
sudo systemctl enable myproject

将 Nginx 配置为代理请求

我们的 uWSGI 应用程序服务器现在应该已经启动并运行,等待项目目录中套接字文件的请求。 我们需要配置 Nginx 以使用 uwsgi 协议将 Web 请求传递到该套接字。

首先在 Nginx 的 sites-available 目录中创建一个新的服务器块配置文件。 我们将简称为 myproject 以与指南的其余部分保持一致:

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

打开一个服务器块并告诉 Nginx 监听默认端口 80。 我们还需要告诉它使用这个块来请求我们服务器的域名或 IP 地址:

/etc/nginx/sites-available/myproject

server {
    listen 80;
    server_name server_domain_or_IP;
}

我们需要添加的唯一另一件事是匹配每个请求的位置块。 在这个块中,我们将包含 uwsgi_params 文件,它指定了一些需要设置的通用 uWSGI 参数。 然后我们将请求传递给我们使用 uwsgi_pass 指令定义的套接字:

/etc/nginx/sites-available/myproject

server {
    listen 80;
    server_name server_domain_or_IP;

    location / {
        include uwsgi_params;
        uwsgi_pass unix:/home/sammy/myproject/myproject.sock;
    }
}

这实际上就是我们为应用程序提供服务所需的全部内容。 完成后保存并关闭文件。

要启用我们刚刚创建的 Nginx 服务器块配置,请将文件链接到 sites-enabled 目录:

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

使用该目录中的文件,我们可以通过键入以下内容来测试语法错误:

sudo nginx -t

如果返回没有任何问题,我们可以重新启动 Nginx 进程以读取我们的新配置:

sudo systemctl restart nginx

我们需要做的最后一件事是再次调整我们的防火墙。 我们不再需要通过端口 5000 访问,因此我们可以删除该规则。 然后我们可以允许访问 Nginx 服务器:

sudo ufw delete allow 5000
sudo ufw allow 'Nginx Full'

您现在应该可以在 Web 浏览器中访问服务器的域名或 IP 地址:

http://server_domain_or_IP

您应该看到您的应用程序输出:

笔记

配置 Nginx 后,下一步应该是使用 SSL/TLS 保护到服务器的流量。 这很重要,因为没有它,包括密码在内的所有信息都会以纯文本形式通过网络发送。

获取 SSL 证书以保护您的流量的最简单方法是使用 Let's Encrypt。 按照 本指南 在 Ubuntu 16.04 上设置 Let's Encrypt with Nginx。


结论

在本指南中,我们在 Python 虚拟环境中创建了一个简单的 Flask 应用程序。 我们创建了一个 WSGI 入口点,以便任何支持 WSGI 的应用程序服务器都可以与之交互,然后配置 uWSGI 应用程序服务器来提供此功能。 之后,我们创建了一个 systemd 服务文件以在启动时自动启动应用程序服务器。 我们创建了一个 Nginx 服务器块,它将 Web 客户端流量传递到应用程序服务器,中继外部请求。

Flask 是一个非常简单但非常灵活的框架,旨在为您的应用程序提供功能,而不会对结构和设计进行过多限制。 您可以使用本指南中描述的通用堆栈来为您设计的烧瓶应用程序提供服务。