如何在Ubuntu 18.04上使用uWSGI和Nginx为Flask应用程序提供服务

12/02/2021 14:18 下午 posted in  Python

转载于:How To Serve Flask Applications with uWSGI and Nginx on Ubuntu 18.04

说明:

在本指南中,您将使用Ubuntu 18.04上的Flask微框架构建Python应用程序。本文的大部分内容将涉及如何设置uWSGI应用程序服务器以及如何启动应用程序以及如何配置Nginx以充当前端反向代理。

准备:

在开始本指南之前,您应该:

  1. 安装了 Ubuntu 20.04 的服务器和具有 sudo 权限的非 root 用户。按照我们的初始服务器设置指南获取指导。
  2. Nginx的安装,下面通过3个步骤1中如何在Ubuntu 20.04安装Nginx的。
  3. 配置为指向您的服务器的域名。你可以购买一个Namecheap或免费获得一个上Freenom。您可以按照有关域和 DNS的相关文档了解如何将域指向 DigitalOcean 。本教程假设您已创建以下 DNS 记录:
    1. your_domain指向您服务器的公共 IP 地址的 A 记录。
    2. 指向您服务器的公共 IP 地址的 A 记录。www.your_domain

此外,熟悉 uWSGI、您将在本指南中设置的应用程序服务器以及 WSGI 规范可能会有所帮助。对定义和概念的讨论详细介绍了两者。

步骤1 - 从Ubuntu存储库安装组件

我们的第一步是从Ubuntu存储库安装我们需要的所有部分。我们将安装pipPython包管理器来管理我们的Python组件。我们还将获得构建uWSGI所需的Python开发文件。

首先,让我们更新本地包索引并安装允许我们构建Python环境的包。这些将包括python3-pip一些强大的编程环境所需的一些软件包和开发工具:

sudo apt update
sudo apt install python3-pip python3-dev build-essential libssl-dev libffi-dev python3-setuptools

有了这些软件包,让我们继续为我们的项目创建一个虚拟环境。

第2步 - 创建Python虚拟环境

Python 虚拟环境是一个自包含的项目目录,其中包含特定版本的 Python 和给定项目所需的 Python 模块。通过分别管理每个应用程序的依赖项,这对于将一个应用程序与同一系统上的其他应用程序隔离开来非常有用。在这一步中,您将设置一个 Python 虚拟环境,您将在其中运行 Flask 应用程序。

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

首先安装python3-venv软件包,安装venv模块:

sudo apt install python3-venv

接下来,让我们为Flask项目创建一个父目录。创建后移动到目录:

mkdir ~/my project
cd ~/myproject

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

python3.8 -m venv myprojectenv

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

在虚拟环境中安装应用程序之前,需要将其激活。输入以下命令:

source myprojectenv/bin/activate

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

第3步 - 设置Flask应用程序

现在您处于虚拟环境中,您可以安装 Flask 和 uWSGI,然后开始设计您的应用程序。

首先,wheel使用 的本地实例进行安装,pip以确保您的软件包即使缺少车轮档案也能安装:

pip install wheel

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

接下来,安装 Flask 和 uWSGI:

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')

本质上,这定义了向访问根域的任何人呈现的内容。完成后保存并关闭文件。如果您曾经nano编辑过文件,如上例所示,请按CTRL + X、Y、 然后按ENTER。

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

sudo ufw allow 5000

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

python myproject.py

您将看到如下输出,包括一个有用的警告,提醒您不要在生产中使用此服务器设置:

Output
* Serving Flask app "myproject" (lazy loading)
 * Environment: production
   WARNING: Do not use the development server in a production environment.
   Use a production WSGI server instead.
 * Debug mode: off
 * Running on http://0.0.0.0:5000/ (Press CTRL+C to quit)

访问您的服务器的 IP 地址,然后:5000在您的 Web 浏览器中:

http://your_server_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()

完成后保存并关闭文件。

第 4 步 – 配置 uWSGI

您的应用程序现在编写并建立了一个入口点。您可以继续配置 uWSGI。

测试uWSGI能否为应用服务

作为第一步,测试以确保 uWSGI 可以通过将您的入口点的名称传递给它来正确地为您的应用程序提供服务。这是由模块的名称(减去.py扩展名)加上应用程序中可调用的名称构成的。在本教程的上下文中,入口点的名称是wsgi:app。

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

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

:5000再次访问您的服务器的 IP 地址,并在您的 Web 浏览器中附加到末尾:

http://your_server_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并将其放在此目录中。

接下来,更改套接字的权限。稍后您将授予 Nginx 组对 uWSGI 进程的所有权,因此您需要确保套接字的组所有者可以从中读取信息并写入其中。另外,添加vacuum选项并将其设置为true;这将在进程停止时清理套接字:

~/myproject/myproject.ini
[uwsgi]
module = wsgi:app

master = true
processes = 5

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

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

~/my-project/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 通信。

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

这样,uWSGI 就在您的系统上配置好了。为了让您更灵活地管理 Flask 应用程序,您现在可以将其配置为作为 systemd 服务运行。

步骤 5 — 创建 systemd 单元文件

Systemd 是一套工具,为管理系统服务提供了快速灵活的初始化模型。创建 systemd 单元文件将允许 Ubuntu 的 init 系统在服务器启动时自动启动 uWSGI 并为 Flask 应用程序提供服务。

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

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 进程通信。请记住将此处的用户名替换为您的用户名:

/etc/systemd/system/myproject.service
[Unit]
Description=uWSGI instance to serve myproject
After=network.target

[Service]
User=sammy
Group=www-data

接下来,绘制工作目录并设置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

检查状态:

sudo systemctl status myproject

你会看到这样的输出:

Output
● myproject.service - uWSGI instance to serve myproject
     Loaded: loaded (/etc/systemd/system/myproject.service; enabled; vendor preset: enabled)
     Active: active (running) since Wed 2020-05-20 13:21:39 UTC; 8h ago
   Main PID: 22146 (uwsgi)
      Tasks: 6 (limit: 2345)
     Memory: 25.5M
     CGroup: /system.slice/myproject.service
             ├─22146 /home/sammy/myproject/myprojectenv/bin/uwsgi --ini myproject.ini
             ├─22161 /home/sammy/myproject/myprojectenv/bin/uwsgi --ini myproject.ini
             ├─22162 /home/sammy/myproject/myprojectenv/bin/uwsgi --ini myproject.ini
             ├─22163 /home/sammy/myproject/myprojectenv/bin/uwsgi --ini myproject.ini
             ├─22164 /home/sammy/myproject/myprojectenv/bin/uwsgi --ini myproject.ini
             └─22165 /home/sammy/myproject/myprojectenv/bin/uwsgi --ini myproject.ini
             

如果您看到任何错误,请确保在继续本教程之前解决它们。否则,您可以继续配置 Nginx 安装以将请求传递到myproject.sock套接字。

第 6 步 – 配置 Nginx 以代理请求

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

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

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

打开一个服务器块并告诉 Nginx 监听默认端口80。此外,告诉它使用这个块来请求你的服务器域名:

/etc/nginx/sites-available/myproject
server {
    listen 80;
    server_name your_domain www.your_domain;
}

接下来,添加一个匹配每个请求的位置块。在此块中,包含uwsgi_params指定一些需要设置的通用 uWSGI 参数的文件。然后将请求传递给您使用uwsgi_pass指令定义的套接字:

/etc/nginx/sites-available/myproject
server {
    listen 80;
    server_name your_domain www.your_domain;

    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

当您安装 Nginx 时,该过程会自动设置default在sites-available目录中命名的服务器块配置文件,然后在该文件和sites-enabled目录之间创建符号链接。如果您保留此符号链接,default配置将阻止您的站点加载。您可以使用以下命令删除链接:

sudo unlink /etc/nginx/sites-enabled/default

之后,您可以通过键入以下内容来测试语法错误:

sudo nginx -t

如果返回但未指示任何问题,请重新启动 Nginx 进程以读取新配置:

sudo systemctl restart nginx

最后,再次调整防火墙。您不再需要通过 port 进行访问5000,因此您可以删除该规则。然后,您可以允许访问 Nginx 服务器:

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

您现在可以在 Web 浏览器中导航到服务器的域名:

http://your_domain

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

如果您遇到任何错误,请尝试检查以下内容:

sudo less /var/log/nginx/error.log: 检查 Nginx 错误日志。
sudo less /var/log/nginx/access.log: 检查 Nginx 访问日志。
sudo journalctl -u nginx: 检查 Nginx 进程日志。
sudo journalctl -u myproject:检查您的 Flask 应用程序的 uWSGI 日志。

步骤 7 — 保护应用程序

为确保到您服务器的流量保持安全,请为您的域获取 SSL 证书。有多种方法可以做到这一点,包括从Let’s Encrypt获取免费证书、生成自签名证书或从商业提供商处购买。为方便起见,本教程解释了如何从 Let’s Encrypt 获取免费证书。

首先,使用以下命令安装 Certbot 及其 Nginx 插件apt:

sudo apt install certbot python3-certbot-nginx

Certbot 提供了多种通过插件获取 SSL 证书的方式。Nginx 插件将负责重新配置 Nginx 并在必要时重新加载配置。要使用此插件,请键入以下内容:

sudo certbot --nginx -d your_domain -d www.your_domain

这certbot与--nginx插件一起运行,-d用于指定您希望证书有效的名称。

如果这是您第一次certbot在此服务器上运行,系统会提示您输入电子邮件地址并同意服务条款。执行此操作后,certbot将与 Let’s Encrypt 服务器通信,然后运行质询以验证您是否控制要为其申请证书的域。

如果成功,certbot会询问您希望如何配置 HTTPS 设置:

Output
Please choose whether or not to redirect HTTP traffic to HTTPS, removing HTTP access.
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1: No redirect - Make no further changes to the webserver configuration.
2: Redirect - Make all requests redirect to secure HTTPS access. Choose this for
new sites, or if you're confident your site works on HTTPS. You can undo this
change by editing your web server's configuration.
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Select the appropriate number [1-2] then [enter] (press 'c' to cancel):

选择您的选择,然后点击ENTER。配置将被更新,Nginx 将重新加载以获取新设置。certbot将以一条消息结束,告诉您该过程已成功以及您的证书的存储位置:

Output
IMPORTANT NOTES:
 - Congratulations! Your certificate and chain have been saved at:
   /etc/letsencrypt/live/your_domain/fullchain.pem
   Your key file has been saved at:
   /etc/letsencrypt/live/your_domain/privkey.pem
   Your cert will expire on 2020-08-18. To obtain a new or tweaked
   version of this certificate in the future, simply run certbot again
   with the "certonly" option. To non-interactively renew *all* of
   your certificates, run "certbot renew"
 - Your account credentials have been saved in your Certbot
   configuration directory at /etc/letsencrypt. You should make a
   secure backup of this folder now. This configuration directory will
   also contain certificates and private keys obtained by Certbot so
   making regular backups of this folder is ideal.
 - If you like Certbot, please consider supporting our work by:

   Donating to ISRG / Let's Encrypt:   https://letsencrypt.org/donate
   Donating to EFF:                    https://eff.org/donate-le

如果您按照先决条件中的 Nginx 安装说明进行操作,您将不再需要冗余 HTTP 配置文件限额:

sudo ufw delete allow 'Nginx HTTP'

要验证配置,请再次导航到您的域,使用https://:

https://your_domain

您将再次看到您的应用程序输出,以及您浏览器的安全指示器,这表明该站点是安全的。

结论

在本指南中,您在 Python 虚拟环境中创建并保护了一个基本的 Flask 应用程序。然后您创建了一个 WSGI 入口点,以便任何具有 WSGI 功能的应用程序服务器都可以与之交互,然后配置 uWSGI 应用程序服务器以提供此功能。之后,您创建了一个 systemd 服务文件以在启动时自动启动应用程序服务器。您还创建了一个 Nginx 服务器块,它将 Web 客户端流量传递到应用程序服务器,从而中继外部请求,并使用 Let’s Encrypt 保护到您服务器的流量。

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