切换主题
🌐 V9OS 分布式部署全攻略 —— 从零搭建多节点集群
适用版本:V9OS 企业版
受众:运维、后端开发、架构师、想体验分布式的小白
预计阅读:15 分钟 | 动手时间:30~60 分钟
📖 为什么要分布式?
| 痛点 | 单体模式 | 分布式模式 |
|---|---|---|
| 高可用 | 单点故障 = 全站挂 | 任意节点挂了,其它节点顶上 |
| 弹性伸缩 | 只能垂直加配 | 横向加节点,秒级扩容 |
| 多地域 | 只能部在一台机器 | 跨机房、跨云、边缘节点随意组网 |
| 插件隔离 | 所有插件抢同一进程资源 | 插件按亲和性调度到不同节点 |
💡 核心思想:V9OS 分布式 = 配置中心 + 共享存储 (Redis/MySQL/RocketMQ) + 无状态节点。节点本身不存状态,随时可增删。
🏗️ 架构一览(ASCII 版)
text
┌─────────────────────┐
│ Nginx / LB │ ← 统一入口,四/七层负载均衡
└──────────┬──────────┘
│
┌──────────────────────┼──────────────────────┐
│ │ │
┌───────▼───────┐ ┌───────▼───────┐ ┌───────▼───────┐
│ Node-1 │ │ Node-2 │ │ Node-N │
│ (v9os 进程) │ │ (v9os 进程) │ │ (v9os 进程) │
│ 127.0.0.1:9099 192.168.1.10:9099 10.0.0.5:9099
└───────┬───────┘ └───────┬───────┘ └───────┬───────┘
│ │ │
└────────────────────┴────────────────────┘
│
┌─────────────────────┼─────────────────────┐
▼ ▼ ▼
┌───────────┐ ┌───────────┐ ┌───────────┐
│ Redis │ │ MySQL/ │ │ RocketMQ │
│ (缓存/锁)│ │ PostgreSQL│ │ (消息队列)│
└───────────┘ └───────────┘ └───────────┘
▲ ▲ ▲
└─────────────────────┼─────────────────────┘
│
┌──────────▼──────────┐
│ 配置中心 │ ← 统一下发 init.json
│ (HTTP API + 文件) │
└─────────────────────┘1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
✅ 前置清单
| 项 | 说明 | 最低建议 |
|---|---|---|
| 操作系统 | Linux / Windows / macOS | Linux |
| V9OS 版本 | 企业版二进制 | v9os 可执行文件 |
| 配置中心 | 任意 HTTP 可写存储 | PHP / Go / Node / Nginx+Lua |
| Redis | 缓存 + 分布式锁 + 订阅缓存 | 单机/哨兵/集群均可 |
| 数据库 | 元数据 + 业务数据 | MySQL 8.0 / PostgreSQL 15+ |
| 消息队列 | 插件事件/定时任务/WebSocket 推送 | RocketMQ 5.x / Redis Stream |
| 负载均衡 | 统一对外入口 | Nginx / HAProxy / 云厂商 SLB |
| 节点互通 | 节点间需直连或同一 VPC | 端口 9099 (可配) 互通 |
💡 小白提示:没现成 Redis/MySQL/RocketMQ?本地用 Docker 起一套:
bashdocker compose up -d redis mysql rocketmq1
🚀 Step 1:生成基础 init.json
1.1 首次启动(单体模式)
bash
# Windows
v9os.exe
# Linux/macOS
chmod +x ./v9os && ./v9os1
2
3
4
5
2
3
4
5
启动成功后 Ctrl+C 关闭,当前目录会生成 init.json 和 config/ 目录。
1.2 删除本地配置目录(关键!)
bash
rm -rf config # Windows: rmdir /s config1
为什么删?因为分布式模式下 所有配置来自配置中心,本地
config/会被忽略,留着反而干扰。
1.3 此时的 init.json 长这样
json
{
"remotes": null, // 👈 稍后填:配置中心「读」地址列表
"remote_save": "", // 👈 稍后填:配置中心「写」地址
"remote_auth": "", // 👈 稍后填:写入密钥
"local": true, // 👈 改为 false 启用分布式
"version": "1.0.000",
"machine_id": "bf6ff42b463e43daafa0b582ce762e76", // 每节点必须唯一
"port": 9099, // 节点监听端口
"load_ip": "", // 对外暴露 IP(留空自动探测)
"wait_network": false // 启动前等网络就绪
}1
2
3
4
5
6
7
8
9
10
11
2
3
4
5
6
7
8
9
10
11
| 字段 | 作用 | 分布式必改? |
|---|---|---|
remotes | 配置中心读取 URL 数组,支持多个做容灾 | ✅ 是 |
remote_save | 配置中心写入 URL | ✅ 是 |
remote_auth | 写入鉴权 token | ✅ 是 |
local | true=单体,false=分布式 | ✅ 是 |
machine_id | 节点唯一标识,集群内不可重复 | ✅ 是 |
port | 节点 HTTP 端口 | 可选 |
load_ip | 反向代理回调用的 IP | 建议填 |
wait_network | 启动阻塞等网络 | 容器环境建议 true |
🛠️ Step 2:搭建配置中心(最简单的 PHP 版)
原理:两个接口
GET /get返回 JSON,POST /save写入 JSON。
你可以用任何语言实现,甚至Nginx + 静态文件 + Lua。
2.1 目录结构
/var/www/config-center/
├── config.php # 入口
├── v9osconfig.php # 实际存储文件(首次自动生成)
└── .htaccess # 可选:限制 IP 访问1
2
3
4
2
3
4
2.2 config.php 完整代码(严格使用此版本)
php
<?php
$file = 'v9osconfig.php';
$pwd = "123456";
$action = substr(strrchr($_SERVER['REQUEST_URI'], '/'), 1);
if ($action == 'get') {
echo file_exists($file) ? substr(file_get_contents($file), 6) : '{}';
} elseif ($action == 'save' && $_SERVER['REQUEST_METHOD'] == 'POST') {
$headers = getallheaders();
if (isset($headers['Authorization']) && $headers['Authorization'] == $pwd) {
file_put_contents($file, "<?php ".file_get_contents('php://input'));
echo '1';
}else{
echo '0';
}
}1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
2
3
4
5
6
7
8
9
10
11
12
13
14
15
⚠️ 注意:
$pwd = "123456"请务必改成强随机串,生产环境建议配合 Nginx 限制内网访问。
2.3 验证配置中心
bash
# 读取(应返回 {})
curl http://192.168.0.100/config.php/get
# 写入测试
curl -X POST -H "Authorization: 123456" \
-d '{"test":123}' \
http://192.168.0.100/config.php/save
# 返回 1 表示成功1
2
3
4
5
6
7
8
2
3
4
5
6
7
8
⚙️ Step 3:先启动节点生成配置,再修改上传(核心流程)
💡 正确逻辑:先用单节点跑起来 → Web 端改配置 → 配置自动同步到配置中心 → 其它节点直接用。
3.1 修改首节点 init.json 仅改必要字段
json
{
"remotes": ["http://192.168.0.100/config.php/get"],
"remote_save": "http://192.168.0.100/config.php/save",
"remote_auth": "123456",
"local": false,
"version": "1.0.000",
"machine_id": "v9os-node-01", // 唯一
"port": 9099,
"load_ip": "192.168.1.10",
"wait_network": true
}1
2
3
4
5
6
7
8
9
10
11
2
3
4
5
6
7
8
9
10
11
3.2 启动首节点(前台跑,看日志)
bash
./v9os1
日志应出现:
[HTTP服务9099]已初始化
distributed runtime started enabled=true localMachineID=v9os-node-01 localIp=192.168.1.101
2
2
3.3 登录 Web 端,按顺序改三大件
| 顺序 | 菜单路径 | 关键动作 |
|---|---|---|
| 1 | 系统设置 → 缓存配置 | driver: redis、填 Redis 地址/密码/模式 |
| 2 | 系统设置 → 数据库配置 | driver: mysql、填 DSN、开启软删除/缓存 |
| 3 | 系统设置 → 消息队列 | driver: rocket、填 NameServer/Topic/密钥 |
每改一项点「保存」,V9OS 会自动把完整配置
POST到配置中心/save。
3.4 验证配置中心已落盘
bash
curl http://192.168.0.100/config.php/get | jq .1
应看到完整 JSON,包含 cachebase.driver: "redis"、database.driver: "mysql"、mqbase.driver: "rocket"、distributed.enabled: true。
3.5 关键密钥统一(全集群必须一致)
| 字段 | 来源 | 建议生成方式 |
|---|---|---|
auth.secret | 系统自动生成 | 首节点生成后复制给其它节点 |
server.password_key | 系统自动生成 | 同上 |
server.communication_key | 系统自动生成 | 同上 |
server.system_id | 系统自动生成 | 同上 |
distributed.distributed_id | 系统自动生成 | 同上 |
这五个值在首节点首次启动时自动生成并写入配置中心,后续节点直接读取,无需手动填。
🖥️ Step 4:部署其它节点(复制 init.json + 改 machine_id)
4.1 准备节点文件
每台机器放入:
v9os # 企业版二进制
init.json # 从首节点拷贝,仅改 machine_id 和 load_ip1
2
2
4.2 修改 init.json(每节点只改这两行)
json
{
"remotes": ["http://192.168.0.100/config.php/get"],
"remote_save": "http://192.168.0.100/config.php/save",
"remote_auth": "123456",
"local": false,
"version": "1.0.000",
"machine_id": "v9os-node-02", // 🔑 每节点唯一!
"port": 9099,
"load_ip": "192.168.1.11", // 本节点对外 IP
"wait_network": true
}1
2
3
4
5
6
7
8
9
10
11
2
3
4
5
6
7
8
9
10
11
| 节点 | machine_id 示例 | load_ip 示例 |
|---|---|---|
| Node-1 | v9os-node-01 | 192.168.1.10 |
| Node-2 | v9os-node-02 | 192.168.1.11 |
| Node-3 | v9os-edge-01 | 10.0.5.20 |
⚠️ machine_id 唯一性是分布式调度、插件亲和性、WebSocket 路由的基础,绝不重复!
4.3 启动节点
bash
# 前台测试
./v9os1
2
2
4.4 验证节点加入
bash
# 健康检查
curl http://192.168.1.11:9099/health
# {"code":0,"version":"1.0.000"}
# 看日志应出现:
# distributed runtime started enabled=true localMachineID=v9os-node-02 localIp=192.168.1.111
2
3
4
5
6
2
3
4
5
6
⚖️ Step 5:Nginx 负载均衡(统一对外入口)
5.1 upstream 定义
nginx
upstream v9os_cluster {
least_conn;
server 192.168.1.10:9099 weight=3 max_fails=3 fail_timeout=30s;
server 192.168.1.11:9099 weight=2 max_fails=3 fail_timeout=30s;
server 10.0.5.20:9099 weight=1 max_fails=3 fail_timeout=30s backup;
keepalive 64;
}1
2
3
4
5
6
7
2
3
4
5
6
7
5.2 固定 location 配置(必须完全一致)
nginx
server {
listen 80;
server_name v9os.example.com;
location ^~ / {
proxy_pass http://v9os_cluster;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection Upgrade;
proxy_connect_timeout 3600s;
proxy_read_timeout 3600s;
proxy_send_timeout 3600s;
client_max_body_size 100m;
proxy_set_header Range $http_range;
proxy_set_header Host $http_host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
}1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
🔑 关键点:
^~ /优先匹配,防止正则 location 抢占proxy_read_timeout 3600s保证 WebSocket 长连接不断Connection Upgrade大小写不敏感,但建议统一写法client_max_body_size 100m支持大文件上传
5.3 HTTPS(生产环境直接套用)
nginx
server {
listen 443 ssl http2;
server_name v9os.example.com;
ssl_certificate /etc/letsencrypt/live/v9os.example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/v9os.example.com/privkey.pem;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers HIGH:!aNULL:!MD5;
# location ^~ / { ... } 完全复用上面的块
}1
2
3
4
5
6
7
8
9
10
11
2
3
4
5
6
7
8
9
10
11
🔍 Step 6:验证分布式生效
| 检查项 | 方法 | 期望结果 |
|---|---|---|
| 节点互通 | curl http://node2:9099/health 从 node1 发起 | 返回 code:0 |
| 缓存共享 | 任意节点写缓存,另一节点读 | 数据一致 |
| 数据库同步 | 任意节点增删改,另一节点查 | 数据一致 |
| 消息队列 | 触发插件事件/定时任务 | 所有在线节点收到 |
| WebSocket | 多标签页登录,发消息 | 消息路由到正确节点 |
| 插件调度 | 启用「文件管理」插件,看日志 | 插件进程只在一节点启动(亲和性) |
6.1 一键验证脚本
bash
#!/bin/bash
# check-cluster.sh
NODES=("192.168.1.10:9099" "192.168.1.11:9099" "10.0.5.20:9099")
for n in "${NODES[@]}"; do
echo "=== $n ==="
curl -s "http://$n/health" | jq .
curl -s "http://$n/api/system/cluster/nodes" | jq . # 需管理员 token
done1
2
3
4
5
6
7
8
2
3
4
5
6
7
8
🛡️ 进阶:高可用与运维技巧
6.1 配置中心高可用
- 多
remotes:"remotes": ["http://cc1/get", "http://cc2/get"]—— 启动时轮询读取,任意一个活着即可 - 静态兜底:关键配置也放一份本地
init.json同目录,配置中心全挂时仍能启动
6.2 优雅下线 / 滚动升级
bash
# 1. 从 LB 剔除
nginx -s reload # 先把 weight=0 或注释掉该 upstream 行
# 2. 通知节点停止接收新请求,处理完现有连接
curl -X POST http://node:9099/api/system/graceful-shutdown # 需实现该接口
# 3. 替换二进制,重启进程
# 4. 加回 LB1
2
3
4
5
6
7
8
9
2
3
4
5
6
7
8
9
6.3 监控指标建议(Prometheus + Grafana)
| 指标 | 含义 | 告警阈值 |
|---|---|---|
v9os_node_up | 节点存活 (1/0) | < N-1 个节点 up |
v9os_redis_connected | Redis 连接数 | > 80% pool |
v9os_mq_lag | 消息堆积 | > 10000 |
v9os_plugin_restart_total | 插件重启计数 | > 5/min |
v9os_request_duration_seconds | P99 延迟 | > 2s |
❓ 常见坑 & 避坑指南
| 现象 | 可能原因 | 解决 |
|---|---|---|
节点启动卡在 distributed runtime start failed | Redis/MySQL/MQ 连不上 | 检查安全组、白名单、DNS、端口 |
machine_id 重复导致插件不启动/调度混乱 | 复制 init.json 忘改 | 每节点生成唯一 UUID:uuidgen 或 cat /proc/sys/kernel/random/uuid |
配置中心 /save 返回 0 | Authorization 头不匹配 / JSON 非法 | 对比 remote_auth,用 json_validate 校验 |
| WebSocket 频繁断开重连 | Nginx proxy_read_timeout 太短 | 设 proxy_read_timeout 3600s; |
| 定时任务重复执行 | 多节点无 Leader 选举 | 企业版内置分布式锁,确保 mqbase.driver=rocket |
| 插件安装后其它节点不生效 | 插件包未同步 | 企业版自动同步,检查 SyncLocalPluginPackage 日志 |
📦 附录:一键生成 init.json 脚本
bash
#!/bin/bash
# gen-init.sh <machine_id> <load_ip> <config_center_url> <auth_secret>
MID=$1
LIP=$2
CCURL=$3
AUTH=$4
cat > init.json <<EOF
{
"remotes": ["${CCURL}/get"],
"remote_save": "${CCURL}/save",
"remote_auth": "${AUTH}",
"local": false,
"version": "1.0.000",
"machine_id": "${MID}",
"port": 9099,
"load_ip": "${LIP}",
"wait_network": true
}
EOF
echo "✅ init.json generated for ${MID} @ ${LIP}"1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
用法:
bash
./gen-init.sh v9os-node-01 192.168.1.10 http://192.168.0.100/config.php 1234561
🙋 遇到问题?
- 先看日志:前台输出或
journalctl -u v9os -f(如用 systemd) - 检查三大件:Redis / MySQL / RocketMQ 连通性
- 确认密钥一致:
secret/password_key/communication_key/system_id/distributed_id - 提 Issue:GitHub Issues 带上
init.json(脱敏后)与错误堆栈
🎉 恭喜! 你已完成 V9OS 分布式集群部署。