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

603 lines
20 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 企迹 (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`,在文件末尾添加:
```toml
[[proxies]]
name = "qiji-backend"
type = "tcp"
localIP = "127.0.0.1"
localPort = 8002
remotePort = 18061
transport.useEncryption = true
```
然后重启 frpc
```bash
# 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 &
```
重启后验证隧道状态:
```bash
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` 中的值。
```bash
# 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 推送镜像到远程服务器
```bash
# 保存镜像并远程加载
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 在远程服务器启动前端容器
```bash
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 前端更新流程(日常使用)
```bash
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 查看前端容器日志
```bash
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`
```nginx
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 应用配置
```bash
# 方式一:通过 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 后端部署方式一致。
```bash
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` 包含生产域名:
```env
CORS_ORIGINS=["http://localhost:5173","http://localhost:3000","https://qj.dhdx.fun","http://qj.dhdx.fun"]
```
### 6.4 后端更新流程
```bash
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` 中添加:
```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.production``VITE_CASDOOR_ENDPOINT` 指向 `https://casdoor.dhdx.fun`
3. 后端 `.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 隧道状态
```bash
# 本机 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
```bash
# 本机 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"
```
### 查看前端容器状态
```bash
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"
```
### 查看后端运行状态
```bash
# 检查后端进程
ps aux | grep uvicorn | grep 8002
# 测试 API 健康
curl http://localhost:8002/api/health
```
### 通过 FRP 隧道测试后端
```bash
# 从远程服务器测试 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 -f``dmesg \| grep frpc` |
> 文档创建日期:2026年6月26日