FastAPI + Nginx 部署 Python API 到云服务器的完整实践

举报
shijiu_dev 发表于 2026/10/27 12:02:22 2026/10/27
【摘要】 本文记录了如何把一个 FastAPI 接口稳稳部署到云服务器:整体采用 FastAPI + Gunicorn 多进程 + Nginx 反向代理 + systemd 托管的四层结构,逐层说明环境准备、最小可用应用编写、Nginx 反向代理与 HTTPS 配置、systemd 服务托管与开机自启,并给出四步排查流程和三个常见踩坑点。

背景

业务逻辑写完之后,真正让人头疼的往往不是代码本身,而是“怎么把它稳稳地放到云服务器上”。
我之前用 Flask 写过一个日志查询接口,直接用 gunicorn 挂在 8000 端口跑,局域网里访问正常,
但放到公网云主机上就接连踩坑:接口被直接裸暴露在公网、没有 TLS、进程崩了没人知道、
日志还把磁盘写满。最后花了两天时间重构,方案收敛成「FastAPI 应用 + Gunicorn 多进程 +
Nginx 反向代理 + systemd 托管」这一套。本文把整个过程和踩过的坑完整记录下来。

一、整体架构

最终的结构是这样的:

客户端 --HTTPS--> Nginx(443) --HTTP--> Gunicorn(127.0.0.1:8000) --> FastAPI 应用

关键点在于 Gunicorn 只监听 127.0.0.1,外部完全无法直接访问,所有流量必须经过 Nginx。
这样 TLS 证书、访问日志、限流、静态文件这些能力都交给 Nginx 处理,Python 进程只负责业务逻辑,
职责分离之后排查问题会清晰很多。

二、准备环境

云服务器选一台 2 核 4G 的 Linux 机器就完全够用了。以 Ubuntu 22.04 为例,先更新系统并装好依赖:

sudo apt update && sudo apt upgrade -y
sudo apt install -y python3 python3-venv python3-pip nginx

项目代码不要放在 /root 或者 /tmp 这类目录,放到 /opt 下面,并用一个独立的系统用户运行,
避免用 root 跑业务进程:

sudo useradd -r -m -d /opt/apiapp -s /bin/bash apiapp
sudo -u apiapp python3 -m venv /opt/apiapp/venv
sudo -u apiapp /opt/apiapp/venv/bin/pip install fastapi uvicorn gunicorn pydantic

用 venv 而不是系统 pip 是为了隔离依赖。后面每次更新代码只要切到虚拟环境装包就行,
不会污染系统 Python,也不会和机器上其他项目打架。

三、编写最小可用的 FastAPI 应用

目录结构保持简单,一个主文件加一个配置就够起步了:

/opt/apiapp/
├── app
└── venv/

主程序的内容不复杂,重点是加上健康检查和请求 ID 中间件,方便后面在 Nginx 和日志里对账:

from fastapi import FastAPI, Query
import uuid, time

app = FastAPI(title='log-query', version='1.0.0')

@app.middleware('http')
async def add_request_id(request, call_next):
    rid = str(uuid.uuid4())[:8]
    start = time.time()
    response = await call_next(request)
    response.headers['X-Request-Id'] = rid
    response.headers['X-Process-Time'] = f'{time.time() - start:.3f}'
    return response

@app.get('/healthz')
async def healthz():
    return {'status': 'ok'}

@app.get('/api/v1/logs')
async def query_logs(keyword: str = Query(..., min_length=1, max_length=64)):
    # 实际业务里替换成查询逻辑
    return {'keyword': keyword, 'items': []}

最后一定要有 /healthz 这类探活接口。后面配 Nginx、systemd 都会依赖它来判断服务是不是真的活着,
而不是仅仅端口在监听。

四、Nginx 反向代理配置

把默认站点改成我们的应用配置,假设对外域名是 api-example-com:

server {
    listen 80;
    server_name api-example-com;
    return 301 https://$host$request_uri;
}

server {
    listen 443 ssl http2;
    server_name api-example-com;

    ssl_certificate     /etc/nginx/ssl/fullchain.pem;
    ssl_certificate_key /etc/nginx/ssl/privkey.pem;

    client_max_body_size 20m;

    location / {
        proxy_pass http://127.0.0.1:8000;
        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_read_timeout 60s;
    }

    location /healthz {
        proxy_pass http://127.0.0.1:8000/healthz;
        access_log off;
    }
}

几个容易忽略的地方:proxy_set_header 那几行一定要写全,否则后端拿到的永远是 127.0.0.1,
客户端真实 IP 就丢了;/healthz 关掉 access_log 是因为探活请求频率很高,写日志会污染磁盘。

配置改完先检查语法再重载:

sudo nginx -t && sudo systemctl reload nginx

五、用 systemd 托管 Gunicorn

systemd 比 nohup 可靠得多,能自动重启、开机自启、日志统一交给 journald。
写一个 apiapp.service 放到 /etc/systemd/system/ 目录下:

[Unit]
Description=FastAPI Log Query Service
After=network.target

[Service]
User=apiapp
Group=apiapp
WorkingDirectory=/opt/apiapp
Environment='PATH=/opt/apiapp/venv/bin'
ExecStart=/opt/apiapp/venv/bin/gunicorn app:app \
    --workers 3 \
    --worker-class UvicornWorker \
    --bind 127.0.0.1:8000 \
    --timeout 60 \
    --access-logfile - \
    --error-logfile -
Restart=always
RestartSec=5

[Install]
WantedBy=multi-user.target

2 核 4G 的机器上开 3 个 worker 比较合适,开太多反而会因为内存不足被 OOM Killer 干掉。
启用之后:

sudo systemctl daemon-reload
sudo systemctl enable --now apiapp
sudo systemctl status apiapp

六、验证与常见问题排查

按顺序做四个检查,能快速定位九成问题:

  1. 进程是否在跑:systemctl status apiapp,看 Active 是不是 active (running)。
  2. 应用是否响应:在服务器上执行 curl -s 127.0.0.1:8000/healthz,应当返回 {“status”:“ok”}。
  3. Nginx 是否转发:curl -sI 127.0.0.1/healthz -H ‘Host: api-example-com’,看是不是 200。
  4. 公网是否可达:从外网机器访问 api-example-com 的 healthz 路径,拿到 200 才算真正跑通。

我踩过的坑主要有三个:一个是安全组没放行 443,Nginx 配得再对外也访问不了;
一个是忘记加 X-Forwarded-Proto,后端生成的重置密码链接全是 http,用户点不开;
还有一个是日志文件没做轮转,跑了一周把磁盘撑满,服务直接写不进去。
现在我会给 /var/log/nginx 加 logrotate 配置,保留 14 天压缩日志。

七、总结

这套组合的核心价值不在于技术多新,而在于每一层都做自己该做的事:Nginx 管入口和流量,
Gunicorn 管进程并发,FastAPI 管业务,systemd 管生命周期。出了问题看哪一层一目了然,
比把一个 Flask 应用直接扔到公网上裸跑要稳得多。

如果你的服务并发量不大,其实连 Gunicorn 都可以省掉,直接用 uvicorn 单进程跑也完全够用。
但只要涉及到多核利用和优雅重启,这套结构就是成本最低的稳妥方案。

后续还可以继续做的优化包括:接入 Prometheus + Grafana 做监控,给慢接口加 trace,
以及用 Docker Compose 把整套环境容器化,方便在多台机器之间迁移。有兴趣的话我会在后续文章里展开。

【声明】本内容来自华为云开发者社区博主,不代表华为云及华为云开发者社区的观点和立场。转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息,否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0)

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。