服务器 + 内网穿透部署(Orange Pi)
将 Vistack 后端全套部署在 Orange Pi(ARM64 单板) 上,通过公网服务器 + frp 隧道映射对外;前端继续托管在 Cloudflare Pages。适合「前端上 CDN、后端在低成本 ARM 设备」的场景。
宿主机已有 Traefik / Dokploy?
若 Orange Pi 上已有其他反向代理占用 80/443(如 Dokploy 自带的 dokploy-traefik),有两种处理方式:
- 改端口保留自有 Traefik(推荐):Vistack 的 Traefik 继续负责内部路径分流(auth / api / 媒体 / 页面),只把宿主端口从 80 改到其他端口(如
TRAEFIK_HTTP_PORT=8082); - 跳过自有 Traefik:使用
compose.server.ymloverride 不起 traefik,由公网服务器 Caddy 按路径分流(见文末「备选」)。
适用架构(推荐:保留 Traefik + 改端口)
Cloudflare Pages(cvistack.pages.dev,前端)
│ VITE_API_BASE=https://api.你的域名/api/v1
▼
公网服务器(VPS:域名解析 + HTTPS 终止 + 隧道入口)
├─ Caddy ── 自动申请 Let's Encrypt 证书,统一转发(无需分路径)
└─ frps(隧道服务端,端口 7000;本地 18080 供 Caddy 反代)
▲ frp 隧道(Orange Pi 主动连出,仅映射 1 个端口)
▼
Orange Pi(ARM64,内网)—— Docker Compose 全套后端
└─ Traefik(宿主端口 8082)统一分流:
├─ /api/v1/auth|user → auth:8081
├─ /api/v1/* → api:8080
├─ /vistack/*(媒体) → minio:9000
└─ 其余(页面) → api:8080(内置 web 产物)前置条件
| 项 | 要求 |
|---|---|
| 域名 | 自己的域名,解析 api.你的域名 → 公网服务器 IP |
| 公网服务器 | VPS,开放 80 / 443 / 7000(frp 控制端口) |
| Orange Pi | ARM64(uname -m 应为 aarch64),建议 ≥ 8GB 内存 |
| 端口确认 | 8082 / 8083 / 9000 / 5432 / 6379 / 9092 / 2379 未被占用(ss -tlnp 检查) |
镜像 ARM64 兼容性
| 镜像 | ARM64 | 说明 |
|---|---|---|
| golang / node / alpine / postgres / redis / traefik / minio | ✅ | 官方 multi-arch,Docker 自动拉取 arm64 |
quay.io/coreos/etcd:v3.5.16 | ✅ | 官方提供 arm64 |
confluentinc/cp-kafka:7.6.1 | ⚠️ | Confluent 7.4+ 提供 arm64,需实测;不兼容则改 compose 镜像为 apache/kafka |
第 1 步:Orange Pi 准备
uname -m # 应为 aarch64
curl -fsSL https://get.docker.com | sh
sudo systemctl enable --now docker第 2 步:Orange Pi 部署 Vistack
git clone https://github.com/binhy/vistack.git && cd vistack
# 环境变量:改所有默认密码、MinIO 密钥、JWT_SECRET,并给 Traefik 换端口
cp .env.example .env.local
vim .env.local.env.local 追加(避开 Dokploy 占用的 80/443):
TRAEFIK_HTTP_PORT=8082 # 统一入口(Vistack Traefik 宿主端口)
TRAEFIK_DASHBOARD_PORT=8083 # Traefik 仪表盘修改 conf/app.docker.toml(两处必改):
[cors]
allow_origins = ["https://cvistack.pages.dev"] # ① 前端 Pages 域名
[minio]
public_endpoint = "https://api.你的域名" # ② 统一 HTTPS 入口(经 Caddy → frp → Traefik 媒体路由)起栈(含 traefik,端口为 8082/8083):
docker compose up -d --build验证本机入口:
curl http://localhost:8082/health # {"db":"ok","minio":"ok",...}Auth 服务私钥生产环境必须固定:启动时注入
VISTACK_AUTH_RSA_PRIVATE_KEY_FILE(PEM 文件),否则重启后已签发 token 全部失效。
第 3 步:frp 隧道(只需映射 1 个端口)
公网服务器 —— frps(/etc/frp/frps.toml):
bindPort = 7000
auth.method = "token"
auth.token = "换成强随机token"sudo frps -c /etc/frp/frps.tomlOrange Pi —— frpc(/etc/frp/frpc.toml):
serverAddr = "公网服务器IP"
serverPort = 7000
auth.method = "token"
auth.token = "换成强随机token"
[[proxies]]
name = "vistack"
type = "tcp"
localIP = "127.0.0.1"
localPort = 8082 # Vistack Traefik(内部已按路径分流)
remotePort = 18080⚠️ frp 的
remotePort:18080监听在公网服务器所有接口,请用防火墙仅放行本机访问(如ufw allow from 127.0.0.1 to any port 18080),避免绕过 Caddy 直连。
第 4 步:公网服务器 Caddy(自动 HTTPS + 统一转发)
api.你的域名 {
reverse_proxy 127.0.0.1:18080 # frp → Orange Pi Traefik,路径分流在 Traefik 完成
}- 域名解析:
api.你的域名A 记录 → 公网服务器 IP; - Caddy 首次启动自动申请 Let's Encrypt 证书并续期;
- 媒体
/vistack/*也走同一入口:Caddy → frp → Traefik media-router → MinIO,签名 Host 全程为api.你的域名,与public_endpoint一致。
第 5 步:前端联动
Cloudflare Pages 项目 → Settings → Environment variables(Production):
VITE_API_BASE = https://api.你的域名/api/v1保存后重新部署(环境变量仅构建时生效)。验证:
curl https://api.你的域名/health
curl https://api.你的域名/vistack/xxxx # 应返回 MinIO 响应(403/200),而非页面备选:跳过自有 Traefik + Caddy 分路径
不想在 Orange Pi 上跑 Vistack 的 Traefik 时(零额外容器),使用 compose.server.yml override 起栈:
docker compose -f compose.yml -f compose.server.yml up -d --build
# traefik 被隔离到 local-only profile,不启动;api(8080)/auth(8081)/minio(9000) 直连frpc 映射 3 个端口:
[[proxies]] # vistack-api localPort = 8080 remotePort = 18080
[[proxies]] # vistack-auth localPort = 8081 remotePort = 18081
[[proxies]] # vistack-minio localPort = 9000 remotePort = 19000Caddy 按路径分流(auth 规则必须在 /api/* 之前):
api.你的域名 {
handle /api/v1/auth/* { reverse_proxy 127.0.0.1:18081 }
handle /api/v1/user/* { reverse_proxy 127.0.0.1:18081 }
handle /api/* { reverse_proxy 127.0.0.1:18080 }
handle /vistack/* { reverse_proxy 127.0.0.1:19000 }
handle { reverse_proxy 127.0.0.1:18080 }
}性能调优(Orange Pi)
- 转码:FFmpeg 无 GPU 加速,DASH 多档位转码较慢。建议接受低档位输出,或仅按需触发(
transcoder副本保持 1); - 内存:全套 + Kafka(KRaft)建议 ≥ 8GB;内存吃紧可给 Kafka 容器加
KAFKA_HEAP_OPTS=-Xmx512m; - 并发:
[kafka].concurrency保持默认 4 即可,单板瓶颈在 CPU 不在消费者数; - 清理:定期
docker system prune -f清理旧镜像与日志。
备选隧道:Tailscale 替代 frp
两台机器都装 Tailscale(组网后互访内网 IP),公网服务器 Caddy 直接反代 Orange Pi 的 Tailscale IP:
api.你的域名 {
reverse_proxy http://100.x.x.x:8082 # Orange Pi 的 Tailscale IP + Traefik 端口
}免 frp 端口配置、隧道加密、断线自动重连,个人场景更省心。
安全清单
- [ ]
.env.local替换全部默认密码 /JWT_SECRET; - [ ] Auth RSA 私钥固定注入(
VISTACK_AUTH_RSA_PRIVATE_KEY_FILE); - [ ] frp 设置强随机
auth.token; - [ ] 公网服务器防火墙仅放行 80 / 443 / 7000(18080 等仅本机);
- [ ] 宿主机 Dokploy 等既有服务保持原样,不相互影响;
- [ ] Traefik 仪表盘(8083)仅内网访问或关闭;
- [ ] Orange Pi 关闭不必要的系统服务,保持 Docker 网络隔离。
故障排查
| 现象 | 排查 |
|---|---|
https://api.你的域名 502/522 | frpc 是否连接成功(frpc 日志)→ curl localhost:8082/health(Orange Pi 本机)→ Caddy 日志 → 防火墙是否放行 frp 端口 |
| 页面能开但接口报错 | VITE_API_BASE 是否已配且重新部署;CORS 是否含 Pages 域名 |
| 80 端口仍冲突 | 确认 .env.local 的 TRAEFIK_HTTP_PORT 已生效(docker compose config 查看 published 端口) |
| 视频播放失败 | 是否 mixed content(页面 HTTPS 加载 HTTP 资源);public_endpoint 是否为 https://api.你的域名;curl https://api.你的域名/vistack/xxx 应返回 MinIO 响应 |
| 登录后 token 失效 | Auth 私钥未固定(重启后临时密钥变更)——生产必须注入固定私钥 |
| 起栈时报端口占用 | 宿主机已有服务占用 8082/9000 等——先 ss -tlnp 排查,再通过 .env.local 的 *_PORT 调整 |
