Files
qiji/qiji部署交接文档.md
T
v6ole 4ae8c69193 docs: qiji 部署交接文档 — FRP + OpenResty + Docker 部署方案
基于 H3ConuMS2 部署模式,适配 qiji 项目:
- 新增 qiji-backend FRP 隧道 (8002→18061)
- 前端容器化部署 (qiji-frontend:18063)
- OpenResty 站点配置 (qj.dhdx.fun)
- 复用已有 Casdoor / PostgreSQL / MinIO
- 含部署检查清单和故障排查指南
- 所有敏感凭据已替换为占位符

Co-Authored-By: Claude <noreply@anthropic.com>
2026-06-26 08:27:31 +08:00

20 KiB
Raw Blame History

企迹 (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.funOpenResty 将前端 / 路由到前端容器(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.funqiji.dhdx.funweekly.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 配置

确保 .envCORS_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

配置要点

  1. Casdoor 回调地址 必须添加 https://qj.dhdx.fun/callback(或实际域名)
  2. 前端 .env.productionVITE_CASDOOR_ENDPOINT 指向 https://casdoor.dhdx.fun
  3. 后端 .envCASDOOR_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 &

# 远程 frpsDocker 容器)
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 HTTPOpenResty 基础设施
443 HTTPSOpenResty 基础设施
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 (新增)
18064 MinIO (frp) 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 -fdmesg | grep frpc

文档创建日期:2026年6月26日