返回博客列表

博客上线中的 Nginx、CORS 与 MySQL 排障复盘

2026-06-1710 分钟阅读
工程部署DockerNginxTroubleshooting
本文目录
  1. 问题背景
  2. Nginx upstream 找不到后端
  3. CORS 环境变量解析失败
  4. MySQL 迁移时 Access denied
  5. 本机可访问但公网 IP 不通
  6. 服务器没有 npm 怎么测试
  7. 收获

这篇文章记录的是个人技术博客上线过程中一组真实排障问题。它们看起来分别属于 Nginx、FastAPI、MySQL、Docker Compose 和服务器环境,但本质上都指向同一件事:一个全栈项目能不能稳定上线,不只取决于代码能不能运行,还取决于配置、网络、启动顺序和验证方式是否闭环。

问题背景

博客项目包含 Next.js 前端、FastAPI 后端、MySQL、Redis 和 Nginx。前端主要服务公开博客和简历作品展示,后端提供文章、AI 草稿和发布管理接口。服务器配置不高,所以部署时不能依赖很重的本地工具链,也不能让所有服务无节制占用内存。

部署过程中遇到过几类典型问题:

现象直接表现核心原因
Nginx 反复重启host not found in upstreamupstream 名称和 compose 服务名不一致
后端容器重启CORS_ORIGINS 解析失败环境变量格式和配置类型不匹配
数据库迁移失败ERROR 1045 Access denied脚本使用的账号密码和容器实际配置不一致
本机 curl 正常,外网不通服务器 127.0.0.1 可访问,公网 IP 不行80 端口映射、防火墙或旧容器仍占用入口
服务器无法 npm smokenpm not found测试方式不适合生产服务器环境

Nginx upstream 找不到后端

一次部署时,Nginx 日志里出现类似 host not found in upstream "backend:8080" 的错误。这个问题不是后端代码挂了,而是 Nginx 容器启动时解析不到配置里的 upstream 主机名。

排查顺序是:

  1. 先看 docker compose ps,确认后端容器是否真的在同一个 compose 项目里。
  2. 再看 Nginx 配置里的 upstream 名称,例如 backend:8080。
  3. 对照 compose service 名、container name 和网络。
  4. 保证 Nginx、frontend、backend 在同一个 Docker 网络里。

这里最容易混淆的是 container name 和 service name。Docker Compose 网络里更推荐使用 service 名作为内部 DNS 名称。如果 Nginx 写的是 backend:8080,compose 里也应该存在对应的 backend 服务,或者统一改成实际可解析的服务名。

CORS 环境变量解析失败

后端曾经因为 cors_origins 配置启动失败。日志里能看到 pydantic settings 在解析环境变量时抛出 JSON decode 错误。

这个问题的根源是:配置字段希望拿到数组,但服务器环境变量里可能写成普通字符串。比如:

示例:CORS_ORIGINS=http://127.0.0.1,http://服务器IP

如果配置层直接把它当 JSON 解析,就会失败。更稳的方式是让代码支持两种输入:

对于低配置服务器上的一键部署脚本来说,逗号分隔更容易维护,也更不容易因为 shell 转义出错。这个问题让我意识到,生产配置不只是“类型正确”,还要考虑部署人员实际会怎么填写。

MySQL 迁移时 Access denied

数据库容器 healthy 后,迁移脚本仍然可能失败。比如执行 migration 时出现:

示例:ERROR 1045 (28000): Access denied for user 'root'@'localhost'

这个问题通常不是 SQL 文件本身错了,而是迁移脚本使用的账号、密码、host 和容器环境变量不一致。排查时要分清几件事:

  • MYSQL_ROOT_PASSWORD 是容器初始化时使用的密码。
  • 数据卷已经存在后,修改 env 不会自动重置数据库密码。
  • 在容器内连接 localhost 和从其他容器连接 mysql 是不同场景。
  • init SQL 只在空数据卷首次初始化时执行,不能替代后续 migration。

最终处理思路是让部署脚本从同一份 env 读取数据库密码,迁移连接明确使用 MySQL service host,并且把“初始化”和“迁移”分开处理。

本机可访问但公网 IP 不通

还有一种情况是服务器上执行:

示例:

都能返回正常结果,但外网访问公网 IP 失败。这时问题一般不在 Next.js 或 FastAPI,而在入口层。

我会按这个顺序排查:

  1. docker ps 看 Nginx 是否映射了 0.0.0.0:80->80/tcp。
  2. ss -lntp | grep ':80' 看 80 端口是不是被旧容器占用。
  3. 检查服务器安全组或防火墙是否开放 80。
  4. 确认当前服务的 Nginx 正在代理新 frontend,而不是旧的 my-blog_web 容器。

这类问题很适合用最小验证链路:先本机 curl,再容器日志,再端口监听,最后查外网防火墙。

服务器没有 npm 怎么测试

部署时我一开始尝试用 Node 版 smoke test,但服务器提示 npm not found。这说明测试方式不应该依赖生产服务器安装完整前端工具链。

后来我把服务器 smoke test 收敛成 bash + curl:

示例:bash scripts/smoke-test.sh http://127.0.0.1

它只验证真实 Nginx 暴露出来的页面和接口,包括首页、博客列表、文章详情、隐藏后台、robots、sitemap 和可选后端 ping。这样测试更贴近生产,也更适合低配置服务器。

收获

这次排障让我更清楚地理解:上线不是把容器跑起来就结束了。一个真正可演示的全栈作品,需要具备这些能力:

  • 能通过日志定位服务失败点。
  • 能理解 Docker Compose 网络和服务名解析。
  • 能把环境变量设计成更适合部署的形式。
  • 能区分数据库初始化和后续迁移。
  • 能用轻量 smoke test 验证真实入口。

这些问题虽然细碎,但很接近真实业务部署。对个人博客来说,它们让项目从“页面作品”变成了一个能上线、能验证、能维护的全栈系统。