基于 H3ConuMS2 部署模式,适配 qiji 项目: - 新增 qiji-backend FRP 隧道 (8002→18061) - 前端容器化部署 (qiji-frontend:18063) - OpenResty 站点配置 (qj.dhdx.fun) - 复用已有 Casdoor / PostgreSQL / MinIO - 含部署检查清单和故障排查指南 - 所有敏感凭据已替换为占位符 Co-Authored-By: Claude <noreply@anthropic.com>
20 KiB
企迹 (qiji) 部署交接文档
1. 部署架构总览
用户浏览器 → qj.dhdx.fun → 远程服务器(175.178.19.237)
│
OpenResty (80/443)
│
┌──────────────┴──────────────┐
▼ ▼
前端容器 frp 隧道(18061)
qiji-frontend │
(18063→80) frps 服务端
(175.178.19.237)
│
frp 隧道
│
frpc 客户端
(本机服务器)
│
┌──────────┴──────────┐
▼ ▼
FastAPI 后端 Casdoor 认证
(8002端口) (18000端口, 共享)
│
▼
PostgreSQL (5432, 共享)
MinIO (10.10.10.13:17051)
一句话概括: 用户访问 http://qj.dhdx.fun,OpenResty 将前端 / 路由到前端容器(18063),将 /api/ 路由到 FRP 隧道(18061),frp 将流量转发到本机服务器的 qiji 后端(8002)。Casdoor 和 PostgreSQL 复用已有基础设施。
与 H3ConuMS2 的关系: qiji 和 H3ConuMS2 共享同一套基础设施(远程服务器、FRP 隧道网格、OpenResty、Casdoor、PostgreSQL),只是各自占用不同的端口和域名。互不干扰。
2. 服务器信息
远程服务器(部署 frps 服务端 + 前端容器 + OpenResty)
| 项目 | 值 |
|---|---|
| IP 地址 | 175.178.19.237 |
| SSH 端口 | 7072 |
| SSH 用户 | root |
| SSH 密码 | (见实际交接信息) |
| SSH 密钥 | 已安装(本机 → 远程),可直接免密登录 |
| 系统 | Ubuntu 22.04 |
| 管理面板 | 1Panel (端口 7071) |
| 配置 | 2 vCPU, 3.6GB RAM |
本机服务器(部署 frpc + 后端 + 数据库)
| 项目 | 值 |
|---|---|
| 角色 | 本地开发/服务机 |
| FRP 类型 | frpc 原生进程(非 Docker) |
| frpc 配置文件 | /etc/frp/frpc.toml |
| frpc 管理地址 | http://localhost:7000 (admin / your-frp-password) |
| 操作系统 | Ubuntu (带 Docker) |
3. FRP 配置(新增 qiji 隧道)
当前 FRP 状态
frpc 已在本机以原生进程运行(/usr/bin/frpc -c /etc/frp/frpc.toml),所有 7 条隧道正常运行。
现有隧道列表(勿动):
| 隧道名称 | 本地端口 | 远程端口 | 用途 |
|---|---|---|---|
| mysql | 3306 | 13306 | MySQL 远程访问 |
| postgresql | 5432 | 15432 | PostgreSQL 远程访问 |
| redis | 6379 | 16379 | Redis 远程访问 |
| gitea-web | 18003 | 18009 | Gitea |
| h3conums-v2-backend | 8000 | 18060 | H3ConuMS2 后端 |
| casdoor | 18000 | 18010 | Casdoor 认证 |
| gx-gp-notify | 18001 | 18011 | 通知服务 |
新增 qiji 后端隧道
编辑 /etc/frp/frpc.toml,在文件末尾添加:
[[proxies]]
name = "qiji-backend"
type = "tcp"
localIP = "127.0.0.1"
localPort = 8002
remotePort = 18061
transport.useEncryption = true
然后重启 frpc:
# frpc 是原生进程,需要 kill 后重启
# 查看当前进程
ps aux | grep frpc
# 重启(二选一)
# 方式一:如果通过 systemd 管理
sudo systemctl restart frpc
# 方式二:如果通过 entrypoint 脚本启动
sudo kill $(pgrep frpc) && sudo /usr/local/bin/entrypoint.d/20-frpc-start.sh &
重启后验证隧道状态:
curl -s http://localhost:7000/api/status -u admin:your-frp-password | python3 -m json.tool | grep -A5 qiji
应显示 "status": "running"。
⚠️ 注意: frpc 使用 host 网络模式(原生进程监听 0.0.0.0),所以
localIP = "127.0.0.1"是安全的——frpc 只连接本机回环地址。
frps 服务端(远程,无需修改)
frps 配置文件在远程 /opt/1panel/apps/frps/frps/data/frps.toml,无需修改。只要 frpc 请求的 remotePort (18061) 没被远程服务器其他服务占用,隧道即可自动建立。
4. 前端部署(远程服务器 Docker 容器)
4.1 构建前端镜像(本机操作)
前端使用 Vue 3 + Vite 构建,产物由 nginx 托管在 Docker 容器中。
项目中的关键文件:
frontend/Dockerfile— 多阶段构建(node:20-alpine 构建 → nginx:alpine 运行)frontend/nginx.conf— nginx 配置(SPA try_files +/api/反向代理)
构建前置: 确保 frontend/.env.production(或构建时环境变量)中 Casdoor 配置正确。Casdoor 回调地址应为 https://qj.dhdx.fun(或实际域名)。如果没有 .env.production 文件,Vite 会使用 .env 中的值。
# 1. 进入前端目录
cd /home/v6ole/pyproject/qiji/frontend
# 2. (可选)创建生产环境变量文件
cat > .env.production << 'EOF'
VITE_CASDOOR_ENDPOINT=https://casdoor.dhdx.fun
VITE_CASDOOR_CLIENT_ID=your-client-id
EOF
# 3. 构建 Docker 镜像
docker build -t qiji-frontend:latest .
# 4. 验证镜像
docker images | grep qiji-frontend
4.2 推送镜像到远程服务器
# 保存镜像并远程加载
docker save qiji-frontend:latest | ssh -p 7072 root@175.178.19.237 "docker load"
# 验证远程镜像已加载
ssh -p 7072 root@175.178.19.237 "docker images | grep qiji-frontend"
4.3 在远程服务器启动前端容器
ssh -p 7072 root@175.178.19.237 "
# 停止并删除旧容器(如果存在)
docker stop qiji-frontend 2>/dev/null
docker rm qiji-frontend 2>/dev/null
# 启动新容器
# 容器内部 nginx 监听 80,映射到宿主机 18063
docker run -d \
--name qiji-frontend \
--restart always \
-p 18063:80 \
qiji-frontend:latest
"
4.4 前端更新流程(日常使用)
cd /home/v6ole/pyproject/qiji/frontend
# 1. 修改代码后重新构建镜像
docker build -t qiji-frontend:latest .
# 2. 推送并重启(一键脚本)
docker save qiji-frontend:latest | ssh -p 7072 root@175.178.19.237 "docker load && docker stop qiji-frontend && docker rm qiji-frontend && docker run -d --name qiji-frontend --restart always -p 18063:80 qiji-frontend:latest"
# 3. 验证
curl -s http://175.178.19.237:18063/ | head -20
4.5 查看前端容器日志
ssh -p 7072 root@175.178.19.237 "docker logs --tail 50 qiji-frontend"
5. OpenResty / Nginx 配置(远程服务器)
5.1 新建站点配置
在远程服务器创建 /opt/1panel/apps/openresty/openresty/conf/conf.d/qj.dhdx.fun.conf:
server {
listen 80;
listen 443 ssl http2;
server_name qj.dhdx.fun;
index index.html;
# 基础代理头
proxy_set_header Host $host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Host $server_name;
proxy_set_header X-Real-IP $remote_addr;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $http_connection;
access_log /www/sites/qj.dhdx.fun/log/access.log main;
error_log /www/sites/qj.dhdx.fun/log/error.log;
# ACME 验证
location ^~ /.well-known/acme-challenge {
allow all;
root /usr/share/nginx/html;
}
# 安全头
add_header Strict-Transport-Security "max-age=31536000";
add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-Content-Type-Options "nosniff" always;
add_header X-XSS-Protection "1; mode=block" always;
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
# 前端(qiji-frontend 容器,端口 18063)
location / {
proxy_pass http://127.0.0.1:18063;
}
# 后端 API(frp 隧道 → 本机后端 8002)
location /api/ {
proxy_pass http://127.0.0.1:18061;
proxy_read_timeout 3600s;
proxy_send_timeout 3600s;
proxy_connect_timeout 60s;
}
# HTTP → HTTPS 重定向
if ($scheme = http) {
return 301 https://$host$request_uri;
}
# SSL 证书(通过 1Panel 申请 Let's Encrypt 或手动上传)
ssl_certificate /www/sites/qj.dhdx.fun/ssl/fullchain.pem;
ssl_certificate_key /www/sites/qj.dhdx.fun/ssl/privkey.pem;
ssl_protocols TLSv1.3 TLSv1.2 TLSv1.1 TLSv1;
ssl_ciphers ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:!aNULL:!eNULL:!EXPORT:!DSS:!DES:!RC4:!3DES:!MD5:!PSK:!KRB5:!SRP:!CAMELLIA:!SEED;
ssl_prefer_server_ciphers on;
ssl_session_cache shared:SSL:10m;
ssl_session_timeout 10m;
error_page 497 https://$host$request_uri;
proxy_set_header X-Forwarded-Proto https;
}
5.2 应用配置
# 方式一:通过 SSH 直接创建配置文件
cat << 'EOF' | ssh -p 7072 root@175.178.19.237 "cat > /opt/1panel/apps/openresty/openresty/conf/conf.d/qj.dhdx.fun.conf"
# (粘贴上面的完整 nginx 配置)
EOF
# 创建日志目录
ssh -p 7072 root@175.178.19.237 "mkdir -p /www/sites/qj.dhdx.fun/log /www/sites/qj.dhdx.fun/ssl"
# 重载 OpenResty 配置(在 1Panel 面板 → OpenResty → 重载配置)
# 或通过命令行
ssh -p 7072 root@175.178.19.237 "docker restart openresty"
⚠️ 注意: 建议通过 1Panel 面板(http://175.178.19.237:7071)管理 OpenResty 站点配置和 SSL 证书。直接修改配置文件后需要在面板中重载配置才能生效。
5.3 DNS 配置
在域名 DNS 管理后台添加 A 记录:
| 类型 | 主机记录 | 记录值 |
|---|---|---|
| A | qj | 175.178.19.237 |
域名根据实际情况选择,如
qj.dhdx.fun、qiji.dhdx.fun或weekly.dhdx.fun。
5.4 SSL 证书
方式一(推荐): 通过 1Panel 面板为 qj.dhdx.fun 申请 Let's Encrypt 免费证书,自动续期。
方式二: 自签名证书(仅测试用,浏览器会报警告)。
方式三: 暂时只使用 HTTP(注释掉 ssl 相关配置行)。
6. 后端服务(本机服务器)
6.1 启动方式
后端以原生进程运行(不容器化),与 H3ConuMS2 后端部署方式一致。
cd /home/v6ole/pyproject/qiji/backend
# 确保 .env 文件配置正确
cp .env.example .env
# 编辑 .env,填入实际值
# 启动后端(端口 8002,监听所有网卡)
uvicorn app.main:app --host 0.0.0.0 --port 8002 --reload
# 生产环境去掉 --reload:
# uvicorn app.main:app --host 0.0.0.0 --port 8002
6.2 环境变量关键配置
| 配置项 | 说明 | 当前值 |
|---|---|---|
DATABASE_URL |
PostgreSQL 连接 | postgresql+asyncpg://postgres:your-db-password@localhost:5432/qiji |
CASDOOR_ENDPOINT |
Casdoor 地址 | http://localhost:18000(本机直连) |
CASDOOR_CLIENT_ID |
Casdoor 应用 ID | 与前端一致 |
CASDOOR_CLIENT_SECRET |
Casdoor 应用 Secret | 保密 |
MINIO_ENDPOINT |
MinIO 地址 | 10.10.10.13:17051 |
SECRET_KEY |
JWT 密钥 | 生产环境务必修改 |
CORS_ORIGINS |
允许的前端域名 | 添加 https://qj.dhdx.fun |
6.3 生产环境 CORS 配置
确保 .env 中 CORS_ORIGINS 包含生产域名:
CORS_ORIGINS=["http://localhost:5173","http://localhost:3000","https://qj.dhdx.fun","http://qj.dhdx.fun"]
6.4 后端更新流程
cd /home/v6ole/pyproject/qiji/backend
# 1. 拉取最新代码
git pull
# 2. (如有新增依赖)
pip install -r requirements.txt
# 3. 重启后端进程
# 找到并杀掉旧进程
pkill -f "uvicorn app.main:app"
# 重新启动
uvicorn app.main:app --host 0.0.0.0 --port 8002 &
建议使用
systemd管理后端进程(见第 10 节)。
7. MinIO 文件存储(已共享)
7.1 当前状况
MinIO 部署在 10.10.10.13:17051(内网地址),qiji 使用 bucket qiji-photos。
问题: 前端通过预签名 URL 直传 MinIO。如果用户从公网(qj.dhdx.fun)访问,浏览器收到的预签名 URL 指向 10.10.10.13:17051,公网用户无法访问内网地址。
7.2 解决方案(三选一)
方案一(推荐):新增 MinIO FRP 隧道
在 /etc/frp/frpc.toml 中添加:
[[proxies]]
name = "minio"
type = "tcp"
localIP = "10.10.10.13"
localPort = 17051
remotePort = 18064
transport.useEncryption = true
然后在 MinIO 端配置公网地址(或通过环境变量 MINIO_ENDPOINT=175.178.19.237:18064),使生成的预签名 URL 指向公网。
方案二:后端代理 MinIO 流量
在 qiji 后端添加文件代理路由(如 /api/files/{bucket}/{object}),前端不直传 MinIO,而是通过后端中转。简单但增加后端带宽压力。
方案三:仅限内网使用
如果 qiji 用户都在内网(通过 VPN 或内网 IP 访问),当前配置无需修改。
当前建议: 如果公网用户需要上传/查看照片,选择方案一。如果只在内网使用,暂不处理。
8. Casdoor 认证(共享,已运行)
qiji 与 H3ConuMS2 共用同一个 Casdoor 实例。
| 项目 | 值 |
|---|---|
| 本机端口 | 18000 |
| FRP 远程端口 | 18010 |
| 外部访问 | https://casdoor.dhdx.fun(已有 Nginx 配置) |
| Casdoor 应用名 | qiji-weekly-report |
配置要点
- Casdoor 回调地址 必须添加
https://qj.dhdx.fun/callback(或实际域名) - 前端
.env.production中VITE_CASDOOR_ENDPOINT指向https://casdoor.dhdx.fun - 后端
.env中CASDOOR_ENDPOINT指向http://localhost:18000(本机直连,不经 FRP)
9. 数据库(共享,已运行)
qiji 使用已有的 PostgreSQL 实例,database 名为 qiji(与 H3ConuMS2 的 database 分开)。
| 项目 | 值 |
|---|---|
| 本机端口 | 5432 |
| FRP 远程端口 | 15432 |
| Database | qiji |
| 远程连接 | postgresql://postgres:your-db-password@175.178.19.237:15432/qiji |
10. 常见操作速查
查看 FRP 隧道状态
# 本机 frpc 管理 API
curl -s http://localhost:7000/api/status -u admin:your-frp-password | python3 -m json.tool
# 只看 qiji 隧道
curl -s http://localhost:7000/api/status -u admin:your-frp-password | python3 -c "import sys,json; d=json.load(sys.stdin); [print(p['name'],p['status'],p['local_addr'],'→',p['remote_addr']) for p in d.get('tcp',[])]"
# 远程 frps 管理 API
curl -s http://175.178.19.237:18001/api/status -u admin:your-frp-password | python3 -m json.tool
重启 FRP
# 本机 frpc(原生进程)
sudo kill $(pgrep frpc) && sudo /usr/local/bin/entrypoint.d/20-frpc-start.sh &
# 远程 frps(Docker 容器)
ssh -p 7072 root@175.178.19.237 "docker restart Frps"
查看前端容器状态
ssh -p 7072 root@175.178.19.237 "docker ps --filter name=qiji-frontend"
ssh -p 7072 root@175.178.19.237 "docker logs --tail 30 qiji-frontend"
查看后端运行状态
# 检查后端进程
ps aux | grep uvicorn | grep 8002
# 测试 API 健康
curl http://localhost:8002/api/health
通过 FRP 隧道测试后端
# 从远程服务器测试 FRP 隧道是否通
ssh -p 7072 root@175.178.19.237 "curl -s http://127.0.0.1:18061/api/health"
# 从公网测试(如果 OpenResty 已配置)
curl -s https://qj.dhdx.fun/api/health
通过 1Panel 管理远程服务器
- 浏览器访问
http://175.178.19.237:7071 - 可视化管理:OpenResty 站点配置、SSL 证书申请、Docker 容器启停
11. 端口总览
远程服务器 (175.178.19.237) 端口
| 端口 | 用途 | 所属项目 |
|---|---|---|
| 22 | SSH(通过 7072 映射) | 基础设施 |
| 7071 | 1Panel 管理面板 | 基础设施 |
| 7072 | SSH(外部访问端口) | 基础设施 |
| 80 | HTTP(OpenResty) | 基础设施 |
| 443 | HTTPS(OpenResty) | 基础设施 |
| 18000 | frps 控制端口 | 基础设施 |
| 18001 | frps 管理面板 | 基础设施 |
| 18002 | Halo 博客 | 其他 |
| 18003 | Speedtest | 其他 |
| 18004-18005 | OpenList | 其他 |
| 18006 | IP 监测 | 其他 |
| 18009 | ← Gitea (frp) | 共享 |
| 18010 | ← Casdoor (frp) | 共享 |
| 18011 | ← 通知服务 (frp) | 共享 |
| 13306 | ← MySQL (frp) | 共享 |
| 15432 | ← PostgreSQL (frp) | 共享 |
| 16379 | ← Redis (frp) | 共享 |
| 18020 | Sun-Panel | 其他 |
| 18051 | Bitwarden | 其他 |
| 18052 | CookieCloud | 其他 |
| 18054 | Bark Server | 其他 |
| 18055 | WeChat API | 其他 |
| 18060 | ← H3ConuMS2 后端 (frp) | H3ConuMS2 |
| 18061 | ← qiji 后端 (frp) | qiji (新增) |
| 18062 | H3ConuMS2 前端容器 | H3ConuMS2 |
| 18063 | qiji 前端容器 | qiji (新增) |
| qiji (待定) |
本机服务器端口
| 端口 | 用途 | 所属项目 |
|---|---|---|
| 3306 | MySQL | 共享 |
| 5432 | PostgreSQL | 共享 |
| 6379 | Redis | 共享 |
| 7000 | frpc 管理面板 | 基础设施 |
| 8000 | H3ConuMS2 后端 | H3ConuMS2 |
| 8002 | qiji 后端 | qiji |
| 18000 | Casdoor | 共享 |
| 18001 | 通知服务 | 共享 |
| 18003 | Gitea | 共享 |
12. 相关项目路径
| 路径 | 说明 | 所在服务器 |
|---|---|---|
/home/v6ole/pyproject/qiji/ |
qiji 项目源码 | 本机 |
/home/v6ole/pyproject/qiji/backend/ |
后端源码 | 本机 |
/home/v6ole/pyproject/qiji/frontend/ |
前端源码 | 本机 |
/home/v6ole/pyproject/qiji/frontend/Dockerfile |
前端镜像构建文件 | 本机 |
/home/v6ole/pyproject/qiji/frontend/nginx.conf |
前端 nginx 配置 | 本机 |
/home/v6ole/pyproject/qiji/backend/.env |
后端环境变量(不提交 git) | 本机 |
/etc/frp/frpc.toml |
frpc 配置(含 qiji 隧道) | 本机 |
/opt/1panel/apps/frps/frps/data/frps.toml |
frps 配置 | 远程 |
/opt/1panel/apps/openresty/openresty/conf/conf.d/qj.dhdx.fun.conf |
qiji 站点 Nginx 配置 | 远程 |
/opt/1panel/apps/openresty/openresty/www/sites/qj.dhdx.fun/ssl/ |
qiji SSL 证书 | 远程 |
/home/v6ole/pyproject/qiji/qiji部署交接文档.md |
本文档 | 本机 |
13. 部署检查清单
初次部署时,按以下顺序逐项确认:
- 后端:
.env配置正确,uvicorn启动在 8002 端口,curl localhost:8002/api/health正常 - FRP 隧道:
/etc/frp/frpc.toml添加qiji-backend隧道,重启 frpc 后状态为running - FRP 验证:从远程
curl 127.0.0.1:18061/api/health能访问后端 - 前端镜像:
docker build -t qiji-frontend:latest .构建成功 - 前端部署:推送镜像到远程,容器运行在 18063 端口,
curl 175.178.19.237:18063/返回 HTML - OpenResty:创建
qj.dhdx.fun.conf,重载配置 - DNS:添加 A 记录
qj → 175.178.19.237 - SSL:通过 1Panel 申请 Let's Encrypt 证书(或自签名测试)
- Casdoor:确认回调地址添加
https://qj.dhdx.fun/callback - 端到端:浏览器访问
https://qj.dhdx.fun,登录→填报→查看周报全流程通过 - MinIO:确认照片上传/查看功能正常(如果公网用户需要,配置 MinIO 隧道)
14. 故障排查
| 现象 | 检查项 |
|---|---|
| 页面打不开 | 远程前端容器是否运行:docker ps | grep qiji-frontend |
| 页面打开但 API 报错 | FRP 隧道是否正常:curl localhost:7000/api/status -u admin:your-frp-password |
| API 返回 500 | 后端进程是否存活:ps aux | grep uvicorn | grep 8002 |
| 登录回调失败 | Casdoor 回调地址是否包含生产域名;前端 .env.production 中 Casdoor 地址是否正确 |
| 照片上传失败 | MinIO 是否可达;预签名 URL 域名是否为公网可访问地址 |
| FRP 隧道状态不是 running | frpc 日志:journalctl -u frpc -f 或 dmesg | grep frpc |
文档创建日期:2026年6月26日