在我们的内部仪表盘上使用 Nginx 反向代理设置 App Hub
Source: Dev.to
请提供您希望翻译的文章正文内容(包括任何需要保留的代码块、链接或其他 Markdown 元素),我将为您完整地翻译成简体中文并保持原有的格式。
背景
我们的团队运行一个基于 Flask 的单页应用(SPA)作为内部仪表盘。随着功能的增多,我们需要一种快速在服务之间跳转的方式,于是我们创建了 App Hub 面板。
此次更改:
- 移除 3 个已退役的服务
- 新增 3 项服务(TechsFree Shop、TechsFree ERP、Accounting AI)
- 更新了已迁移服务的 URL
Problem: Host Header with IP‑Based Access
当在 App Hub 链接中通过 IP 地址引用另一内部服务器的服务时,Nginx 未能路由到正确的 vhost。
Nginx 配置假设基于子域名的路由(techsfree.com、blog.techsfree.com 等)。当使用原始 IP 访问时,Host 头会变成 IP 本身,这与任何 server_name 都不匹配。
解决方案:向默认服务器添加 Location
# /www/server/panel/vhost/nginx/0.default.conf
server {
listen 80 default_server;
# ...
location /shop/ {
root /www/apps/techsfree-shop;
try_files $uri $uri/ /shop/index.html;
}
location /erp/ {
proxy_pass http://127.0.0.1:3101/;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
}
}
关键点: 为 default_server 虚拟主机添加基于路径的 location 块。当直接通过内部 IP(无域名)访问服务时,这是最简洁的做法。
静态文件 vs 反向代理
| Service | Architecture | Nginx Config |
|---|---|---|
| TechsFree Shop | Vite‑构建的静态文件 | root 指令 |
| TechsFree ERP | 后端 API + 前端 | proxy_pass |
静态文件(Shop)
构建产物放置在 /www/apps/techsfree-shop/,由 Nginx 直接提供。由于是 SPA,try_files $uri $uri/ /shop/index.html 处理客户端路由。
反向代理(ERP)
后端运行在 Node.js(ts‑node)上,监听 3101 端口。Nginx 将请求转发给它。proxy_pass 中的结尾斜杠至关重要:
# 错误:/erp/ 前缀会传递给后端
location /erp/ {
proxy_pass http://127.0.0.1:3101; # 没有结尾斜杠
}
# 正确:/erp/ 前缀在转发前被剥除
location /erp/ {
proxy_pass http://127.0.0.1:3101/; # 有结尾斜杠
}
在 proxy_pass URL 中是否包含结尾斜杠会改变路径的处理方式。容易忽视,调试时令人恼火。
同时管理 systemd 与 PM2
另一个教训:混用进程管理器会导致混乱。
仪表盘在 Ubuntu 上使用 systemd 运行。我曾短暂尝试将其迁移到运行 aaPanel (PM2) 的服务器上,但遇到权限问题后又回滚了。
# Check on systemd server
systemctl --user status task-dashboard.service
# Check on PM2 server (if migrated)
pm2 status
当同一服务同时在两个环境中运行时,修改没有生效——因为 systemd 会生成新进程却没有终止旧的进程。
# Find stale processes
ps aux | grep server.py
# Kill explicitly, then restart
kill <pid>
systemctl --user restart task-dashboard.service
部署后务必检查是否存在重复的进程。
错误修复:JSON 字段名称不一致
在实现“批量删除已完成任务”功能时,我遇到了一个容易被忽视的错误。
任务的 JSON 使用 completed,但 API 的过滤逻辑却检查了 done:
# Buggy
active_tasks = [t for t in tasks if not t.get('done')]
# Correct
active_tasks = [t for t in tasks if not t.get('completed')]
一旦确定了字段名称,就要在前端、后端和文档中保持一致。任何偏差都会导致类似的错误。
摘要
- 向默认服务器添加 location 以处理基于 IP 的访问。
- 根据使用场景选择静态文件或反向代理(单页应用需要
try_files)。 - 注意
proxy_pass中的尾随斜杠——它会改变路径处理方式。 - 在混合使用进程管理器时,部署后检查是否有重复进程。
- 在整个技术栈中保持 JSON 字段名的一致性。
即使是小型内部工具,在类似生产环境中运行时也能教会我们真实的经验教训。
Tags: nginx, reverse-proxy, spa, flask, systemd, deployment, webdev, infra