Nginx+Gunicorn+Flask部署
Nginx + Gunicorn + Flask 部署
概述
这篇博客源自于两件事情 。首先,当我运行 Flask 后端项目时,控制台总会反复弹出这样一条提示:
1 | WARNING: This is a development server. Do not use it in a production deployment. Use a production WSGI server instead. |
刚开始接触 Flask 时,对服务、网络、以及背后的机制都还不太熟悉,因此只是把它当作一个黑盒工具来使用,也就暂时搁置了这个问题。另一个动机是,最近需要将数据库中的一些声子属性进行可视化,把生成的图像保存在静态目录下,供前端通过接口访问。比如我们希望将 dalu.png
这张图片放在某个目录下,然后通过浏览器直接访问:
1 | http://127.0.0.1:9527/dalu.png |
回顾整个过程时发现其中涉及到的一些细节发现并不能完全讲清楚,比如图片是怎么变成 URL 的?其实并不是把图片变成 URL,而是需要启动一个 Web 服务器:它监听 HTTP 请求,并返回对应的资源或结果。这个过程和之前遇到的 Flask 部署问题本质上是紧密相关的。结合目前最成熟的 Flask 部署方案:Nginx + Gunicorn + Flask,把整个过程的图像通过这个博客厘清楚。
Web 服务器
Web 服务器本质上是一个处理 HTTP 请求的程序。它监听指定的网络端口,接收来自客户端的请求,并返回对应的内容,比如网页、图片、JSON 数据或文件等。以浏览器访问 http://127.0.0.1:8080/index.html
为例,实际上背后浏览器向 Web 服务器发送了如下请求:
1 | GET /index.html |
Web 服务器接收到请求后,会在本地查找 index.html
文件,读取其内容,并将其作为响应返回给浏览器。上网经常能看到的 Nginx,其实本质上就是一个 Web 服务器。Nginx 以高性能、低资源占用而著称,尤其擅长处理静态资源(如 HTML、图片、CSS、JS 文件)以及作为反向代理服务器转发请求。
那么,Flask 是 Web 服务器吗?其实并不是。Flask 是一个轻量级的 Web 应用框架,本质上只是一个用 Python 编写的程序,用来处理路由逻辑和业务功能。它并不具备独立处理 HTTP 请求的能力,因此需要借助 Web 服务器才能真正对外提供服务。Flask 内部默认集成了一个开发用的 HTTP 服务器 —— Werkzeug。它的主要作用是监听 HTTP 请求并将其分发到对应的路由函数上。这套机制在开发和调试过程中非常方便,但 Werkzeug 并不适合在生产环境中使用,因为它缺乏稳定性、并发处理能力差、安全性较低,容易崩溃。
在生产环境中需要引入更加专业的 Web 服务器,比如 Gunicorn(Green Unicorn)是一个专门为 Python 应用设计的高性能 Web 服务器,适合在生产环境中托管 Flask、Django 等应用。Gunicorn 启动后会生成多个 worker,每个 worker 实运行着独立的 Flask 实例,因此能够支持多并发访问,大大提升了吞吐能力和稳定性。除了 Gunicorn,Python 生态中还有其他生产级 Web 服务器,比如 uWSGI,具备更强的自定义性与扩展性。
那么像 Flask 这些 Web 应用,该如何与这些更高性能的 Python Web 服务器协同工作呢?这就要提到 Python 的一项关键标准 —— WSGI(Web Server Gateway Interface)。WSGI 是 Python 应用与 Web 服务器之间的接口协议,只要双方都遵守这个协议,就能像插头和插座一样无缝连接。Flask 应用本身就是一个符合 WSGI 协议的对象,而像 Gunicorn、uWSGI 等服务器则负责加载并运行这个对象。通过 WSGI,Flask 应用可以被更强大的 Web 服务器接管,实现真正意义上的部署上线。
Flask + Gunicorn
基本流程
Gunicorn 没法在 Windows 上运行,我们在 Docker 中运行 Flask + Gunicorn。用最简示例演示这个过程的基本流程,文件结构为:
1 | myapp/ |
在 app.py
中启动一个 Flask 应用:
1 | from flask import Flask |
在 requirements.txt
中指定我们这个应用所依赖的库:
1 | flask |
编写一个 Dockerfile:
1 | # 使用官方 Python 基础镜像 |
构建镜像:
1 | docker build -t flask-gunicorn-app . |
运行容器:
1 | docker run -p 8000:8000 flask-gunicorn-app |
这里 docker 的 -p local_port:container_port
将容器的 8000 端口映射到宿主机的 8000 端口。之后,我们在宿主机的浏览器中访问 127.0.0.1:8000
就可以收到 Flask 的 hello
路由函数返回的内容了。
说明
在使用 Gunicorn
时,Flask 中就不再需要使用 app.run()
了。Gunicorn 会直接接管 Flask 应用,调用它的 WSGI 接口。app.run()
是 Flask 内置的开发服务器启动方式,它背后使用的是 Werkzeug。Gunicorn 运行的参数的含义是:
部分 | 含义 |
---|---|
gunicorn |
启动 Gunicorn 主程序(Python WSGI 服务器) |
-w 4 |
启动 4 个 worker 进程,每个处理一个请求(并发能力) |
-b 0.0.0.0:8000 |
绑定到所有网卡的 8000 端口,监听来自外部的请求 |
app:app |
指定 Flask 应用的入口,格式是 模块名:变量名 ,例如:app.py 文件中定义了 app = Flask(__name__) |
每个 worker 是一个独立的 Python 进程,监听来自主进程的请求:
1 | Client |
- 客户端发送 HTTP 请求到 Gunicorn 监听的端口(8000);
- Gunicorn 的主进程接收连接后,将请求分发给空闲的 worker;
- worker 进程调用 Flask 应用(WSGI callable)处理请求;
- Flask 运行路由逻辑,返回响应内容;
- worker 把响应返回给客户端。
使用 Gunicorn 的优点在于可以通过多个 worker 实现并发处理,主进程会监督子进程的运行状态,异常时会自动重启。
Nginx + Gunicorn + Flask
为什么要引入 Nginx
Gunicorn + Flask 的组合工作流程可以处理并发请求,但想象这样一个场景:我们的数据可视化应用越来越受欢迎,现在有 100 个用户同时在线,每个用户查看的报告页面包含 10 张散点图。这意味着在短时间内,我们的服务器需要处理 1000 个图片请求。在纯 Gunicorn + Flask 架构下,会发生什么?
1 |
|
每当用户请求一张图片时,以下事情会发生:
- 一个 Gunicorn worker 被分配处理这个请求
- Python 进程读取磁盘上的图片文件(可能 500KB)
- 通过网络将文件内容传输给用户
- 整个过程中,这个 worker 无法处理其他请求
这就产生了一个明显的问题:宝贵的 Python 进程被简单的文件传输任务占用了。假设我们配置了 4 个 Gunicorn workers,在高峰期 4 个 workers 都在忙着传输图片文件,新的业务请求(比如用户想生成新的报告)只能排队等待。这导致用户体验急剧下降,比如页面加载变慢,甚至出现超时。同时,客户端的网络连接可能很慢或不稳定。如果让 Gunicorn 直接处理这些连接,一个慢速客户端可能会占用一个 precious 的 worker 进程很长时间。这就像一家餐厅里,技艺精湛的厨师不仅要负责烹饪复杂的菜品,还要亲自把每道菜端到客人桌上。结果就是厨师疲于奔命,客人等餐时间越来越长。
这时候,我们可以在 Gunicorn 之前再引入一层 Nginx 来解决这个问题。Nginx 就像餐厅里专业的服务员,专门负责"端菜"(静态文件服务),让厨师(Python 应用)专心"做菜"(业务逻辑处理)。Nginx 采用异步、非阻塞的事件驱动模型,单个 Nginx 进程可以同时处理数万个并发连接,内存占用极低,CPU 效率极高。它可以快速接收请求,缓冲数据,然后通过快速的内部网络转发给 Gunicorn,Flask worker 可以快速处理完请求并释放。Nginx 还是静态文件处理专家,直接在内核空间传输文件。同时具有请求限流、负载均衡、智能缓存等多种生产环境必备功能。在引入 Nginx 之后,我们的后端结构也就成最初的:
1 | 用户请求 → Gunicorn → Flask → 返回响应 |
变成了:
1 | 用户请求 → Nginx → 判断请求类型 |
此时的工作流程变成了,浏览器发送 HTTP 请求到 Nginx 服务器,Nginx 接收到请求后,对 URL 进行分析。如果访问的是静态资源,就会直接读取静态资源,然后返回给浏览器。如果不是,就转交给 Gunicorn 服务器。Gunicorn 服务器根据 WSGI 协议,找到对应的 Flask 应用。Flask 进行逻辑处理后,将返回值发送到 Gunicorn 服务器,Gunicorn 服务器再将结果返回给 Nginx,最后 Nginx 将结果再返回给用户。回到我们前面的场景(100 用户,1000 图片请求),使用 Nginx 后,Nginx 直接处理所有 1000 个图片请求,响应时间从秒级降到毫秒级。4 个 Gunicorn workers 完全空闲,随时准备处理新的业务请求。用户看到页面几乎瞬间加载完成,服务器资源利用率大幅提升。
配置示例
概述
我们整理了如下的项目目录结构:
1 | myapp/ |
整个部署流程如下:
- 前端由 Nginx 提供 Web 服务,监听 HTTP 请求;
- 请求将被转发给运行在 Gunicorn 上的 Flask 应用;
- Flask 负责处理业务逻辑,包括权限判断等;
- 当需要返回静态文件(如
/download/dalu.txt
)时,Flask 不会直接读取并返回文件,而是通过设置X-Accel-Redirect
响应头,将文件交由 Nginx 直接处理和返回,实现高效文件传输; - 应用服务通过
Dockerfile
构建为一个包含 Flask 和 Gunicorn 的容器; - 为了同时运行 Flask 服务和 Nginx,并实现容器之间的通信,我们使用
docker-compose.yml
来定义和管理这两个容器。
接下来将逐一介绍每个文件的作用与配置内容。
Flask 应用
1 | from flask import Flask, Response |
在宿主机中,文件被存放在项目根目录下的 files
文件夹中。而由于我们在 Dockerfile 中指定了工作目录为 WORKDIR /app
,这个目录在容器中的路径就变成了 /app/files
,即容器内访问文件时应使用这一路径。
为实现高效、安全的文件下载,同时和 Nginx 联用,我们使用了 X-Accel-Redirect
—— 这是 Nginx 提供的一个 非标准 HTTP 头部字段。按照 HTTP 协议的传统,前缀 X-
常用于表示该字段是厂商自定义的扩展,X-Accel-Redirect
也正是如此,它是 Nginx 独有的“后端引导式文件传输机制”。
具体来说,当 Flask 接收到文件下载请求后,并不会直接读取文件返回,而是通过响应头 X-Accel-Redirect
告诉 Nginx 去读取某个内部路径(如 /internal/filename.txt
)。Nginx 接收到这个响应后,会在配置文件中查找与 /internal/
匹配的 location
区块,并在服务器本地查找实际文件,最终将其高效地返回给客户端。(这部分会在后续的 Nginx 中更详细介绍)
通过这种方式,Flask 只负责业务逻辑和权限控制,不再承担读取和传输大文件的任务。相比之下,如果不使用 X-Accel-Redirect
,Flask 需要手动读取整个文件内容,既消耗内存,又会增加网络负载;同时,用户将直接访问实际路径,也会导致权限难以隔离和控制。
最简 Nginx 配置
1 | events { |
总的来说,这个 nginx.conf
文件配置了一个 Nginx Web 服务器,它定义了两条请求处理规则。一个是先将所有请求转发到 Flask,另一个是将对于 /internal/
部分的文件静态返回给客户端。接下来来详细介绍一下各字段的含义。
1 | worker_connections 1024; |
在 Web 服务器的世界里,一个连接可能会持续一段时间。比如用户打开网页后可能会停留几分钟浏览,在这期间连接一直保持着。所以 1024 指的是一个 Nginx 工作进程能够同时维持的最大 TCP 连接数量。如果服务器有 4 个 CPU 核心,Nginx 通常会启动 4 个工作进程,那么理论上最大并发连接数就是 4 × 1024 = 4096。
1 | http { |
http
块是用来专门用来定义 Web 服务器功能的。可以把它想象成一个总控制室,里面包含了所有与 HTTP 协议相关的设置。
其中 upstream
定义的是后端服务器组。想象我们开了一家披萨店,前台接订单,但后厨有多个厨师在做披萨。upstream
就是定义"后厨团队"的地方。如果我们有多个 Flask 实例,Nginx 会自动把请求分发给不同的后端服务器:
1 | upstream flask_app { |
当然,在我们的例子中,我们并没有用 Nginx 去做负载均衡,Nginx 直接将请求转发到 Gunicorn 管理节点上,由 Gunicorn 再去进行分发,这是个两层的负载分发过程。
在这里,我们之所以可以直接写 flask:8000
,是因为我们在后面的 docker-compose.yml
中定义了一个叫作 flask
的服务(这部分在后面 Docker Compose 的部分会更详细介绍),Docker Compose 会自动为每个服务创建一个可解析的 DNS 名称(即容器名 = 服务名),这些服务可以在 同一个自定义网络中相互解析通信。我们在 docker-compose.yml
中定义了:
1 | services: |
这时,Docker Compose 会创建一个名为 flask
的服务,所有服务默认加入一个同名网络(如 myapp_default
)。在这个网络中,Nginx 容器可以通过 flask:8000
来连接 Flask 容器的 8000 端口。不需要指定 IP 地址,也不需要手动配置 /etc/hos
1 | ┌────────────┐ proxy_pass ┌──────────────┐ |
http
块中的 server
块是用来定义 Web 服务器的,一个 server
块就代表一个虚拟 Web 服务器!我们可以定义多个 server 块,就像在同一台物理服务器上运行多个不同的网站:
1 | http { |
每个 server
块根据域名(server_name)和端口(listen)来决定处理哪些请求。
server
块中的 location
块是用于定义 URL 匹配规则和处理方式的,可以把它想象成快递分拣中心的规则。在介绍 URL 匹配规则之前,我们有必要回顾一下 URL 的结构。一个典型 URL 如下图所示,它可以被拆分为几个部分:
部分 | 名称 | 作用 / 举例 |
---|---|---|
http:// |
Scheme | 协议,如 http 、https 、ftp 等 |
www.example.com |
Domain Name | 域名,代表服务器主机地址 |
:80 |
Port | 可选,服务器监听的端口,默认为 80 |
/path/to/myfile.html |
Path to the file | 表示请求的资源路径 |
?key1=value1&key2=value2 |
Query Parameters | 参数,发送给服务器做查询或处理 |
#SomewhereInTheDocument |
Anchor | 页面锚点,仅在客户端定位,不参与请求 |
Nginx 的 location
指令主要用来匹配客户端请求的资源路径部分,即 /path/to/myfile.html
的部分。常见的 location
匹配规则包括:
1 | # 精确匹配 |
在匹配了资源路径后,location
内部有着对向不同资源路径请求的不同处理方式:
1 | location / { |
proxy_pass
的处理方式是请求转发,类似于呼叫转移。当用户请求比如 http://yoursite.com/api/user
时,Nginx 会:
- 接收到请求
- 查看
proxy_pass
的目标地址http://flask_app
- 将请求完整地转发到 Flask 应用(运行在
http://flask_app
) - 等待 Flask 应用的响应
- 将 Flask 的响应
在这个过程中,Nginx 扮演的是代理中介的角色,客户端以为自己在直接与 Nginx 通信,而实际的业务逻辑处理发生在后端的 Flask 应用中,这实际上就是反向代理。在进行请求转发时,我们往往还会额外设置两个 HTTP 头部:
1 | location / { |
其中 Host
头部是 HTTP 标准协议中的字段,用来告诉服务器客户端想访问的是哪个主机名,比如当客户端访问 http://example.com/index.html
时,浏览器发出的请求会包含:
1 | GET /index.html HTTP/1.1 |
在 Nginx 请求转发中,加上 proxy_set_header Host $host;
,表示把客户端原始请求的主机名传递给后端 Flask 应用,这对于 Flask 中需要根据主机名做逻辑判断的情况是必要的。
X-Real-IP
并不是 HTTP 标准协议的一部分,但是已经被很多代理服务器广泛采用。它的目的是在反向代理存在时,保留客户端的真实 IP 地址。因为在反代场景中,后端应用接收到的 IP 会是 Nginx 的地址,而非用户的原始地址。比如设置好后,Nginx 转发到 Flask 的 HTTP 的头部就包括:
1 | X-Real-IP: 203.0.113.42 |
在 Flask 中可以通过 request.headers['X-Real-IP']
来获取它。proxy_pass
主要用于当请求需要动态处理、业务逻辑处理或者数据库访问时。
1 | location ~* \.(jpg|jpeg|png|gif)$ { |
除了请求转发,Nginx 也可以直接返回静态资源。root
指令是最直接的方法,就是 Nginx 的直接文件服务。当用户请求 http://yoursite.com/photos/sunset.jpg
时,Nginx 会:
- 看到资源路径是
/photos/sunset.jpg
- 将
root
路径/static/images/
与资源路径拼接 - 最终文件路径变成:
/static/images/photos/sunset.jpg
- 直接从文件系统读取这个文件路径并返回给用户
这里需要注意的关键点是完整的资源路径会被保留并拼接到 root
路径后面,所以文件系统结构必须与 URL 结构完全匹配。使用 root
的场景是,当我们有一个标准的静态文件目录结构,并且 URL 路径与文件系统路径结构完全匹配。
1 | location /internal/ { |
alias
是路径替换,相当于是更灵活的 root
,它的意思是把 URL 这部分路径替换成另一个路径。当用户发起内部重定向到 /internal/report.pdf
时,Nginx 会:
- 识别 URL 路径是
/internal/report.pdf
- 移除匹配到的
location
部分/internal/
- 剩余部分是
report.pdf
- 将
alias
路径/app/files
与剩余部分拼接 - 最终文件路径变成
/app/files/report.pdf
- 直接从文件系统读取这个文件路径返回给用户
使用 alias
的场景是,当我们需要将特定的 URL 路径映射到不同的文件系统位置时。
Docker Compose
在我们这个示例里,我们需要同时用到 Gunicorn+Flask 和 Nginx 两个应用匹配起来完成一个完整的功能。这时,我们不需要构建一个镜像同时包含这两个应用,而是可以通过 Docker Compose 来把多个容器组合起来,让他们协同工作。我们可以把 Docker Compose 想象成乐队指挥,每个 Docker 容器就像一个乐器手(专门负责一种乐器),Docker Compose 就是指挥家,告诉每个乐器手什么时候开始、如何配合,最终演奏出一首完整的交响乐(完整的应用系统)。要使用 Docker Compose,我们只需要编写一个 docker-compose.yml
配置文件:
1 | version: '3.8' |
1 | version: '3.8' |
这一行告诉 Docker Compose 我们使用的是哪个版本的配置文件格式。通过指定版本,Docker 知道如何正确解释配置文件。版本 3.8 是一个相对较新且稳定的版本,支持我们需要的所有功能。
1 | services: |
这是一个顶级关键字,告诉 Docker Compose:"接下来我要定义这个应用包含哪些服务了"。把它想象成餐厅的菜单开头:"今日菜品如下:" 在 Docker Compose 的世界里,一个"服务"就是一个容器的定义。我们的应用由两个服务组成:flask 和 nginx。首先定义 flask 服务:
1 | flask: # 服务名称(自定义的) |
1 | flask: |
这是我们给这个服务起的名字,可以叫它任何名字,比如 backend
、api
、web-server
都可以。这个名字很重要,因为:
- 其他服务可以通过这个名字来访问它(就像网络中的主机名)
- 在容器内部,flask 服务可以通过
flask
这个域名被访问
1 | build: . |
这里的 .
表示"当前目录"。这行告诉 Docker Compose:"在当前目录寻找 Dockerfile,然后根据 Dockerfile 构建一个镜像来创建这个容器"。这和直接运行 docker build .
是一样的效果,但是 Docker Compose 会自动帮助管理镜像的命名和构建过程。
1 | volumes: |
这是文件系统挂载配置。./files
是宿主机上的路径,表示当前目录下的 files 文件夹。/app/files
是容器内部的路径,中间的 :
表示"挂载关系"。这样设置后,当容器内的程序在 /app/files
目录读写文件时,实际上就是在宿主机上的 ./files
目录读写文件。
对于 Nginx 服务定义:
1 | nginx: # 服务名称 |
1 | image: nginx:alpine |
与 flask 服务不同,nginx 服务不需要我们自己构建镜像,而是直接使用 Docker Hub 上现成的镜像。
nginx
是镜像名称alpine
是标签(tag),表示基于 Alpine Linux 的轻量级版本
这就像去超市买东西,flask 是"自制菜品"(需要按照 Dockerfile 这个菜谱来做),而 nginx 是"现成的罐头"(直接拿来用)。
1 | ports: |
这是端口映射配置,格式是 "宿主机端口:容器端口"
。容器内的 nginx 监听 8000 端口(容器内部的"信箱号码"),我们把宿主机的 80 端口映射到容器的 8000 端口,当有人访问你电脑的 8000 端口时,请求会被转发到容器内的 8000 端口。所以当你在浏览器访问 http://localhost
时,实际访问路径是:
1 | 浏览器 → 宿主机 8000 端口 → 容器内 8000 端口 → nginx服务 |
接下来配置挂载:
1 | volumes: |
nginx 服务有两个挂载点:
第一个挂载:配置文件
./nginx.conf
是我们在宿主机上写的 nginx 配置文件/etc/nginx/nginx.conf
是 nginx 容器内默认读取配置的位置- 这样我们就用自己的配置替换了默认配置
第二个挂载:文件目录
- 和 flask 服务一样,也挂载了
./files
目录 - 这样 nginx 就能直接访问到文件,实现 X-Accel-Redirect 功能
1 | depends_on: |
这告诉 Docker Compose:"nginx 服务依赖于 flask 服务,所以启动 nginx 之前,请先确保 flask 已经启动了"。就像做菜的顺序,你得先把米饭煮好,才能开始炒菜,最后摆盘。Docker Compose 会按照依赖关系安排启动顺序。
当这两个服务启动后,Docker Compose 会自动创建一个虚拟网络,让它们可以互相通信:
1 | ┌─────────────────┐ ┌─────────────────┐ |
这就是为什么在 nginx.conf 中我们可以写 server flask:8000
,因为 flask
已经成为了一个有效的主机名。我们在项目目录中运行 docker compose up
时就可以运行这个 Docker Compose,会发生这样的过程:
- 创建网络:Docker Compose 创建一个名为
项目名_default
的虚拟网络 - 构建镜像:根据 Dockerfile 为 flask 服务构建镜像
- 下载镜像:从 Docker Hub 下载 nginx:alpine 镜像(如果本地没有的话)
- 启动 flask:因为 nginx 依赖它,所以先启动
- 启动 nginx:flask 启动完成后,启动 nginx
- 建立连接:两个容器都连接到同一个虚拟网络,可以互相通信
服务起来了以后,大概长成下面这个样子。Docker Compose 默认会将容器命名为 <项目名>-<服务名>-<序号>
。app
是项目名,如果没有设置 COMPOSE_PROJECT_NAME
,那么通常就是当前目录名。flask-1
是 flask
服务的第一个容器,nginx-1
是 nginx
服务的第一个容器。我们如果终止了这个服务,再次使用 docker compose up
启动时,Docker 会检查是否已有同名容器存在(服务名 + 项目名),如果有,直接复用这个容器重启它。如果容器不存在或已删除,才会重新创建。另外,对于容器镜像的构建过程(Flask+Gunicorn 的部分),再再次运行的时候,Docker 会首先会检查 Dockerfile
、上下文目录(.
)、docker-compose.yml
是否有改动。如果一切未变,现有镜像可用,就会跳过重建,直接启动容器。
容器起来了之后,我们在宿主机中访问 127.0.0.1:8000/download/dalu.txt
,就可以正确返回文件了:
小结
Web 服务器是运行在物理服务器上的软件程序,其核心功能是接收来自客户端(浏览器)的 HTTP 请求并返回相应的资源。从功能角度看,Web 服务器主要承担两大职责:静态内容服务(直接从文件系统读取并返回 HTML、CSS、JavaScript、图片等文件)和动态内容代理(将需要程序处理的请求转发给应用服务器,如 PHP、Python、Java 应用)。现代 Web 服务器如 Nginx、Apache 还提供负载均衡、SSL 终止、请求缓存、访问控制、日志记录等高级功能。在架构层面,Web 服务器通常位于整个应用栈的最前端,作为客户端请求的第一接触点,负责请求的初步处理、路由分发和响应优化,它就像一个智能的"门卫"和"调度员",根据请求的性质决定是直接提供服务还是转发给后端的专业应用程序处理,从而实现高效的资源利用和optimal的用户体验。
在现代 Web 服务器架构中,Nginx + Gunicorn + Flask 组合形成了一个高效的三层处理体系:Nginx 作为反向代理服务器,负责静态文件服务、连接管理和请求路由分发,其核心配置通过 proxy_pass
(请求转发)、root
/alias
(静态文件服务)等指令实现不同类型请求的智能处理;Gunicorn 作为 WSGI 服务器,管理多个 Python worker 进程并处理并发请求分发;Flask 专注于业务逻辑处理和动态内容生成。这种分层架构体现了"专业分工"的设计理念,Nginx 利用其 C 语言实现的高性能优势处理网络 I/O 和静态资源,而 Python 应用专注于复杂的业务逻辑处理。通过 Docker Compose 进行容器编排,可以实现服务间的网络隔离、依赖管理和一键部署,同时 X-Accel-Redirect 等机制进一步优化了文件下载的安全性和性能,整个技术栈为构建可扩展、高性能的 Web 应用提供了完整的解决方案。
参考
- Linux 集群之美,余洪春,2020-12,机械工业出版社.