如何在Ubuntu 18.04上使用uWSGI和Nginx为Flask应用提供服务
前些天发现了一个巨牛的人工智能学习网站,通俗易懂,风趣幽默,忍不住分享一下给大家。点击跳转到网站。
简介
在本指南中,您将在 Ubuntu 18.04 上使用 Flask 微框架构建一个 Python 应用程序。本文的重点将是如何设置 uWSGI 应用服务器,以及如何启动应用程序并配置 Nginx 作为前端反向代理。
先决条件
要完成本教程,您需要:
-
安装了 Ubuntu 18.04 的服务器,并且有一个具有 sudo 权限和已启用防火墙的非 root 用户。请参考我们的初始服务器设置指南以获取指导。
-
已安装 Nginx,按照《在 Ubuntu 18.04 上安装 Nginx 的步骤 1 和 2》进行操作。
-
配置了指向您服务器的域名。您可以在 Namecheap 上购买一个,或者在 Freenom 上免费获取一个。您可以按照有关域名和 DNS 的相关文档来了解如何将域名指向 DigitalOcean。确保创建以下 DNS 记录:
- 将
your_domain
指向您服务器的公共 IP 地址的 A 记录。 - 将
www.your_domain
指向您服务器的公共 IP 地址的 A 记录。
- 将
-
熟悉 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 虚拟环境
接下来,为了将您的 Flask 应用程序与系统上的其他 Python 文件隔离开来,设置一个虚拟环境。
首先,安装 python3-venv
软件包,它将安装 venv
模块:
sudo apt install python3-venv
然后,在您的 Flask 项目中创建一个父目录:
mkdir ~/myproject
然后在创建目录后进入该目录:
cd ~/myproject
通过运行以下命令创建一个虚拟环境,以存储您的 Flask 项目的 Python 要求:
python3.6 -m venv myprojectenv
这将在您的项目目录中的名为 myprojectenv
的目录中安装 Python 和 pip
的本地副本。
在虚拟环境中安装应用程序之前,您需要激活它:
source myprojectenv/bin/activate
您的提示符将更改以指示您现在正在虚拟环境中操作。它将显示如下内容:(myprojectenv) user@host:~/myproject$
。
步骤 3 — 设置 Flask 应用程序
现在您已经在虚拟环境中,可以安装 Flask 和 uWSGI,并开始设计您的应用程序。
首先,使用本地实例的 pip
安装 wheel
,以确保即使缺少 wheel 存档,您的软件包也能安装成功:
pip install wheel
接下来,安装 Flask 和 uWSGI:
pip install uwsgi flask
安装完成后,您可以开始使用 Flask。
创建一个示例应用
现在您已经可以使用 Flask,可以创建一个简单的应用程序。您可能还记得,Flask 是一个微框架,不包括许多更全面的框架可能包含的工具。Flask 主要作为一个模块存在,您可以将其导入到您的项目中,以帮助您初始化一个 Web 应用程序。
虽然您的应用程序可能更复杂,但您将在一个文件中创建您的 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
和 ENTER
来完成此操作。
如果您按照初始服务器设置指南进行了操作,应该已经启用了 UFW 防火墙。要测试应用程序,您需要允许访问端口 5000
:
sudo ufw allow 5000
现在测试您的 Flask 应用程序:
python myproject.py
您将收到以下输出,其中包括一个有用的警告,提醒您不要在生产环境中使用此服务器设置:
* Serving Flask app "myproject" (lazy loading)* Environment: productionWARNING: 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
您应该会看到类似以下内容:
!Flask sample app
完成后,按 CTRL + C
来停止 Flask 开发服务器。
创建 WSGI 入口点
接下来,您将创建一个文件,作为应用程序的入口点。这将告诉您的 uWSGI 服务器如何与应用程序交互。
首先创建并命名文件 wsgi.py
:
nano ~/myproject/wsgi.py
在这个文件中,从您的应用程序中导入 Flask 实例,然后运行它:
from myproject import appif __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
再次在您的网络浏览器中访问服务器的 IP 地址,并在末尾添加 :5000
:
http://your_server_ip:5000
您应该再次收到应用程序的输出:
!Flask 示例应用程序
确认它正常运行后,在终端窗口中按下 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
并将其放置在此目录中。
还要更改套接字的权限。这将使 Nginx 组拥有稍后的 uWSGI 进程的所有权,因此确保套接字的组所有者可以从中读取信息并向其中写入信息。此外,当进程停止时清理套接字,添加 vacuum
选项:
[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 进行通信。
完成后保存并关闭文件。
步骤 5 —— 创建 systemd 单元文件
接下来,创建 systemd 服务单元文件。创建 systemd 单元文件将允许 Ubuntu 的 init 系统在服务器启动时自动启动 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 进程通信。记得在这里用你自己的用户名替换用户名:
[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 服务并启用它,以便在启动时启动:
sudo systemctl start myproject
sudo systemctl enable myproject
检查状态:
sudo systemctl status myproject
你应该会收到如下输出:
● myproject.service - uWSGI instance to serve myprojectLoaded: loaded (/etc/systemd/system/myproject.service; enabled; vendor presetActive: active (running) since Mon 2021-10-25 22:34:52 UTC; 14s agoMain 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
。还告诉它使用此块处理服务器域名的请求:
server {listen 80;server_name your_domain www.your_domain;
}
接下来,添加一个匹配每个请求的位置块。在此块中,你将包含 uwsgi_params
文件,该文件指定需要设置的一些通用 uWSGI 参数。然后,你将使用 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
将文件放在该目录中后,可以通过运行以下命令检查语法错误:
sudo nginx -t
如果没有指示任何问题,重新启动 Nginx 进程以读取新配置:
sudo systemctl restart nginx
现在再次调整防火墙。你不再需要通过端口 5000
访问,因此可以删除该规则:
sudo ufw delete allow 5000
然后,允许访问 Nginx 服务器:
sudo ufw allow 'Nginx Full'
现在,你应该能够在 Web 浏览器中导航到你的服务器域名:
http://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 日志。
第七步 —— 保护应用程序
为了确保服务器的流量保持安全,您应该为您的域名获取一个 SSL 证书。有多种方法可以做到这一点,包括从 Let’s Encrypt 获取免费证书,生成自签名证书,或者从其他提供商购买证书并按照《在 Ubuntu 18.04 上为 Nginx 创建自签名 SSL 证书》的步骤 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 设置:
请选择是否将 HTTP 流量重定向到 HTTPS,删除 HTTP 访问。
-------------------------------------------------------------------------------
1: 不重定向 - 对 web 服务器配置不做进一步更改。
2: 重定向 - 使所有请求重定向到安全的 HTTPS 访问。对于新站点或者您确信您的站点可以在 HTTPS 上正常工作,请选择此项。您可以通过编辑您的 web 服务器配置来撤消此更改。
-------------------------------------------------------------------------------
选择适当的数字 [1-2] 然后 [enter] (按 'c' 取消):
选择您的选择然后按 ENTER
。配置将被更新,Nginx 将重新加载以应用新的设置。certbot
将以一条消息告诉您过程成功,并告诉您证书存储在何处:
重要提示:- 恭喜!您的证书和链已保存在:/etc/letsencrypt/live/your_domain/fullchain.pem您的密钥文件已保存在:/etc/letsencrypt/live/your_domain/privkey.pem您的证书将在 2022-01-24 到期。在将来获取新的或调整版本的证书时,只需再次运行 certbot 并使用 "certonly" 选项。要非交互地更新 *所有* 您的证书,请运行 "certbot renew"- 您的帐户凭据已保存在您的 Certbot 配置目录 /etc/letsencrypt。您现在应该对此文件夹进行安全备份。此配置目录还将包含由 Certbot 获取的证书和私钥,因此定期备份此文件夹是理想的。- 如果您喜欢 Certbot,请考虑通过以下方式支持我们的工作:向 ISRG / Let's Encrypt 捐赠: https://letsencrypt.org/donate向 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 应用程序提供服务。