云主机部署图片/视频管理系统的实践经验

云主机部署图片/视频管理系统的实践经验(Nginx + FastAPI + 数据盘)

读者对象 :准备把个人 Web 应用(尤其是带大量图片、视频存储的)部署到云服务器的开发者。

解决的问题:从「本地能跑」到「服务器上稳定对外」这一步里,那些官方文档不会写、但你几乎一定会撞上的部署、防火墙、数据盘挂载与存储选型问题。

本文记录一次将 Python (FastAPI) + Vite 前端 + SQLite + 本地文件存储的媒体类应用部署到云服务器(OpenCloudOS,RHEL 系)的过程。文中路径与参数均为示例,不涉及具体项目信息。

文章目录

一、部署架构:后端不直接对外

不要让 uvicorn / gunicorn 直接监听公网端口,统一走 Nginx 反向代理:

复制代码
浏览器 ──> Nginx
            ├─ /        → 前端静态产物(SPA 回退 index.html)
            ├─ /media/  → Nginx 直接发送上传的图片/视频(不经过后端)
            └─ /api/    → 反向代理到 127.0.0.1:8000(后端只监听本机)

关键点是媒体文件交给 Nginx 直发 ,而不是用后端读文件返回。用后端 StaticFiles 吐大文件时,进程既要处理请求又要搬运字节,内存和并发都吃亏;换成 Nginx alias 后,Range 请求(视频拖动)、缓存头、高并发读取都由 Nginx 承担,后端只处理业务接口。

后端进程用 systemd 托管,配置 Restart=always 并设置开机自启:

ini 复制代码
# /etc/systemd/system/gallery.service
[Unit]
Description=Gallery API
After=network.target

[Service]
Type=simple
WorkingDirectory=/opt/gallery
ExecStart=/opt/gallery/venv/bin/uvicorn main:app --host 127.0.0.1 --port 8000 --workers 1
Restart=always
RestartSec=3
Environment=PYTHONUNBUFFERED=1
Environment=TZ=Asia/Shanghai

[Install]
WantedBy=multi-user.target

workers 数量受内存限制,小内存机器保持 1 即可。

二、依赖与环境

如果项目缺少 requirements.txt,可以先扫描源码里的 import 反推第三方依赖,再逐个锁定版本:

bash 复制代码
grep -rhE "^(import|from) " . | sort -u

几个固定要装的:fastapi、uvicorn[standard]、sqlalchemy、pydantic、Pillow,以及------上传接口必须装 python-multipart ,FastAPI 处理 UploadFile / Form 依赖它,漏装会直接 500。

关于 Redis 有一个判断技巧:如果代码里用的是 redis.from_url(...),它在构造时并不会建立连接,且调用处若都包了 try/except,那么只需要安装 redis 这个包让 import 通过即可,不必真的部署 Redis 服务 。不要因为看到 import redis 就去装中间件。

虚拟环境隔离:

bash 复制代码
python3 -m venv /opt/gallery/venv
/opt/gallery/venv/bin/pip install -r requirements.txt

三、二进制依赖(ffmpeg)

视频封面依赖 ffmpeg,两个常见误区:

  1. 平台必须对应 。Windows 的 ffmpeg.exe 无法在 Linux 运行,服务器需要的是 Linux x86_64 版本。
  2. 下载方式。服务器直连境外源可能很慢或超时,此时可以「本地下载好 Linux 静态包 → 上传到服务器 → 解包安装」,往往比服务器直接拉更快更稳。

在 Windows 的 curl 上做 HTTPS 下载,若遇到证书吊销检查离线报错 CRYPT_E_REVOCATION_OFFLINE,加参数绕过:

bash 复制代码
curl --ssl-no-revoke -L -o ffmpeg.tar.xz <下载地址>

安装到 /usr/local/bin 后,systemd 服务默认 PATH 即可找到,无需额外配置。

四、远程操作与文件传输

Git Bash 的路径转换问题 (Windows 下高频坑):在 Git Bash(MSYS)里,命令行参数中形如 /opt/gallery/deploy.tgz 的绝对路径会被自动转换成 C:/Program Files/Git/opt/gallery/deploy.tgz 再传给程序。这会导致远程路径错乱,表现为 SFTP 报 ENOENT、cat 重定向失败等,很容易被误判为「SFTP 子系统坏了」。解决方式是关闭转换:

bash 复制代码
export MSYS_NO_PATHCONV=1 MSYS2_ARG_CONV_EXCL='*'

免交互 SSH 在 Windows 上可用 Python 的 paramiko,密码通过环境变量传入脚本,避免写进文件。传文件优先用 sftp.put;确实遇到 SFTP 不可用时,可退化为 exec_command("cat > 远端路径") 流式写入,或分块 base64 追加(可靠但慢)。

打包上传用 tar 并排除 __pycache__、日志等;上传的脚本若带 Windows 换行符,执行前用 sed -i 's/\r$//' 处理一下。

五、云服务器的两层防火墙

轻量应用服务器通常存在两道独立的防火墙,外网可访问要求两道都放行:

  1. 系统层(firewalld / iptables);
  2. 云平台层(控制台或云 API 配置的防火墙,SSH 无法修改)。

系统层放行时注意:启用 firewalld 会接管并可能清空原有 iptables 规则(含厂商注入的安全链),操作前先备份,并确认 ssh 仍在放行列表,避免把自己锁在外面:

bash 复制代码
iptables-save > /root/iptables.bak.$(date +%s)
firewall-cmd --permanent --add-port=8080/tcp
firewall-cmd --reload
firewall-cmd --query-service=ssh

云平台层可用 SDK 调 API 添加规则。若子账号密钥缺少对应资源权限会报 NoPermission,需要主账号在访问管理里给子账号补上相应预设策略(如轻量服务器的 FullAccess)。

六、数据盘挂载

为媒体单独挂一块数据盘时,注意三个容易出问题的点:

  1. 挂载点权限 。不要把媒体放在 /root 下------/root 通常是 550 / 700,Nginx 的运行用户无法穿越该目录,导致文件 403。挂载点应放在可被 web 用户遍历的位置,例如 /data(755)。
  2. fstab 必须用绝对路径 + 真实 UUID 。自动生成的 fstab 有时会把挂载点写成相对路径,重启后挂载失败。用 blkid 取真实 UUID 后重写,并加 nofail(避免磁盘异常时卡住开机):
bash 复制代码
blkid /dev/vdb
# /etc/fstab 追加:
UUID=<真实uuid>  /data  ext4  defaults,nofail  0 2
mount -a
findmnt /data      # 确认源确实是 /dev/vdb

用脚本改 fstab 时,尽量用 printf 直接写入字面值,避免 shell 变量在多层引号中未展开、把 ${UUID} 原样写进文件。

  1. 在线扩容。云盘在控制台扩容容量后,操作系统内再扩展文件系统即可,无需重启:
bash 复制代码
resize2fs /dev/vdb     # ext4

如果希望应用「不改代码」就把文件写到数据盘,可以用软链把应用写入目录指向数据盘目录:

bash 复制代码
ln -sfn /data/uploads /opt/gallery/uploads

应用仍写 uploads/,实际落到数据盘;数据库文件则保留在 SSD。验证是否真的落到目标盘:调用一次上传接口后 find /data/uploads 查看文件、df 看容量变化,确认无误后清理测试数据。

需要提醒的是,软链属于过渡方案,路径真相分散、且备份要同时覆盖两块盘(SSD 的数据库 + 数据盘的媒体)。更彻底的做法见第八节。

七、存储选型:SSD / 机械盘 / 对象存储

判断云硬盘介质看 IOPS / 吞吐公式,不要看名字。 例如某云的「高性能云硬盘」IOPS 上限约 6000、吞吐上限约 150 MB/s,本质是机械盘;「SSD 云硬盘」IOPS 可达数万、吞吐更高,才是固态。名字会营销,规格参数不会。

按负载分工:

  • 数据库/元数据(随机小 IO、延迟敏感)→ SSD;
  • 原图/视频(大文件顺序读写、容量大、访问偏冷)→ 机械盘,成本低且够用。

对象存储(COS / OSS)要算总账:存储单价低,但外网下行流量和请求次数是隐藏成本。单人自用、访问量小的场景,用对象存储往往是「省了存储费、多了流量费和复杂度」,本地数据盘更划算。对象存储更适合海量访客、需要 CDN、多实例共享或冷数据归档的场景。

选型前先做容量估算:平均单文件大小 × 目标数量,判断是否需要额外数据盘,不要等写满才处理。

八、可扩展性:趁数据量小先把结构定对

真正「便宜时不做、贵时做不完」的是数据布局与结构,建议在早期一次做对:

  • 存储抽象层:定义统一的存储接口(save / delete / url / exists),默认本地盘实现。将来换更大的盘或对象存储时只替换实现,不改业务代码。
  • 内容哈希分桶目录 :用 {ab}/{cd}/{hash}.ext 这样的路径,不依赖拍摄时间或导入时间(元数据可能缺失),避免单目录堆积海量文件,也基本不需要重新布局。
  • 数据库结构:标签用关联表而非 JSON 列(可索引、跨库可移植);只存相对 key、不存绝对路径;为高频查询建复合索引;EXIF 等大字段拆分存储。趁表还是空的,这些改动几乎零迁移成本。
  • 可延后的运行时优化:后台任务队列、把重 IO 移出事件循环、keyset 分页、缓存、CDN------等数据量上来再按需加,且都不影响数据结构。

九、验证:以实测为准

改完不要只凭代码推断,尽量实际验证:

  • 界面:用无头浏览器截图验证渲染结果,稳定可控:
bash 复制代码
msedge --headless=new --disable-gpu --hide-scrollbars \
  --window-size=1440,900 --virtual-time-budget=8000 \
  --screenshot=shot.png http://localhost:8080/

--virtual-time-budget 给前端加载数据留时间,--window-size 决定视口宽度(否则响应式布局可能折叠侧栏,导致误判)。

  • 功能:不要只测首页 200,把「上传 → 落盘核对 → 经 Web 读取 200」这条链完整走一遍。
  • 服务 :systemctl is-active、journalctl -u <svc>、nginx -t。

十、安全注意事项

  • 服务器密码、云 API 密钥等凭据不要明文写进代码或文档;一旦泄露应立即在控制台轮换。
  • 端口对 0.0.0.0/0 放行等于全网可访问,含隐私的站点应加访问控制:Nginx auth_basic、来源 IP 白名单,或至少 HTTPS + 登录。
  • 生产环境把 CORS 从 * 收紧到具体域名。
  • 调试类、一次性全表载入内存的接口,上线前移除或加保护,既防数据泄露也防 OOM。

小结

回到开头那个问题------从「本地能跑」到「服务器上稳定对外」,卡住人的往往不是业务代码,而是这些平台差异、权限、网络分层和存储选型的细节。几条可复用的结论:

  • 后端只监听本机,统一走 Nginx 反代;媒体由 Nginx 直发。
  • 上传接口记得装 python-multipart;惰性连接的客户端不必真部署服务。
  • Windows Git Bash 会篡改 / 开头的参数,远程操作用 MSYS_NO_PATHCONV=1。
  • 云服务器两层防火墙都要放行,动 firewalld 前备份并保住 ssh。
  • 数据盘挂载点别放 /root 下,fstab 用绝对路径 + 真实 UUID + nofail。
  • 存储按负载分介质,对象存储注意流量费;选型前先估容量。
  • 结构类改动(存储抽象、哈希分桶、schema)趁数据小做对,运行时优化可延后。
  • 一切改动以无头截图 + 端到端实测为准。

标签 :Linux Nginx FastAPI 云服务器 防火墙 云硬盘 对象存储 运维部署

背景说明:本文是个人项目上云过程中的一次实践整理,聚焦通用踩坑与取舍,命令与路径已做脱敏与泛化处理,具体参数请以你所用云厂商的文档为准。

相关推荐
外收内放14 小时前
Python基础语法练习题(59-原始版本与优化版本)
开发语言·python
ASS-ASH14 小时前
从力学与机械原理角度深度剖析:尊界V800刹车踏板支架断裂事件的技术逻辑
人工智能·python·数据可视化·机械工程·汽车安全·物理仿真·制动系统
xhy_070714 小时前
AI 读了 .env,密钥会原样发给大模型吗?WES Code 的密钥打码(附实测)
人工智能·python·数据挖掘·flask·ai编程·wes code
泡海椒14 小时前
JQuick-Excel 实战:用 STYLE 配置单元格与区域样式
开发语言·python·excel
威联通网络存储15 小时前
TS-h1283XU-RP 在医药批记录与检验场景的部署
网络·python
Patrick在香港15 小时前
时间戳凭空早了 8 小时:datetime.utcnow() 弃用实测与漂移复盘
数据库·python·标准库·datetime·时区·弃用
东莞市云毅网络有限公司21 小时前
AI 引用句逐条回指原文:让回答可回溯的校验实现
python·数据清洗·rag·企业知识库·文档解析
数字融合1 天前
透明化视频三维矿山井下照明重建技术
人工智能·python·数码相机
yi0111 天前
LeetCode 219:存在重复元素 II——哈希表记录“最近一次出现的位置”
数据结构·人工智能·笔记·python·算法·leetcode·哈希表
Marst Code1 天前
上位机开发日记 · 第 2 篇 · 架构先行:六层分层与边界
python