🚀 Gradio AI 应用云服务器部署总结文档

作者:

在


📖 概述

本文档记录了一个基于 Gradio + MySQL + OpenAI 兼容接口 的 Python Web 应用,如何从本地环境打包,通过 1Panel 面板 部署到腾讯云轻量应用服务器,并最终绑定自定义域名和 HTTPS 证书的完整过程。

部署架构: 用户浏览器 -> HTTPS (Nginx/OpenResty) -> 反向代理 -> Docker 容器 (Gradio 应用) -> Docker 容器 (MySQL)


🛠️ 第一阶段:本地项目准备与改造

1.1 代码修改 (app.py)

为了让 Gradio 应用能正常运行在 Docker 和反向代理后面,对 app.py 做了以下关键修改:

  • 网络绑定:将 APP_HOST 默认值改为 0.0.0.0,允许外部访问。
  • 代理路径:增加 GRADIO_ROOT_PATH 环境变量读取,适配反向代理的子路径。
  • 访问控制:增加 APP_AUTH_USER 和 APP_AUTH_PASS 环境变量,便于测试阶段加锁防止 API 被滥用。
  • 启动参数:将 theme 从 Blocks 构造器移动到 launch() 方法,消除 Gradio 6.x 的警告。

1.2 依赖清单修正 (requirements.txt)

  • 发现并补全了缺失的 PDF 导出依赖 fpdf2>=2.7.0。

1.3 编写 Dockerfile

创建了基于 python:3.11-slim 的 Dockerfile,关键优化点:

  • 设置了 TZ=Asia/Shanghai 保证日志时间正确。
  • 更换国内镜像源:RUN pip install --no-cache-dir -i https://pypi.tuna.tsinghua.edu.cn/simple -r requirements.txt,解决了在云服务器上构建时因网络问题导致的超时失败。
  • 暴露 7860 端口,启动命令 CMD ["python", "app.py"]。

1.4 准备服务器端 .env 文件

本地 .env 中的 DB_HOST=127.0.0.1 在 Docker 中会导致连接失败。服务器端的 .env 做了如下调整:

  • APP_HOST=0.0.0.0
  • DB_HOST=[MySQL容器名称](而不是 127.0.0.1)
  • DB_USER 和 DB_PASSWORD 使用独立创建的数据库账号,不使用 root。

🗄️ 第二阶段:服务器数据库与文件准备

2.1 独立数据库隔离

在 1Panel 的【数据库】中新建了 MySQL 数据库:

  • 数据库名:ai_jianli
  • 用户名:ai_jianli_user(独立账号,避免与博客数据库冲突)
  • 权限:所有人 (%)(允许 Docker 容器网络访问)

2.2 上传项目文件

将项目文件上传到服务器 /opt/ai_jianli 目录,排除了不需要的文件(如 .venv、__pycache__、本地 .env、openspec 等),只保留:

  • app.py、requirements.txt
  • utils/ 文件夹
  • modules/ 文件夹
  • Dockerfile
  • 服务器专用的 .env 文件

🐳 第三阶段:构建镜像与启动容器

3.1 构建 Docker 镜像

在 1Panel 的【容器】->【镜像】中构建:

  • 名称:ai-jianli
  • 路径:/opt/ai_jianli/Dockerfile
  • 标签:latest

踩坑记录:第一次构建因 pip install 网络超时卡死,产生了一个悬空的“幽灵容器”占用镜像导致无法删除。通过强制停止并删除该容器,并修改 Dockerfile 加入清华源后,第二次构建成功(镜像体积约 523MB)。

3.2 创建并运行容器

在 1Panel 的【容器】中创建:

  • 镜像:ai-jianli:latest
  • 端口映射:7860:7860
  • 网络:必须与 MySQL 容器处于同一个 Docker 网络(如 1panel-network),否则无法连接数据库。
  • 重启策略:失败后重启 / 总是重启。
  • 存储卷挂载(推荐):将宿主机 /opt/ai_jianli/.env 挂载到容器 /app/.env,方便后续修改配置无需重新构建镜像。

3.3 验证启动日志

容器启动后,日志显示:

  • MySQL OK(数据库连接成功)
  • 数据库结构 OK,导入种子题目 38 道
  • LLM OK,模型: [模型名称]
  • 启动 Gradio @ 0.0.0.0:7860
  • 此时通过 http://[服务器IP]:7860 已可正常访问。

🌐 第四阶段:域名解析与反向代理

4.1 DNS 解析(关键踩坑)

  • 域名在阿里云购买,但服务器在腾讯云,且域名 NS 已切换至腾讯云 DNSPod。
  • 教训:必须在实际生效的 DNS 服务商(腾讯云 DNSPod)处添加解析记录,在阿里云控制台添加是无效的。
  • 添加记录:主机记录 ai,类型 A,记录值 [服务器公网IP]。

4.2 创建反向代理网站

在 1Panel 的【网站】->【创建网站】中:

  • 类型:反向代理
  • 主域名:ai.example.com
  • 代理地址:127.0.0.1:7860(注意:不要写 http://127.0.0.1:7860,1Panel 的输入框左边已经默认有 http 下拉框,重复会导致 invalid port 报错)。

🔒 第五阶段:SSL 证书与 HTTPS 配置

5.1 申请 Let’s Encrypt 证书

  • 踩坑:最初使用“DNS账号”方式申请,因 NS 不在阿里云导致验证卡在“申请中”。
  • 解决:放弃 DNS 验证,改为 “HTTP 文件验证”。由于域名已解析且 80 端口畅通,HTTP 验证 1 分钟内即签发成功。
  • 在网站设置 -> HTTPS 中,开启 HTTPS,选择刚申请的证书,开启“强制 HTTPS”和 HSTS。

5.2 Nginx 配置调优(Gradio 专属)

Gradio 极度依赖 WebSocket 和流式输出。在【网站】->【配置文件】中确认 location / 块包含以下配置:

proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
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 86400;
  • 解决流式输出失效问题:如果发现 AI 翻译或回答是“一次性全部显示”而不是“打字机式流式输出”,在 Nginx 配置中加入 proxy_buffering off; 并重载。

🛡️ 第六阶段:测试与安全加固

6.1 功能测试

  • 访问 https://ai.example.com,页面正常加载,带有安全锁。
  • 测试“翻译润色”和“简历生成”模块,功能正常。
  • 测试“导出 DOCX”,文件可正常下载(PDF 导出因 Linux 缺少微软雅黑字体,暂时乱码,建议使用 DOCX)。

6.2 安全建议

  • 关闭 7860 端口:在 1Panel 防火墙和腾讯云安全组中,关闭 7860 端口的外部访问,仅保留 80 和 443,防止别人通过 IP:7860 绕过域名。
  • 添加访问认证:在 .env 中开启 APP_AUTH_USER 和 APP_AUTH_PASS,重启容器生效,防止 API 额度被恶意消耗。
  • 密钥轮换:如果在部署过程中不小心暴露过 API Key 或数据库密码,务必去对应控制台重新生成并更新 .env。

📋 附录:常见问题速查

问题现象可能原因解决方案
构建镜像时卡在 pip install网络超时Dockerfile 中加入清华源 -i https://pypi.tuna.tsinghua.edu.cn/simple
容器启动后日志报 MySQL 不可达网络不通或 DB_HOST 错误确认 Gradio 容器与 MySQL 容器在同一 Docker 网络,DB_HOST 写 MySQL 容器名
反向代理创建报错 invalid port代理地址重复写了 http://改为纯 IP + 端口格式 127.0.0.1:7860
SSL 证书一直卡在“申请中”DNS 验证失败(NS 不在当前平台)改用“HTTP 文件验证”方式申请证书
域名访问白屏或按钮点不动WebSocket 未配置Nginx 配置中加入 proxy_http_version 1.1 和 Upgrade 头
AI 输出不是打字机效果Nginx 缓冲了响应Nginx 配置中加入 proxy_buffering off;

文档生成时间:2026-09-11
部署环境:腾讯云轻量服务器 + Ubuntu + 1Panel + Docker

评论

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注