如何在Ubuntu18.04上使用uWSGI和Nginx服务Flask应用程序
介绍
在本指南中,您将在 Ubuntu 18.04 上使用 Flask 微框架构建 Python 应用程序。 本文的大部分内容将介绍如何设置 uWSGI 应用程序服务器 以及如何启动应用程序并配置 Nginx 作为前端反向代理。
先决条件
要完成本教程,您需要:
- 安装了 Ubuntu 18.04 的服务器和具有 sudo 权限并启用了防火墙的非 root 用户。 请按照我们的 初始服务器设置指南 获取指导。
- 按照 如何在 Ubuntu 18.04 上安装 Nginx 的 步骤 1 和 2 安装 Nginx。
- 配置为指向您的服务器的域名。 您可以在 Namecheap 上购买一个或在 Freenom 上免费获得一个。 您可以通过遵循有关域和 DNS 的相关 文档来了解如何将域指向 DigitalOcean。 请务必创建以下 DNS 记录:
- 带有
your_domain
的 A 记录指向您服务器的公共 IP 地址。 - 带有
www.your_domain
的 A 记录指向您服务器的公共 IP 地址。
- 带有
- 熟悉 uWSGI、我们的应用服务器和 WSGI 规范。 这个定义和概念的讨论 详细讨论了两者。
第 1 步 — 从 Ubuntu 存储库安装组件
第一步是从 Ubuntu 存储库中安装您需要的所有部件。 您将安装 Python 包管理器 pip
来管理您的 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 虚拟环境
接下来,设置一个虚拟环境,以便将您的 Flask 应用程序与系统上的其他 Python 文件隔离开来。
首先安装 python3-venv
包,它将安装 venv
模块:
sudo apt install python3-venv
接下来,为您的 Flask 项目创建一个父目录:
mkdir ~/myproject
然后在创建后进入目录:
cd ~/myproject
通过运行以下命令创建一个虚拟环境来存储 Flask 项目的 Python 需求:
python3.6 -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,您可以创建一个简单的应用程序。 您可能还记得,Flask 是一个微框架,不包含功能更全的框架可能提供的许多工具。 Flask 主要作为一个模块存在,您可以将其导入项目以帮助您初始化 Web 应用程序。
虽然您的应用程序可能更复杂,但您将在单个文件中创建 Flask 应用程序。 您可以使用您喜欢的文本编辑器创建文件。 对于本例,我们将使用 nano
并将其命名为 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)
在 Web 浏览器中访问服务器的 IP 地址,然后访问 :5000
:
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 是否可以为您的应用程序提供服务可能会有所帮助。
您可以通过将入口点的名称传递给 uWSGI 来做到这一点。 这是由模块的名称(减去 .py
扩展名)加上应用程序中可调用的名称构成的。 在这种情况下,入口点的名称是 wsgi:app
。
您还将指定套接字,以便它将在公开可用的接口和协议上启动,以便它将使用 HTTP 而不是 uwsgi
二进制协议。 使用您之前打开的相同端口号 5000
:
uwsgi --socket 0.0.0.0:5000 --protocol=http -w wsgi:app
再次在 Web 浏览器的末尾添加 :5000
访问服务器的 IP 地址:
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
选项在进程停止时清理套接字:
~/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 强制通信。
完成后,保存并关闭文件。
第 5 步 — 创建一个 systemd 单元文件
接下来,创建 systemd 服务单元文件。 创建一个 systemd 单元文件将允许 Ubuntu 的 init 系统在服务器启动时自动启动 uWSGI 并为 Flask 应用程序提供服务。
在/etc/systemd/system
目录下创建一个以.service
结尾的单元文件开始:
sudo nano /etc/systemd/system/myproject.service
在文件中,从 [Unit]
部分开始,该部分用于指定元数据和依赖项。 在此处描述您的服务并告诉初始化系统仅在达到网络目标后启动:
/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 Active: active (running) since Mon 2021-10-25 22:34:52 UTC; 14s ago Main PID: 9391 (uwsgi) Tasks: 6 (limit: 1151) CGroup: /system.slice/myproject.service ├─9391 /home/sammy/myproject/myprojectenv/bin/uwsgi --ini myproject.i ├─9410 /home/sammy/myproject/myprojectenv/bin/uwsgi --ini myproject.i ├─9411 /home/sammy/myproject/myprojectenv/bin/uwsgi --ini myproject.i ├─9412 /home/sammy/myproject/myprojectenv/bin/uwsgi --ini myproject.i ├─9413 /home/sammy/myproject/myprojectenv/bin/uwsgi --ini myproject.i └─9414 /home/sammy/myproject/myprojectenv/bin/uwsgi --ini myproject.i
如果您收到任何错误,请务必在继续本教程之前解决它们。
第 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
使用该目录中的文件,您可以通过运行以下命令来测试语法错误:
sudo nginx -t
如果返回没有任何问题,请重新启动 Nginx 进程以读取新配置:
sudo systemctl restart nginx
现在再次调整防火墙。 您不再需要通过端口 5000
访问,因此您可以删除该规则:
sudo ufw delete allow 5000
之后,您将允许访问 Nginx 服务器:
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 获取免费证书、 生成自签名证书 或 从另一个提供商处购买 并配置Nginx 按照 How to Create a Self-signed SSL Certificate for Nginx in Ubuntu 18.04 的步骤 2 到 6 来使用它。 为方便起见,我们将使用选项一进行演示。 如需完整教程,请查看 如何在 Ubuntu 18.04 上使用 Let's Encrypt 保护 Nginx。
首先,添加 Certbot Ubuntu 存储库:
sudo add-apt-repository ppa:certbot/certbot
您需要按 ENTER
接受。
接下来,使用 apt
安装 Certbot 的 Nginx 包:
sudo apt install python-certbot-nginx
Certbot 提供了多种通过插件获取 SSL 证书的方式。 Nginx 插件将负责重新配置 Nginx 并在必要时重新加载配置。 要使用此插件,请运行以下命令:
sudo certbot --nginx -d your_domain -d www.your_domain
这将使用 --nginx
插件运行 certbot
,使用 -d
指定您希望证书有效的名称。
如果这是您第一次运行 certbot
,系统将提示您输入电子邮件地址并同意服务条款。 完成此操作后,certbot
将与 Let's Encrypt 服务器通信,然后运行质询以验证您是否控制了要为其请求证书的域。
如果成功,certbot
将询问您希望如何配置 HTTPS 设置:
OutputPlease 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
将以一条消息结束,告诉您该过程已成功以及您的证书存储在哪里:
OutputIMPORTANT 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 2022-01-24. 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 是一个非常灵活的框架,旨在为您的应用程序提供功能,而不会对结构和设计进行过多限制。 您可以使用本指南中描述的通用堆栈来为您想要设计的烧瓶应用程序提供服务。