如何在 Ubuntu 20.04 上使用 uWSGI 和 Nginx 服务 Flask 应用程序

此教程的早期版本是由 Justin Ellingwood_

介绍

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

前提条件

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

  • 安装了 Ubuntu 20.04 的服务器和具有 sudo 特权的非 root 用户。 遵循我们的 初始服务器设置指南以获取指导
  • Nginx 安装,遵循 How To Install Nginx on Ubuntu 20.04步骤 1 到 3)。
  • 配置到您的服务器的域名。 您可以在 Namecheap购买一个或在 Freenom免费获得一个。 您可以通过遵循相关的 [域和 DNS(LINK4)的文档] 学习如何将域名指向 DigitalOcean。 本教程假定您已经创建了以下公共 DNS 记录:(

此外,有助于熟悉 uWSGI,您将在本指南中设置的应用程序服务器和 WSGI 规格。

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

你的第一步将是从Ubuntu存储库中安装你需要的所有部件。你需要安装的包包括pip,Python包管理器,以管理你的Python组件。

首先,更新本地包索引:

1sudo apt update

然后安装可用于构建 Python 环境的包,这些包将包括python3-pip,以及为强大的编程环境所需的其他一些包和开发工具:

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

有了这些包,您已经准备好为您的项目创建一个虚拟环境。

第2步:创建一个Python虚拟环境

Python 虚拟环境是一个单独的项目目录,包含特定版本的 Python 和所需的 Python 模块,这有助于通过单独管理每个依赖程序来将一个应用程序与其他应用程序隔离。

开始安装python3-venv包,该包将安装venv模块:

1sudo apt install python3-venv

接下来,为您的 Flask 项目创建一个家长目录:

1mkdir ~/myproject

创建后移动到目录:

1cd ~/myproject

创建一个虚拟环境来存储您的 Flask 项目的 Python 要求,键入:

1python3.8 -m venv myprojectenv

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

在在虚拟环境中安装应用程序之前,您需要激活它。

1source myprojectenv/bin/activate

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

步骤 3 – 设置一个Flask应用程序

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

首先,使用本地实例pip安装轮子,以确保您的软件包会安装,即使它们缺少轮子档案:

1pip install wheel

<$>[注] :无论您正在使用哪个版本的Python,当虚拟环境被激活时,您应该使用pip命令(而不是pip3)。

接下来,安装 Flask 和 uWSGI:

1pip install uwsgi flask

创建样本应用程序

现在你有Flask可用,你可以创建一个样本应用程序. Flask是一个微框架. 它不包括更多的功能框架可能的许多工具,并且主要是作为一个模块,你可以导入到你的项目,以帮助你初始化一个Web应用程序。

虽然你的应用程序可能更复杂,但在这个例子中,你将创建你的Flask应用程序在一个文件中,名为myproject.py:

1nano ~/myproject/myproject.py

应用程序代码将生活在这个文件中. 它将导入Flask并实例化Flask对象. 您可以使用此功能来定义您想要在请求特定路径时运行的函数:

 1[label ~/myproject/myproject.py]
 2from flask import Flask
 3app = Flask(__name__)
 4
 5@app.route("/")
 6def hello():
 7    return "<h1 style='color:blue'>Hello There!</h1>"
 8
 9if __name__ == "__main__":
10    app.run(host='0.0.0.0')

基本上,这定义了要向访问根域的人提供的内容。在完成时,保存并关闭文件. 如果您使用nano来编辑文件,就像上一个示例一样,请按CTRL + X,Y,然后按ENTER

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

1sudo ufw allow 5000

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

1python myproject.py

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

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

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

1http://your_server_ip:5000

你会看到这样的东西:

Flask sample app

当你完成时,在终端窗口中按CTRL + C,停止Flask开发服务器。

创建 WSGI 入口点

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

请点击wsgi.py文件:

1nano ~/myproject/wsgi.py

在此文件中,从应用程序中导入 Flask 实例,然后运行它:

1[label ~/myproject/wsgi.py]
2from myproject import app
3
4if __name__ == "__main__":
5    app.run()

保存并关闭文件,当你完成。

步骤 4 – 配置 uWSGI

您的应用程序现在已经创建了一个入口点,您可以继续配置 uWSGI。

测试 uWSGI 是否可以服务应用程序

作为第一步,测试以确保 uWSGI 可以通过传递您的入口点的名称来正确地服务您的应用程序。 这是由模块名称(减去 .py 扩展)以及应用程序内可调用的名称构建的。

此外,指定插件,以便它将在公共可用接口上启动,以及协议,以便它将使用HTTP而不是uwsgi二进制协议。

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

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

1http://your_server_ip:5000

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

Flask sample app

当您确认它正常工作时,请在终端窗口中按CTRL + C

现在你已经完成了虚拟环境,所以你可以禁用它:

1deactivate

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

创建 uWSGI 配置文件

您已经测试了 uWSGI 是否能够为您的应用提供服务,但最终您将想要更强大的东西,可用于长期使用。

将该文件放入您的项目目录,并将其命名为myproject.ini:

1nano ~/myproject/myproject.ini

在内部,用[uwsgi]标题启动文件,以便 uWSGI 知道如何应用设置。下方,指定模块本身 - 通过引用wsgi.py文件减去扩展 - 和文件内可调用的app:

1[label ~/myproject/myproject.ini]
2[uwsgi]
3module = wsgi:app

接下来,告诉 uWSGI 在主模式下启动并生成五个工人流程,以满足实际请求:

1[label ~/myproject/myproject.ini]
2[uwsgi]
3module = wsgi:app
4
5master = true
6processes = 5

当你在测试时,你在网络端口上暴露了uWSGI,但是,你会使用nginx来处理实际的客户端连接,然后将请求传递给uWSGI。

接下来,更改插槽上的权限. 您将在以后给 Nginx 群组对 uWSGI 流程的所有权,所以您需要确保插槽的群组所有者可以从它读取信息并写入它。

 1[label ~/myproject/myproject.ini]
 2[uwsgi]
 3module = wsgi:app
 4
 5master = true
 6processes = 5
 7
 8socket = myproject.sock
 9chmod-socket = 660
10vacuum = true

最后要做的是设置死于期限选项,这可以帮助确保 init 系统和 uWSGI 对每个过程信号的含义有相同的假设。

 1[label ~/myproject/myproject.ini]
 2[uwsgi]
 3module = wsgi:app
 4
 5master = true
 6processes = 5
 7
 8socket = myproject.sock
 9chmod-socket = 660
10vacuum = true
11
12die-on-term = true

您可能已经注意到,这些行不像您从命令行中所做的那样指定一个协议,这是因为默认情况下,uWSGI使用uwsgi协议进行交谈,这是一个快速的二进制协议,旨在与其他服务器进行通信。

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

为了让您更灵活地管理 Flask 应用程序,您现在可以将其配置为运行为 systemd 服务。

步骤 5 — 创建一个 systemd 单元文件

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

/etc/systemd/system目录中创建一个终止在.service的单元文件,开始:

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

在内部,从[单位]部分开始,该部分用于指定元数据和依赖性,然后将服务的描述放到这里,并告诉 init 系统在实现网络目标后才启动:

1[label /etc/systemd/system/myproject.service]
2[Unit]
3Description=uWSGI instance to serve myproject
4After=network.target

接下来,打开[服务]部分。这将指定您希望该过程运行的用户和组。 给您的常规用户帐户拥有该过程的所有权,因为它拥有所有相关文件。 然后给组拥有 www-data组,以便 Nginx 可以轻松地与 uWSGI 流程进行通信。

1[label /etc/systemd/system/myproject.service]
2[Unit]
3Description=uWSGI instance to serve myproject
4After=network.target
5
6[Service]
7User=sammy
8Group=www-data

接下来,绘制工作目录并设置PATH环境变量,以便 init 系统知道该过程的可执行文件位于您的虚拟环境中。 此外,指定启动服务的命令。

请记住用自己的信息替换用户名和项目路径:

 1[label /etc/systemd/system/myproject.service]
 2[Unit]
 3Description=uWSGI instance to serve myproject
 4After=network.target
 5
 6[Service]
 7User=sammy
 8Group=www-data
 9WorkingDirectory=/home/sammy/myproject
10Environment="PATH=/home/sammy/myproject/myprojectenv/bin"
11ExecStart=/home/sammy/myproject/myprojectenv/bin/uwsgi --ini myproject.ini

最后,添加一个[安装]部分,这将告诉系统d如果您允许该服务在启动时启动,该服务将链接到什么。

 1[label /etc/systemd/system/myproject.service]
 2[Unit]
 3Description=uWSGI instance to serve myproject
 4After=network.target
 5
 6[Service]
 7User=sammy
 8Group=www-data
 9WorkingDirectory=/home/sammy/myproject
10Environment="PATH=/home/sammy/myproject/myprojectenv/bin"
11ExecStart=/home/sammy/myproject/myprojectenv/bin/uwsgi --ini myproject.ini
12
13[Install]
14WantedBy=multi-user.target

这样,你的 systemd 服务文件就完成了. 现在保存并关闭它。

您现在可以启动您创建的 uWSGI 服务:

1sudo systemctl start myproject

然后启用它以便在 boot 上启动:

1sudo systemctl enable myproject

查看状态:

1sudo systemctl status myproject

你会看到这样的输出:

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

如果您看到任何错误,请确保在继续使用教程之前解决这些错误,否则您可以继续配置您的 Nginx 安装以将请求传输到myproject.sock插槽。

步骤 6 — 将 Nginx 配置为代理请求

您的 uWSGI 应用程序服务器现在正在运行,正在等待项目目录中的插件文件上的请求。

首先,在 Nginx 的可用站点目录中创建一个新的服务器封锁配置文件,以保持与其他指南相一致,下面的示例称之为myproject:

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

打开服务器块,并告诉 Nginx 倾听默认端口 80。

1[label /etc/nginx/sites-available/myproject]
2server {
3    listen 80;
4    server_name your_domain www.your_domain;
5}

接下来,添加一个与每个请求匹配的位置块。在这个块中,包括指定一些需要设置的一般 uWSGI 参数的 uwsgi_params 文件。

 1[label /etc/nginx/sites-available/myproject]
 2server {
 3    listen 80;
 4    server_name your_domain www.your_domain;
 5
 6    location / {
 7        include uwsgi_params;
 8        uwsgi_pass unix:/home/sammy/myproject/myproject.sock;
 9    }
10}

保存并关闭文件,当你完成。

要启用您刚刚创建的 Nginx 服务器封锁配置,请将文件链接到网站启用目录:

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

当您安装 Nginx 时,该过程会自动在网站可用目录中设置一个名为默认的服务器封锁配置文件,然后在该文件与网站可用目录之间创建一个符号链接。

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

接下来,您可以通过键入测试语法错误:

1sudo nginx -t

如果返回不表示任何问题,请重新启动 Nginx 流程以阅读新配置:

1sudo systemctl restart nginx

最后,重新调整防火墙,您不再需要通过端口5000访问,因此您可以删除该规则。

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

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

1http://your_domain

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

Flask sample app

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

  • 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获得免费证书(https://letsencrypt.org/),生成自签证书,或从商业提供商购买证书。

首先,安装 Certbot 及其 Nginx 插件,使用apt:

1sudo apt install certbot python3-certbot-nginx

Certbot 提供了通过插件获取 SSL 证书的多种方式。 Nginx 插件将负责重新配置 Nginx 并在必要时重新加载配置。

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

这将运行certbot--nginx插件,使用-d来指定您希望证书有效的名称。

如果这是你第一次在这个服务器上运行certbot,你将被要求输入电子邮件地址并同意服务条款. 这样做后,certbot将与 Let’s Encrypt 服务器进行通信,然后运行一个挑战来验证你控制的域,你正在请求证书。

如果成功,certbot 会问你想如何配置你的 HTTPS 设置:

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

选择你的选择,然后点击ENTER。配置将被更新,Nginx将重新加载以获取新的设置。

 1[secondary_label Output]
 2IMPORTANT NOTES:
 3 - Congratulations! Your certificate and chain have been saved at:
 4   /etc/letsencrypt/live/your_domain/fullchain.pem
 5   Your key file has been saved at:
 6   /etc/letsencrypt/live/your_domain/privkey.pem
 7   Your cert will expire on 2020-08-18. To obtain a new or tweaked
 8   version of this certificate in the future, simply run certbot again
 9   with the "certonly" option. To non-interactively renew *all* of
10   your certificates, run "certbot renew"
11 - Your account credentials have been saved in your Certbot
12   configuration directory at /etc/letsencrypt. You should make a
13   secure backup of this folder now. This configuration directory will
14   also contain certificates and private keys obtained by Certbot so
15   making regular backups of this folder is ideal.
16 - If you like Certbot, please consider supporting our work by:
17
18   Donating to ISRG / Let's Encrypt:   https://letsencrypt.org/donate
19   Donating to EFF:                    https://eff.org/donate-le

如果您在前提条件中遵循了 Nginx 安装说明,您将不再需要多余的 HTTP 配置文件许可:

1sudo ufw delete allow 'Nginx HTTP'

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

1https://your_domain

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

结论

在本指南中,您创建并在Python虚拟环境中保护了基本的Flask应用程序,然后您创建了一个WSGI入口点,以便任何具有WSGI能力的应用程序服务器可以与其进行交互,然后配置了uWSGI应用程序服务器以提供此功能。

Flask 是一个简单但灵活的框架,旨在为您的应用提供功能,而无需过于限制结构或设计。

Published At
Categories with 技术
comments powered by Disqus