使用 Docker 和 Nginx 部署 Python FastAPI 应用完整步骤
本文详细介绍使用 Docker 容器化 Python FastAPI 应用,并通过 Nginx 作为高性能反向代理网关的完整部署步骤,涵盖架构原理、项目结构、Dockerfile 编写、Nginx 配置、Docker Compose 编排及生产环境校验。
一、整体架构与工作流转原理
在生产级部署中,直接将 Uvicorn 监听的端口暴露到公网存在多项局限:Uvicorn 作为 ASGI 应用服务器,设计重心在于利用 Python 的 asyncio 事件循环高效处理 Python 协程与业务请求,但其并非为直接对抗恶劣的公网网络环境而设计。公网请求往往包含慢速客户端连接、恶意畸形包探测、突发流量洪峰以及静态资源的高频请求,这些若直接由 Uvicorn 处理,会过早耗尽工作进程的并发队列与文件描述符。
引入 Nginx 担任反向代理网关后,整个流量流转机制被清晰地划分为外网与内网两层:外部客户端与 Nginx 建立 TCP 连接,Nginx 在前端完成请求合法性过滤、连接缓冲与 SSL 加解密;通过内部 Docker 虚拟网络,Nginx 将清洗合规后的请求以轻量 HTTP 协议转发至后端的 FastAPI 容器。FastAPI 容器无需对外开放宿主机端口,仅在 Docker 内部桥接网络与 Nginx 通信,从而构筑了严格的网络边界防护。
二、项目工程目录与基础业务代码准备
为了让部署流程条理清晰并支持多环境平滑迁移,建议按照职责对配置文件与业务源代码进行目录分离。创建一个名为 fastapi-nginx-docker 的工作根目录,其标准布局如下:
fastapi-nginx-docker/
├── docker-compose.yml # Docker Compose 服务编排配置文件
├── nginx/
│ └── nginx.conf # Nginx 反向代理与 HTTP 转发规则
└── app/
├── Dockerfile # FastAPI 应用容器镜像构建脚本
├── requirements.txt # Python 依赖包清单
└── main.py # FastAPI 应用入口与核心路由
1. 编写 Python 依赖文件
在 app/requirements.txt 中指定生产所需的最小依赖包集合。为了保证容器运行时的稳定性与可复现性,建议固定主版本号:
fastapi==0.110.0
uvicorn[standard]==0.29.0
pydantic==2.6.4
2. 编写 FastAPI 业务代码
在 app/main.py 中编写基础示例应用。代码包含一个根路径探针接口和一个状态查询接口,并在接口中回显接收到的客户端 IP 信息,便于后续验证反向代理请求头传递是否正确:
import time
from fastapi import FastAPI, Request
app = FastAPI(
title="FastAPI Docker Gateway Demo",
version="1.0.0",
docs_url="/docs",
redoc_url="/redoc"
)
@app.get("/")
def read_root():
return {
"status": "online",
"service": "fastapi-docker-nginx",
"timestamp": int(time.time())
}
@app.get("/api/v1/system/status")
def system_status(request: Request):
# 提取经过 Nginx 转发的真实客户端 IP 与 Host 头
client_ip = request.headers.get("x-real-ip", request.client.host if request.client else "unknown")
host_header = request.headers.get("host", "unknown")
forwarded_proto = request.headers.get("x-forwarded-proto", "http")
return {
"app_status": "healthy",
"client_real_ip": client_ip,
"request_host": host_header,
"protocol": forwarded_proto,
"backend_engine": "uvicorn-asgi"
}
三、编写 Dockerfile 容器化 FastAPI 应用
在 app/Dockerfile 中定义构建镜像的步骤。在容器工程实践中,构建速度与运行安全是两大关键考量:利用 Docker 分层缓存机制可大幅减少镜像构建时间,而以非 root 用户身份启动进程则是避免容器逃逸的安全基线。
# 选用官方轻量级 Python 运行时基础镜像
FROM python:3.11-slim
# 设置 Python 环境变量:
# 1. PYTHONUNBUFFERED=1 保证日志直接输出至终端,不进行流缓冲,方便 docker logs 采集
# 2. PYTHONDONTWRITEBYTECODE=1 阻止生成 .pyc 文件,保持容器干净
ENV PYTHONUNBUFFERED=1 PYTHONDONTWRITEBYTECODE=1
# 设置容器内工作目录
WORKDIR /app
# 先独立复制依赖描述文件,安装依赖包
# 这样当仅修改业务代码时,该步骤会命中缓存,无需重复下载第三方包
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
# 复制业务源码到工作目录
COPY . /app
# 创建非 root 专用运行用户并切换,提升运行时安全性
RUN useradd -u 1000 -m appuser && chown -R appuser:appuser /app
USER appuser
# 声明容器内部监听端口(供文档与编排参考)
EXPOSE 8000
# 启动 Uvicorn ASGI 服务:必须监听 0.0.0.0,否则宿主机和 Nginx 无法访问
CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8000"]
在容器环境内运行服务时,如果启动参数设置为
--host 127.0.0.1,Uvicorn 只会监听容器自身的回环网络接口(loopback)。由于 Docker 为每个容器分配了独立的网络命名空间,来自 Nginx 容器的请求到达网卡时会被直接丢弃,最终导致 Nginx 报 502 Bad Gateway 错误。监听 0.0.0.0 才能保证应用接受来自容器内所有网络接口的转发流量。
四、配置 Nginx 反向代理与请求头转发
在 nginx/nginx.conf 中定义 Nginx 反向代理规则。Nginx 既作为前端请求路由中心,又充当网络协议转换器。在这里,Nginx 必须将客户端原始的域名、协议与真实 IP 通过专属 HTTP Header 传递给 FastAPI,避免代理后信息丢失。
worker_processes auto;
events {
worker_connections 1024;
}
http {
include mime.types;
default_type application/octet-stream;
# 日志格式定义,打印上游响应耗时与真实客户端 IP
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for" '
'rt=$request_time uct="$upstream_connect_time" uht="$upstream_header_time" urt="$upstream_response_time"';
access_log /var/log/nginx/access.log main;
error_log /var/log/nginx/error.log warn;
sendfile on;
keepalive_timeout 65;
# 客户端上传包体限制(按需调整)
client_max_body_size 20M;
# 定义后端应用上游服务器池:通过 Docker 内部服务名 "web" 与端口 8000 寻址
upstream fastapi_backend {
server web:8000;
keepalive 32;
}
server {
listen 80;
server_name localhost;
# 针对静态资源或根路径的全局代理
location / {
proxy_pass http://fastapi_backend;
# 协议与连接优化
proxy_http_version 1.1;
proxy_set_header Connection "";
# 核心请求头传递:将客户端真实元数据传递给 FastAPI
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
# 代理超时控制(秒)
proxy_connect_timeout 60s;
proxy_send_timeout 60s;
proxy_read_timeout 60s;
# 开启缓冲,避免慢客户端直接阻塞后端 Uvicorn
proxy_buffering on;
proxy_buffer_size 8k;
proxy_buffers 8 8k;
}
}
}
上述配置的关键指令解析:
upstream fastapi_backend { server web:8000; }:利用 Docker 内部集成的 DNS 解析器,将名为web的容器服务名解析为其对应的容器虚拟 IP。proxy_set_header X-Real-IP $remote_addr;:将与 Nginx 建立 TCP 连接的客户端实际 IP 填入X-Real-IP头中,以便 FastAPI 获取访问者的真实物理地址。proxy_set_header X-Forwarded-Proto $scheme;:让后端明确请求是基于 HTTP 还是 HTTPS 协议到达前端的。这在配置 SSL 证书后尤其重要,能避免 FastAPI 自动生成的 OpenAPI/Swagger 文档资源加载出现混合内容警告。
五、使用 Docker Compose 编排多容器服务
在项目根目录下编写 docker-compose.yml 文件,将 web(FastAPI)和 proxy(Nginx)两个容器组合在同一个专用的自定义网络中,实现容器生命周期的统一管理与按序启动。
services:
web:
build:
context: ./app
dockerfile: Dockerfile
container_name: fastapi_app
restart: unless-stopped
expose:
- "8000"
networks:
- fastapi_network
environment:
- APP_ENV=production
proxy:
image: nginx:1.25-alpine
container_name: nginx_proxy
restart: unless-stopped
ports:
- "80:80"
volumes:
- ./nginx/nginx.conf:/etc/nginx/nginx.conf:ro
depends_on:
- web
networks:
- fastapi_network
networks:
fastapi_network:
driver: bridge
编排规则的关键设计考量:
- 内部端口开放(
expose: - "8000") vs 物理映射(ports):在web服务中,仅声明了expose: - "8000",并没有声明ports映射。这意味着只有同在fastapi_network网络的容器(即 Nginx)可以通过http://web:8000访问它,宿主机外部无法通过8000端口直连应用,有效消除了未经反向代理过滤的潜在入侵通道。 - Nginx 卷挂载(
volumes):将宿主机的./nginx/nginx.conf映射到容器内的/etc/nginx/nginx.conf并设置为只读(:ro),便于在宿主机调整代理规则而无需频繁重新构建镜像。 - 自动重启策略(
restart: unless-stopped):确保宿主机重启或容器因偶发未捕获异常退出时,Docker 守护进程能自动拉起服务。
六、服务启动与连通性验证
在项目根目录(即 fastapi-nginx-docker/)下打开终端,依次执行以下步骤启动并验证服务运行状态。
1. 构建镜像并后台启动容器
docker compose up -d --build
2. 检查容器运行状态
docker compose ps
终端中输出的状态列表应如下所示(状态列需为 Up):
NAME IMAGE COMMAND SERVICE CREATED STATUS PORTS
fastapi_app fastapi-app "uvicorn main:app --…" web 10 seconds ago Up 9 seconds 8000/tcp
nginx_proxy nginx:1.25-alpine "/docker-entrypoint.…" proxy 10 seconds ago Up 9 seconds 0.0.0.0:80->80/tcp
3. 使用 curl 发送 HTTP 请求测试
在终端向本机 80 端口(Nginx 监听端口)发送请求,测试反向代理连通性:
curl -i http://localhost/
HTTP/1.1 200 OK
Server: nginx/1.25.4
Date: Thu, 30 Oct 2026 09:15:20 GMT
Content-Type: application/json
Content-Length: 68
Connection: keep-alive
{"status":"online","service":"fastapi-docker-nginx","timestamp":1793351720}
进一步请求状态接口,验证真实客户端 IP 与请求头转发功能:
curl -s http://localhost/api/v1/system/status
{"app_status":"healthy","client_real_ip":"172.18.0.1","request_host":"localhost","protocol":"http","backend_engine":"uvicorn-asgi"}
4. 查看实时运行日志
当接口请求出现异常时,可通过以下命令实时追踪 Nginx 访问日志与 FastAPI 应用内部报错:
# 查看 Nginx 访问与转发日志
docker compose logs -f proxy
# 查看 FastAPI 应用及 Uvicorn 运行日志
docker compose logs -f web
七、生产环境高频问题排查与运维清单
| 故障现象 | 根本原因排查 | 标准处置方案 |
|---|---|---|
| 502 Bad Gateway | Uvicorn 启动参数误配为 --host 127.0.0.1,导致容器拒绝外来访问;或 nginx.conf 中 upstream 声明的服务名拼写错误。 |
检查 Dockerfile 的 CMD 指令,确保为 --host 0.0.0.0;检查 nginx.conf 中的 server web:8000; 是否与 compose 中的服务名 web 严格吻合。 |
| Swagger / Docs 加载失败 | 在启用 HTTPS 时,Nginx 未传递 X-Forwarded-Proto $scheme,FastAPI 认为处于 HTTP 环境,文档资产加载产生混合内容(Mixed Content)被浏览器阻断。 |
在 Nginx location / 中补充 proxy_set_header X-Forwarded-Proto $scheme;,并在 FastAPI 实例化时按需指定 root_path。 |
| 413 Request Entity Too Large | 客户端上传文件或大 JSON 请求体超过 Nginx 默认的 1MB 阈值。 | 在 nginx.conf 的 http 或 server 上下文中增加 client_max_body_size 20M;(按实际业务上限调整)。 |
| 并发吞吐量不足 | 容器内单个 Uvicorn 进程受 Python GIL 限制,仅能利用单核 CPU 处理业务代码。 | 在生产环境修改启动命令,增加工作进程数(例如 CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8000", "--workers", "4"]),或通过 Gunicorn 作为进程管理器驱动 Uvicorn Worker。 |
免责声明:本文内容仅供参考,不代表本站观点。转载或引用请注明出处。









