Skip to content

14.3.4 部署前后端分离应用

本节目标:掌握前后端分离架构的通用部署流程,学会用 Claude Code 辅助配置反向代理,适用于任何技术栈(Node.js / Python / PHP / Java / Go / .NET 等)。

本节与 14.3.2 的区别

  • 14.3.2:适合 Next.js 这种"前后端一体"的全栈框架,一个容器搞定
  • 本节(14.3.4):适合"前后端分离"的架构,前端和后端是独立的项目,需要分别部署

如果你的项目是 Next.js/Nuxt 等全栈框架,直接看 14.3.2 即可。如果你的前端是 React/Vue,后端是独立的 API 服务(Node.js/Python/Java 等),那就需要本节的方案。

小明的全栈项目有前端、后端 API、数据库三个部分。"之前在 Vercel 上是一个项目搞定,现在要分开部署?"

老师傅说:"Vercel 帮你把这些打包在一起了,但在自己的服务器上,你需要理解它们各自的角色。好处是——你对每一层都有完全的控制权。"

他画了一张图:"前后端分离的架构长这样——"

前后端分离架构

用户浏览器


  OpenResty(反向代理):80

    ├── /          → 前端静态文件(HTML/CSS/JS)
    ├── /api/*     → 后端服务(容器端口 3000/8000/8080...)

    └── 后端服务


        数据库(PostgreSQL/MySQL/MongoDB...)

三个服务各司其职:

  • OpenResty:对外的唯一入口,负责分发请求
  • 后端服务:处理业务逻辑和数据库操作(Node.js / Python / Java / Go 等)
  • 数据库:存储数据

什么是反向代理?

反向代理就像一个"前台接待员":

  • 用户的所有请求都先到前台(OpenResty)
  • 前台看请求的路径,决定转给谁处理:
    • 访问 / → 返回前端页面
    • 访问 /api/users → 转发给后端服务
  • 用户感觉只和一个服务器打交道,但背后可能有多个服务在协作

好处

  1. 统一入口:用户只需要记一个域名
  2. 避免跨域:前端和 API 在同一个域名下
  3. 安全:后端服务不直接暴露在公网

小明画了一张简单的图:用户请求先到 OpenResty,OpenResty 看路径——如果是 /api 开头就转给后端,其他的就返回前端页面。"原来 Vercel 在背后也是这么干的,只不过它帮我把这些全藏起来了。"

它们都运行在 Docker 容器里,通过容器网络互相通信。

通用部署流程

无论你的后端是什么技术栈,部署流程都是类似的:

第一步:部署数据库

根据项目需求,在 1Panel 应用商店安装对应的数据库:

  • PostgreSQL:适合关系型数据、复杂查询
  • MySQL:适合传统 Web 应用
  • MongoDB:适合文档型数据
  • Redis:适合缓存、会话存储

安装完成后,创建应用专用数据库和用户,记下连接信息(主机名、端口、用户名、密码)。

1Panel 数据库容器命名规则

1Panel 创建的数据库容器名遵循 1Panel-{数据库类型}-{随机4位字母} 的格式:

  • PostgreSQL: 1Panel-postgresql-ukow
  • Redis: 1Panel-redis-w94p
  • MySQL: 1Panel-mysql-abcd
  • MongoDB: 1Panel-mongodb-xyz1

如何查看实际的容器名: 在 1Panel 的「容器」页面,列表的第一列就是容器名。你也可以在「数据库」页面点击对应数据库的「详情」查看连接信息。

后端应用通过容器名(而非 localhost)连接数据库。

常见数据库连接字符串示例

PostgreSQL:

bash
DATABASE_URL="postgresql://用户名:密码@1Panel-postgresql-ukow:5432/数据库名"

MySQL:

bash
DATABASE_URL="mysql://用户名:密码@1Panel-mysql-abcd:3306/数据库名"

Redis(有密码):

bash
REDIS_URL="redis://:密码@1Panel-redis-w94p:6379"

Redis(无密码):

bash
REDIS_URL="redis://1Panel-redis-w94p:6379"

MongoDB:

bash
MONGODB_URI="mongodb://用户名:密码@1Panel-mongodb-xyz1:27017/数据库名"

第二步:部署后端服务

根据后端技术栈选择部署方式:

技术栈1Panel 运行环境(语言)启动命令参考
Node.js / Next.jsNode.jsgit pull && pnpm build && pnpm start
Python / FastAPIPythongit pull && pip install -r requirements.txt && uvicorn main:app --host 0.0.0.0
PHP / LaravelPHPPHP-FPM 自动启动,通过 OpenResty 反向代理访问
Java / Spring BootJavajava -jar app.jar
GoGo./app
.NET / ASP.NET Core.NETdotnet run --urls http://0.0.0.0:5000

运行环境 = Docker 容器

1Panel 的「运行环境」本质上是 Docker 容器。每种语言(Node.js、Python、PHP、Java、Go、.NET)对应一个预配置好的基础镜像,你选择语言和版本后,1Panel 会用 docker-compose 帮你管理容器的创建和启动。

关键配置

  1. 环境变量:数据库连接字符串、密钥等
  2. 端口映射:在高级配置中,把容器内部端口(如 3000)映射到服务器外部端口(如 3001)
  3. 启动命令:确保每次重启时自动拉取最新代码
  4. 容器名称:用于容器间通信(如反向代理通过容器名转发请求)

小明的项目是 Node.js,他在「运行环境」中创建了一个容器,填了项目目录、启动命令、环境变量。几分钟后,后端 API 跑起来了。他在浏览器访问 http://服务器IP:3001/api/health,看到了 JSON 响应。

第三步:构建并部署前端

前端通常需要先在本地构建,然后上传到服务器:

bash
# 在本地电脑上
cd your-project

# 设置环境变量(指向服务器的后端 API)
echo "VITE_API_URL=http://你的服务器IP:3001" > .env.production

# 构建前端
npm run build  # 或 pnpm build / yarn build

# 构建产物通常在 dist/ 或 build/ 或 out/ 目录

上传到服务器:

bash
# 方式一:使用 scp
scp -r dist/* root@你的服务器IP:/opt/your-frontend/

# 方式二:使用 FinalShell 拖拽上传

在 1Panel 中创建静态网站:

  1. 进入「网站 > 网站」,点击「创建网站」
  2. 选择「静态网站」
  3. 主域名:yourdomain.com(或先用 IP 测试)
  4. 网站目录:/opt/your-frontend
  5. 点击确认

小明用 FinalShell 把构建好的前端文件拖到服务器上,然后在 1Panel 创建了静态网站。浏览器访问服务器 IP,前端页面出来了——但他试着登录,发现报错:"跨域请求被阻止"。

第四步:配置反向代理(关键步骤)

现在前端和后端是分开的——前端在 80 端口,后端在 3001 端口。前端发起的 API 请求会遇到跨域问题。解决方案是配置反向代理,让前端和后端在同一个域名下。

推荐工作流:让 Claude Code 帮你写配置

  1. 获取 1Panel 的默认配置

在 1Panel 中,进入刚创建的静态网站的设置页面,点击「配置文件」选项卡,复制整个配置内容。

  1. 在本地用 Claude Code 完善配置

打开本地的 Claude Code,发送以下 prompt:

我需要配置 OpenResty 反向代理,实现前后端分离部署。

当前 1Panel 默认配置:
[粘贴你复制的配置]

我的需求:
- 前端静态文件在 /opt/your-frontend
- 后端 API 容器名是 your-backend,端口 3000
- 所有 /api/* 请求转发到后端
- 其他请求返回前端页面(SPA 路由支持)

请帮我完善这个配置。

Claude Code 会根据你的实际情况生成完整的配置,包括:

  • 正确的 proxy_pass 地址
  • 必要的请求头设置
  • 超时配置
  • SPA 路由支持(try_files
  1. 复制回服务器

把 Claude Code 生成的配置复制回 1Panel 的「配置文件」编辑框,点击「保存并重载」。

小明把 1Panel 的默认配置复制到本地 Claude Code,说明了自己的需求。Claude Code 很快生成了完整的配置,包括反向代理规则、请求头设置、超时配置。小明复制回 1Panel,点击保存——刷新浏览器,这次登录成功了!

为什么推荐这个工作流?

  1. Claude Code 理解你的项目:它知道你的容器名、端口、路由结构
  2. 避免手动出错:容器名拼写错误、路径配置错误是常见问题
  3. 自动补全细节:超时设置、请求头、错误处理等容易遗漏的配置
  4. 可以反复调整:配置不对可以继续让 Claude Code 修改

如果 Claude Code 生成的配置有问题怎么办?

  1. 查看错误日志:在「应用商店 → 已安装 → OpenResty」中查看错误日志
  2. 把错误信息发给 Claude Code:复制错误日志,告诉 Claude Code "我的反向代理配置有问题,错误日志是..."
  3. Claude Code 会帮你修正:它会分析错误原因,生成修正后的配置
  4. 常见错误
    • 容器名拼写错误 → 检查容器名是否正确
    • 端口号错误 → 检查后端服务的实际端口
    • 路径配置错误 → 检查 proxy_pass 的路径是否正确

如果多次尝试仍然失败,可以参考本节末尾的"典型的反向代理配置示例"手动修改。

典型的反向代理配置示例

Claude Code 生成的配置通常长这样:

nginx
server {
    listen 80;
    server_name yourdomain.com;

    # 前端静态文件
    location / {
        root /opt/your-frontend;
        try_files $uri $uri/ /index.html;  # SPA 路由支持
    }

    # 后端 API 反向代理
    location /api/ {
        proxy_pass http://your-backend:3000/api/;
        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_connect_timeout 60s;
        proxy_send_timeout 60s;
        proxy_read_timeout 60s;
    }
}

第五步:更新前端 API 地址

反向代理配置好后,前端的 API 地址应该改成相对路径(因为前端和后端在同一个域名下了):

bash
# 在本地电脑上
echo "VITE_API_URL=/api" > .env.production  # 或 NEXT_PUBLIC_API_URL=/api

# 重新构建
npm run build

# 重新上传到服务器
scp -r dist/* root@你的服务器IP:/opt/your-frontend/

小明重新构建并上传了前端。刷新浏览器,所有功能都正常了——"前端、后端、数据库,三个部分在自己的服务器上协同工作了。"

不同技术栈的差异点

虽然部署流程类似,但不同技术栈有一些细节差异:

Node.js / Next.js

  • 运行环境语言:Node.js
  • 版本选择:选择和本地一致的 Node.js 版本(最新版本 24.10.0)
  • 启动命令pnpm build && pnpm start
  • 端口:通常 3000
  • 环境变量DATABASE_URLNODE_ENV=production

Python / FastAPI

  • 运行环境语言:Python
  • 版本选择:选择和本地一致的 Python 版本(最新版本 3.14.0)
  • 启动命令pip install -r requirements.txt && uvicorn main:app --host 0.0.0.0 --port 8000
  • 端口:通常 8000
  • 环境变量DATABASE_URLPYTHONUNBUFFERED=1

PHP / Laravel

  • 运行环境语言:PHP
  • 版本选择:选择和项目要求一致的 PHP 版本(最新版本 8.5.2)
  • 部署方式:PHP 运行环境提供 PHP-FPM 服务(默认端口 9000),需要配合 OpenResty 使用
  • 启动命令:PHP-FPM 自动启动,无需配置启动命令
  • 环境变量DB_CONNECTIONDB_HOST(填容器名)、APP_ENV=production
  • 注意:PHP 项目通常通过 OpenResty 反向代理到 PHP-FPM,而不是独立运行 HTTP 服务器

Java / Spring Boot

  • 运行环境语言:Java
  • 版本选择:选择和项目一致的 JDK 版本(最新版本 22)
  • 启动命令java -jar app.jar
  • 端口:通常 8080
  • 环境变量SPRING_DATASOURCE_URLSPRING_PROFILES_ACTIVE=prod

Go

  • 运行环境语言:Go
  • 版本选择:选择和项目一致的 Go 版本(最新版本 1.25)
  • 启动命令./app
  • 端口:自定义(如 8080)
  • 环境变量:根据项目自定义

.NET / ASP.NET Core

  • 运行环境语言:.NET
  • 版本选择:选择和项目一致的 .NET 版本(最新版本 10.0)
  • 启动命令dotnet run --urls http://0.0.0.0:5000
  • 端口:通常 5000
  • 环境变量ConnectionStrings__DefaultConnectionASPNETCORE_ENVIRONMENT=Production

测试完整流程

部署完成后,测试以下功能确保一切正常:

  1. 访问首页http://你的服务器IP → 看到前端页面
  2. API 请求:前端发起的 API 请求能正常返回数据
  3. 数据持久化:刷新页面后数据仍然存在
  4. 用户认证:登录、退出、权限验证等功能正常

小明把这些功能都测试了一遍,全部正常。他打开开发者工具的 Network 面板,看到前端页面是从 / 加载的,API 请求是发往 /api/xxx 的——"用户完全感觉不到后面有三个服务在跑。"

常见问题排查

现象可能原因解决方案
前端页面打不开安全组没开 80 端口去云厂商控制台开放 80 端口
API 请求 404反向代理配置错误检查 proxy_pass 的容器名和路径
API 请求 502后端容器没启动在「运行环境」中查看日志,重启容器
数据库连接失败DATABASE_URL 主机名填错用容器名(如 postgresql)替代 localhost
跨域错误反向代理未生效确认 OpenResty 配置已保存并重载
SPA 路由 404缺少 try_files 配置让 Claude Code 补充 SPA 路由支持

遇到问题时的调试流程

  1. 先看日志:在「容器」或「运行环境」中查看日志,90% 的问题日志里有答案
  2. 检查容器状态:确认所有容器都在运行中
  3. 测试容器间通信:进入后端容器终端,ping postgresql 测试网络连通性
  4. 让 Claude Code 帮忙:把错误日志发给 Claude Code,让它帮你分析

架构优势总结

通过前后端分离部署,你获得了:

  1. 统一入口:用户只需要记一个域名,不需要知道后端在哪个端口
  2. 避免跨域:前端和 API 在同一个域名下,不存在跨域问题
  3. 安全:后端服务不直接暴露在公网,只有 OpenResty 对外
  4. 灵活扩展:前端、后端、数据库可以独立升级和扩容
  5. 完全控制:你对每一层都有完全的控制权,不受平台限制

小明回想起在 Vercel 上部署时,一个 vercel deploy 就搞定了。现在虽然步骤多了,但他理解了每一步在做什么——"Vercel 帮我做的事情,我现在自己也能做了。而且有 Claude Code 帮忙写配置,也没那么难。"


下一步

应用部署完成了,但现在只能通过 IP 访问,不够专业。接下来配置域名和 HTTPS——14.4 配置域名与证书