如何在 Ubuntu 22.04 上使用 uWSGI 和 Nginx 服务 Flask 应用程序
介绍
在本指南中,您将构建一个使用 Nginx 作为前端反向代理的 Python 应用程序。
先决条件
在开始本指南之前,您应该:
- <李> 安装了 Ubuntu 22.04 的服务器和具有 sudo 权限的非根用户。请按照我们的初始服务器设置指南获取指导。 <李> 按照如何在 Ubuntu 22.04 上安装 Nginx 的步骤 1 到 3 安装 Nginx。 <李> 配置为指向您的服务器的域名。您可以购买有关域和 DNS 的文档。本教程假设您已经创建了以下 DNS 记录:
- 一条 A 记录,其中
your_domain
指向您服务器的公共 IP 地址。 - 带有
www.your_domain
的 A 记录指向您服务器的公共 IP 地址。
此外,熟悉 uWSGI、您将在本指南中设置的应用程序服务器以及 WSGI 规范可能会有所帮助。对定义和概念的讨论详细介绍了这两者。
第 1 步 — 从 Ubuntu 存储库安装组件
您的第一步是从 Ubuntu 存储库安装您需要的所有部分。您需要安装的包包括 pip
,Python 包管理器,用于管理您的 Python 组件。您还将获得构建 uWSGI 所需的 Python 开发文件。
首先,更新本地包索引:
- sudo apt update
然后安装允许您构建 Python 环境的包。这些将包括 python3-pip
,以及健壮的编程环境所需的更多包和开发工具:
- sudo apt install python3-pip python3-dev build-essential libssl-dev libffi-dev python3-setuptools
有了这些包,您就可以继续为您的项目创建虚拟环境了。
第 2 步 — 创建 Python 虚拟环境
Python 虚拟环境是一个独立的项目目录,其中包含特定版本的 Python 和给定项目所需的 Python 模块。这对于通过单独管理每个应用程序的依赖项来将一个应用程序与同一系统上的其他应用程序隔离开来很有用。在此步骤中,您将设置一个 Python 虚拟环境,您将从中运行 Flask 应用程序。
首先安装 python3-venv
包,它将安装 venv
模块:
- sudo apt install python3-venv
接下来,为您的 Flask 项目创建一个父目录:
- mkdir ~/myproject
创建后进入目录:
- cd ~/myproject
通过键入以下命令创建一个虚拟环境来存储 Flask 项目的 Python 需求:
- python3.10 -m venv myprojectenv
这会将 Python 和 pip
的本地副本安装到项目目录中名为 myprojectenv
的目录中。
在虚拟环境中安装应用程序之前,您需要激活它。键入以下内容:
- source myprojectenv/bin/activate
您的提示符将发生变化,表明您现在正在虚拟环境中操作。它看起来像这样:(myprojectenv)user@host:~/myproject$
。
第 3 步 — 设置 Flask 应用程序
现在您已进入虚拟环境,您可以安装 Flask 和 uWSGI,然后开始设计您的应用程序。
首先,使用 pip
的本地实例安装 wheel
以确保即使缺少 wheel 存档也能安装您的包:
- pip install wheel
注意:无论您使用的是哪个版本的 Python,当虚拟环境被激活时,您应该使用 pip
命令(而不是 pip3
)。
接下来,安装 Flask 和 uWSGI:
- pip install uwsgi flask
创建示例应用程序
现在您已经有了 Flask,您可以创建一个示例应用程序。 Flask 是一个微框架。它不包括功能更全的框架可能包含的许多工具,主要作为一个模块存在,您可以将其导入项目以帮助您初始化 Web 应用程序。
虽然您的应用程序可能更复杂,但在此示例中,您将在名为 myproject.py
的单个文件中创建 Flask 应用程序。使用 nano
或您最喜欢的文本编辑器打开 myproject.py
:
- nano ~/myproject/myproject.py
应用程序代码将存在于此文件中。它将导入 Flask 并实例化一个 Flask 对象。您可以使用它来定义在请求特定路由时要运行的功能:
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
,然后按 输入
。
如果您遵循初始服务器设置指南,则应该启用 UFW 防火墙。要测试应用程序,您需要允许访问端口 5000
:
- sudo ufw allow 5000
现在,您可以通过键入以下内容来测试您的 Flask 应用程序:
- python myproject.py
您将看到如下输出,包括一个有用的警告,提醒您不要在生产中使用此服务器设置:
OutputWARNING: This is a development server. Do not use it in a production deployment. Use a production WSGI server instead.
* Serving Flask app 'myproject'
* Debug mode: off
* Running on all addresses (0.0.0.0)
WARNING: This is a development server. Do not use it in a production deployment.
* Running on http://127.0.0.1:5000
* Running on http://your_server:5000 (Press CTRL+C to quit)
在网络浏览器中访问服务器的 IP 地址,后跟 :5000
:
http://your_server_ip:5000
你会看到这样的东西:

完成后,在终端窗口中按 CTRL + C
停止 Flask 开发服务器。
创建 WSGI 入口点
接下来,创建一个文件作为您的应用程序的入口点。这将告诉你的 uWSGI 服务器如何与之交互。
调用文件 wsgi.py
:
- nano ~/myproject/wsgi.py
在此文件中,从您的应用程序导入 Flask 实例,然后运行它:
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
再次在您的 Web 浏览器中访问您服务器的 IP 地址并在末尾附加 :5000
:
http://your_server_ip:5000
您将再次看到应用程序的输出:

当您确认它正常运行时,请在您的终端窗口中按 CTRL + C
。
你现在已经完成了你的虚拟环境,所以你可以停用它:
- deactivate
任何 Python 命令现在将再次使用系统的 Python 环境。
创建一个 uWSGI 配置文件
您已经测试过 uWSGI 能够为您的应用程序提供服务,但是您需要更健壮的东西来长期使用。您可以为此创建一个带有相关选项的 uWSGI 配置文件。
将该文件放在您的项目目录中并将其命名为 myproject.ini
:
- nano ~/myproject/myproject.ini
在内部,使用 [uwsgi]
标头启动文件,以便 uWSGI 知道应用设置。在此之下,指定模块本身——通过引用 wsgi.py
文件减去扩展名——以及文件中的可调用对象 app
:
[uwsgi]
module = wsgi:app
接下来,告诉 uWSGI 以主模式启动并生成五个工作进程来服务实际请求:
[uwsgi]
module = wsgi:app
master = true
processes = 5
当您进行测试时,您在网络端口上公开了 uWSGI。但是,您将使用 Nginx 来处理实际的客户端连接,然后将请求传递给 uWSGI。由于这些组件在同一台计算机上运行,因此 Unix 套接字更可取,因为它更快、更安全。调用套接字myproject.sock
,放在这个目录下。
接下来,更改套接字的权限。稍后您将授予 uWSGI 进程的 Nginx 组所有权,因此您需要确保套接字的组所有者可以从中读取信息并向其写入信息。另外,添加 vacuum
选项并将其设置为 true;这将在进程停止时清理套接字:
[uwsgi]
module = wsgi:app
master = true
processes = 5
socket = myproject.sock
chmod-socket = 660
vacuum = true
最后要做的是设置 die-on-term
选项。这有助于确保 init 系统和 uWSGI 对每个进程信号的含义具有相同的假设。设置此对齐两个系统组件,实现预期的行为:
[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 的初始化系统在服务器启动时自动启动 uWSGI 并为 Flask 应用程序提供服务。
在 /etc/systemd/system
目录中创建一个以 .service
结尾的单元文件开始:
- sudo nano /etc/systemd/system/myproject.service
在内部,从 [Unit]
部分开始,该部分用于指定元数据和依赖项。然后在此处放置服务的描述,并告诉 init 系统仅在达到网络目标后才启动此服务:
[Unit]
Description=uWSGI instance to serve myproject
After=network.target
接下来,创建 [Service]
部分。这将指定您希望进程在其下运行的用户和组。将流程的所有权授予您的普通用户帐户,因为它拥有所有相关文件。然后将组所有权赋予 www-data 组,以便 Nginx 可以与 uWSGI 进程通信(这是 Nginx 在 Ubuntu 上默认运行的组)。请记住将此处的用户名替换为您的用户名:
[Unit]
Description=uWSGI instance to serve myproject
After=network.target
[Service]
User=sammy
Group=www-data
接下来,映射工作目录并设置 PATH
环境变量,以便 init 系统知道该进程的可执行文件位于您的虚拟环境中。此外,指定启动服务的命令。 Systemd 要求您提供安装在您的虚拟环境中的 uWSGI 可执行文件的完整路径。在这里,我们传递您在项目目录中创建的 .ini
配置文件的名称。
请记住将用户名和项目路径替换为您自己的信息:
[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 将此服务链接到什么。在这种情况下,将服务设置为在常规多用户系统启动并运行时启动:
[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 服务之前,您需要更改权限,因为在 Ubuntu 22.04 及更新版本中,默认情况下 Nginx www-data
用户将无法读取您主目录中的文件。这可以防止您在主目录之外提供 Web 应用程序。一个快速修复是使用 chgrp
更改与您的主目录关联的组:
- sudo chgrp www-data /home/sammy
这将允许 Nginx 查看您的主目录的内容,它需要这些内容才能访问套接字文件。它不会将您主目录中的任何文件暴露给网络。
您现在可以启动您创建的 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: ena>
Active: active (running) since Fri 2022-08-05 17:22:05 UTC; 6s ago
Main PID: 4953 (uwsgi)
Tasks: 6 (limit: 2327)
Memory: 20.9M
CPU: 241ms
CGroup: /system.slice/myproject.service
├─4953 /home/sammy/myproject/myprojectenv/bin/uwsgi --ini myproject.ini
├─4954 /home/sammy/myproject/myprojectenv/bin/uwsgi --ini myproject.ini
├─4955 /home/sammy/myproject/myprojectenv/bin/uwsgi --ini myproject.ini
├─4956 /home/sammy/myproject/myprojectenv/bin/uwsgi --ini myproject.ini
├─4957 /home/sammy/myproject/myprojectenv/bin/uwsgi --ini myproject.ini
└─4958 /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
。此外,告诉它使用这个块来请求你的服务器的域名:
server {
listen 80;
server_name your_domain www.your_domain;
}
接下来,添加一个匹配每个请求的位置块。在这个块中,包含指定一些需要设置的通用 uWSGI 参数的 uwsgi_params
文件。然后将请求传递到您使用 uwsgi_pass
指令定义的套接字:
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 时,该过程会自动在 sites-available
目录中设置一个名为 default
的服务器块配置文件,然后在该文件和 sites-enabled
目录。如果您保留此符号链接,默认
配置将阻止您的站点加载。您可以使用以下命令删除链接:
- sudo unlink /etc/nginx/sites-enabled/default
之后,您可以通过键入以下内容来测试语法错误:
- sudo nginx -t
如果这返回但没有指示任何问题,请重新启动 Nginx 进程以读取新配置:
- sudo systemctl restart nginx
最后,再次调整防火墙。您不再需要通过端口 5000
进行访问,因此您可以删除该规则。然后,您可以允许访问 Nginx 服务器:
- sudo ufw delete allow 5000
- sudo ufw allow 'Nginx Full'
您现在可以在网络浏览器中导航到服务器的域名:
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 获取免费证书。
首先,使用 apt
安装 Certbot 及其 Nginx 插件:
- sudo apt install certbot python3-certbot-nginx
Certbot 提供了多种通过插件获取 SSL 证书的方式。 Nginx 插件将负责重新配置 Nginx 并在必要时重新加载配置。要使用此插件,请键入以下内容:
- sudo certbot --nginx -d your_domain -d www.your_domain
这将使用 --nginx
插件运行 certbot
,使用 -d
指定您希望证书有效的名称。
如果这是您第一次在此服务器上运行 certbot
,系统将提示您输入电子邮件地址并同意服务条款。这样做之后,certbot
将与 Let's Encrypt 服务器通信,然后运行挑战以验证您是否控制您为其请求证书的域。
配置将被更新,Nginx 将重新加载以获取新设置。 certbot
将以一条消息结束,告诉您该过程已成功以及您的证书存储在哪里:
OutputSuccessfully received certificate.
Certificate is saved at: /etc/letsencrypt/live/your_domain/fullchain.pem
Key is saved at: /etc/letsencrypt/live/your_domain/privkey.pem
This certificate expires on 2022-11-03.
These files will be updated when the certificate renews.
Certbot has set up a scheduled task to automatically renew this certificate in the background.
Deploying certificate
Successfully deployed certificate for your_domain to /etc/nginx/sites-enabled/myproject
Successfully deployed certificate for your_domain to /etc/nginx/sites-enabled/myproject
Congratulations! You have successfully enabled HTTPS on https://your_domain and https://www.your_domain
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
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 应用程序提供服务。