feat(deploy): 单节点全套部署脚本 deploy/single-node(档 3 真机准备)

一台全新 Ubuntu/Debian VPS 上一条命令起整套 pangolin:MySQL + Redis(docker)
+ 控制面(HTTP + gRPC agent 服务)+ 节点 agent + sing-box 数据面,客户端连该
VPS 公网 IP 真实出网。

- deploy.sh:幂等编排——构建二进制 → docker MySQL/Redis → 生成 JWT/webhook/
  derive 密钥 + Node CA + CA 签发的 gRPC server 证书(SAN localhost,匹配 agent
  enroll 后的 mTLS 校验)+ REALITY 密钥对(持久化跨重部署)→ migrate + seed 节点
  → 起控制面(走完整 gRPC 分支)→ nodectl bootstrap-token → 起 agent + sing-box。
- systemd/{pangolin-server,pangolin-agent,sing-box}.service、docker-compose.yml
  (MySQL/Redis 仅 loopback)、seed-node.sql.tmpl。
- README.md:架构图 + 部署/验证步骤 + 客户端接线(API URL / PANGOLIN_SINGBOX_BIN
  / sudoers / 真节点 uuid,客户端代码无改动)+ 排查指引。

本机验证:shellcheck + bash -n 通过;openssl CA ↔ mtls.NewCA + gRPC 证书链互通
已单测实测通过。VPS 端到端(出口 IP == VPS IP)待真机到位后执行。

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
wangjia
2026-06-17 00:32:47 +08:00
parent 0b6173d9cd
commit 912a495e91
9 changed files with 672 additions and 52 deletions
+118
View File
@@ -0,0 +1,118 @@
# 单节点全套部署(档 3 真实连接)
一台全新 Ubuntu/Debian VPS 上跑 **整套** pangolinMySQL + Redis + 控制面(HTTP
API + gRPC agent 服务)+ 节点 agent + sing-box 数据面。客户端连这台 VPS 的公网
IP,真实出网(出口 IP = VPS IP)。
> 不碰 EC2 生产、不用本机出网。这套是独立的端到端验证环境。
## 架构(一台机器内)
```
客户端 (mac/手机)
│ ① POST /v1/auth/code → 注册/登录 (JWT)
│ ② GET /v1/nodes → 看到本节点 (status=up)
│ ③ POST /v1/nodes/{uuid}/connect → 控制面下发 sing-box CLIENT 配置 (REALITY)
┌───────────────────────────── VPS ──────────────────────────────┐
│ pangolin-server HTTP :8080 + gRPC :9443 │
│ │ connect → Hub.Push(Upsert dp_uuid) │
│ ▼ (gRPC Subscribe 命令流, mTLS) │
│ pangolin-agent ── 渲染 sing-box SERVER 配置 → systemctl restart │
│ ▼ │
│ sing-box VLESS+REALITY :11443 (+ Hysteria2 :443) → 直连出网 │
│ MySQL :3306 (loopback) Redis :6379 (loopback) [docker] │
└─────────────────────────────────────────────────────────────────┘
```
凭证天然自洽:connect 给客户端的 REALITY `uuid = dp_uuid`、Hy2 `password =
DeriveHy2Password(dp_uuid, NODE_DERIVE_KEY)`agent 渲染 server 配置时用 **同一**
`NODE_DERIVE_KEY` 和 dp_uuid,两端一致。
## 部署(VPS 上一条命令)
前置:root、`docker`(含 compose v2)、`openssl``go``sing-box`(脚本会尝试自动装)。
```bash
# 1. 把仓库 checkout 到 VPSgit clone 或 scp
# 2. 一键起全套(VPS_IP 不传则自动探测公网 IP)
sudo VPS_IP=<你的公网IP> bash deploy/single-node/deploy.sh
```
幂等:重跑只刷新二进制 + 配置,**不**轮换 REALITY 密钥 / 数据库(已发的客户端配置不失效)。
### 真发邮件(可选)
不传 SMTP_* 时,注册验证码打到 `journalctl -u pangolin-server`。要真发:
```bash
sudo VPS_IP=<IP> \
SMTP_HOST=smtp.resend.com SMTP_PORT=587 SMTP_USERNAME=resend \
SMTP_PASSWORD=<resend-api-key> SMTP_FROM='穿山甲 <noreply@mail.51yanmei.com>' \
bash deploy/single-node/deploy.sh
```
## 验证(VPS 上)
```bash
journalctl -u pangolin-agent -n 50 --no-pager # enrolled successfully + Register
systemctl status sing-box --no-pager # active (running)
grep -A3 reality-in /etc/sing-box/config.json # listen_port 11443 + 你的 short_id
ss -tlnp | grep 11443 # sing-box 在监听
```
agent 第一次 enroll 后会渲染 `/etc/sing-box/config.json``systemctl restart
sing-box`。在 connect 之前 server 配置里 reality-in 的 users 可能为空(没有已连用户),
客户端 connect 后 Hub 推 Upsertagent 会把 `dp_uuid` 加进 users 并重启 sing-box。
## 客户端接线(本机,无需改代码)
客户端代码已就绪(connect 透传、桌面 sing-box 子进程 + sudo TUN)。只需指向本 VPS
1. **API 地址**
```bash
flutter run -d macos --dart-define=PANGOLIN_API_URL=http://<VPS_IP>:8080
```
2. **本机 sing-box 二进制**:装 `brew install sing-box`,并让 app 能找到它
(环境变量 `PANGOLIN_SINGBOX_BIN=/opt/homebrew/bin/sing-box``--dart-define`
传不进进程 env,用真实环境变量或放到 kernel_process 查找路径)。
3. **macOS TUN 提权**:给 sing-box 免密 sudoPoC 用,正式走 SMJobBless
```
# /etc/sudoers.d/pangolin-singbox
<你的用户名> ALL=(root) NOPASSWD: /opt/homebrew/bin/sing-box
```
4. **真节点 uuid**seed 的节点 uuid 非空,`connection_provider` 会自动走真实分支
`ConnectApi.fetchConfig` → `bridge.start`),无需改代码。
## 验证出口(端到端)
客户端连上后,在本机:
```bash
curl https://api.ipify.org # 出口 IP 应等于 <VPS_IP>
```
出口 IP == VPS IP 即真实远端代理打通。
## 文件 / 路径
| 路径 | 内容 |
|------|------|
| `/etc/pangolin/server.env` | 控制面环境变量(DB/JWT/CA/gRPC/derive key |
| `/etc/pangolin/{ca,grpc}.{key,crt}` | Node CA + gRPC server 证书(CA 签发,SAN localhost |
| `/etc/pangolin/reality.env` | REALITY 密钥对 + short_id(跨重部署稳定) |
| `/etc/pangolin/db.env` | MySQL root 密码 |
| `/etc/pangolin-agent/agent.env` | agent 环境变量(含一次性 bootstrap token |
| `/etc/pangolin-agent/{node.key,node.crt,ca.crt}` | agent enroll 后持久化的 mTLS 身份 |
| `/etc/sing-box/config.json` | agent 渲染的 sing-box SERVER 配置 |
| `/usr/local/bin/{pangolin-server,pangolin-agent,nodectl,pangolin-migrate}` | 二进制 |
## 排查
- **agent 起不来 / enroll 失败**`journalctl -u pangolin-agent`。检查
`pangolin-server` 是否已起 gRPC(日志应有 gRPC,不是 "gRPC not configured")——
需要 server.env 里 `GRPC_ADDR` + `CA_*` + `GRPC_*` 全配(deploy.sh 已配好)。
- **sing-box 反复重启**:首次 enroll 前 `/etc/sing-box/config.json` 不存在,属正常;
enroll 后应自愈。仍失败看 `journalctl -u sing-box`,多半是 REALITY 配置/端口占用。
- **客户端 connect 返回 4xx**:确认 JWT 有效(先登录)、节点 `status='up'`。
- **出口 IP 不是 VPS**:检查客户端是否真的起了 TUNsudo 提权)、route final=auto。