面向第一次接触 oMLX 和本地大模型的读者。照本教程从零可以在两台 Mac mini 上跑起一个真正的分布式推理集群,并让一个单机装不下的模型同时使用两台机器的统一内存。
本教程不是凭文档推测写的——它是在下面这套真实机器上完整跑通后回写的,所有命令、截图、报错文本都来自实测。
这套环境(实测基线)
| 项目 | 机器 A(协调者 / Coordinator) | 机器 B(工作节点 / Worker) |
|---|---|---|
| 机型 | Mac mini(Mac16,10) | Mac mini(Mac16,10) |
| 芯片 | Apple M4 | Apple M4 |
| 统一内存 | 16 GB | 16 GB |
| 主机名 | remotemac.local |
mm4deMac-mini.local |
| 登录账号 | remotemac |
mm4 |
| 雷电口 IP | 10.9.9.1/24 |
10.9.9.2/24 |
| 雷雳接口 | Thunderbolt 4(40 Gb/s) | Thunderbolt 4(40 Gb/s) |
| 软件 | 版本 |
|---|---|
| macOS | 26.6.2 |
| oMLX | 0.6.4 |
| MLX | 0.32.0 |
| MLX-LM | 0.31.3 |
| 集群运行时 Python | 3.11.10 |
最终跑通的模型:GLM-4.7-Flash-4bit(15.7 GiB 权重,47 层,30B MoE 每次只激活约 3B)
最终结果(本教程第 6 章的验收状态):
CLUSTER LIVE GLM-4.7-Flash-4bit http://127.0.0.1:8000/v1
COORDINATOR · R0 READY remotemac · M4 · 16 GB 7.99 GiB / 13.50 GiB
WORKER · R1 READY mm4deMac mini · M4 · 16 GB 8.04 GiB / 13.50 GiB
RING LATENCY 141 µs COLLECTIVE THROUGHPUT 1.62 GiB/s
RESIDENT MEMORY 16.03 GiB / 27.00 GiB
目录
- 第 0 章 先弄懂这套东西
- 第 1 章 前置条件
- 第 2 章 雷电口直通(核心)
- 第 3 章 让对端不会睡着
- 第 4 章 把可用显存拉到最大
- 第 5 章 oMLX 侧配置(跟图操作)
- 第 6 章 验收:真实推理
- 第 7 章 坑点清单
- 第 8 章 哪些模型能上集群
- 第 9 章 日常运维
- 第 10 章 参考资料
第 0 章 先弄懂这套东西
0.1 oMLX 是什么
oMLX 是一个跑在 macOS 上的本地大模型推理服务(/Applications/oMLX.app)。它做的事情:
- 用 Apple 的 MLX 框架,把模型跑在 Mac 的 统一内存 + GPU(Metal) 上;
- 对外暴露一套 OpenAI 兼容的 HTTP API(默认
http://127.0.0.1:8000/v1),所以任何支持 OpenAI 接口的客户端都能直接接上; - 自带一个网页管理后台(
http://127.0.0.1:8000/admin/dashboard),模型下载、量化、参数调整、集群配置全在里面点。
统一内存是 Apple Silicon 的关键特性:CPU 和 GPU 共用同一块内存,不需要把模型在显存和内存之间搬来搬去。所以"16 GB 的 Mac"能拿来跑模型的那部分内存,就是它的"显存"。
0.2 集群(Cluster)解决什么问题
一台 16 GB 的 Mac,实际能分给模型的只有 13.5 GiB 左右。想跑 15.7 GiB 的模型就装不下。
oMLX 的集群功能把两台(或更多)Mac 的内存拼起来,把模型按层切开分给各台机器:
┌──────────────────────────────┐
客户端请求 ───► │ 机器 A = Rank 0(协调者) │
OpenAI API │ • 对外提供 API 和对话模板 │
│ • 持有模型的「后段」层 24–47 │
└──────────┬───────────────────┘
│ 雷电口直连
│ TCP Ring 集合通信
┌──────────▼───────────────────┐
│ 机器 B = Rank 1(工作节点) │
│ • 持有模型的「前段」层 0–24 │
│ • 不对外暴露端口 │
└──────────────────────────────┘
两个关键点:
- 对客户端完全透明。两台机器对外仍然只是
http://127.0.0.1:8000/v1一个地址,客户端不需要知道后面有几台机器。 - KV 缓存不出机器。每台机器只存自己那部分层的 KV,不做跨机搬运——否则每生成一个 token 都要走一次网络,得不偿失。
0.3 名词速查表
| 名词 | 意思 |
|---|---|
| Rank | 集群里每台机器的编号。Rank 0 是协调者,其余是工作节点 |
| 层 / layer | 大模型的 Transformer 层。模型被按层切开分给各机器,例如 layers 0–24 给 Rank 1、layers 24–47 给 Rank 0 |
| 分片 / shard | 每台机器分到的那部分模型 |
| 流水线并行 / Pipeline | 按层切:A 机算前面几层,算完把中间结果传给 B 机继续算。适合"模型太大、一台装不下" |
| 张量并行 / Tensor (TP) | 按宽度切:每一层都拆开,两台机器同时算同一个 token,再合并结果。延迟更低,但要求链路足够快,而且模型的注意力头数要能整除机器数 |
| 预填充 / Prefill | 处理你输入的那段文字(“提示词”)的阶段。这个阶段吃算力 |
| 解码 / Decode | 一个一个字往外吐的阶段。这个阶段吃内存带宽 |
| TTFT | Time To First Token,从发出请求到收到第一个字的耗时 |
| Metal 上限 | macOS 允许 GPU 最多占用多少内存(内核参数 iogpu.wired_limit_mb) |
| 内存守卫 / memory guard | oMLX 自己的内存阀门,防止把系统撑爆 |
| 传输后端 / backend | 机器之间怎么通信。jaccl = 走 RDMA;ring = 走普通 TCP 环 |
| RDMA | 远程直接内存访问,绕过 CPU 和操作系统、直接从一台机器的内存搬到另一台。延迟极低 |
| JACCL | Apple 开源的集合通信库,用来实现 RDMA 上的多机通信 |
| MLX / MLX-LM | Apple 的机器学习框架 / 它的大模型工具库 |
第 1 章 前置条件
1.1 硬件
- 两台 Apple Silicon Mac(M 系列芯片)。本教程用两台 M4 Mac mini。
- 一条雷雳线(Thunderbolt 4 或 5 都可;用 5 的线插在 4 的口上也能跑,速率按低的算)。
用雷雳线,不是 USB-C 数据线。USB-C 线在雷雳口上可能只跑 USB 协议,不当成雷雳链路。
- 建议两台都插上电源。
1.2 软件(两台都要装)
- 两台装同一个版本的 oMLX。
defaults read /Applications/oMLX.app/Contents/Info.plist CFBundleShortVersionString # 两台输出必须一致,例如 0.6.4 - 两台 macOS 版本尽量一致(RDMA 相关能力依赖 macOS 版本)。
- 两台都要打开"远程登录":系统设置 → 通用 → 共享 → 远程登录(Remote Login)打开。
1.3 两台必须满足的三条硬性一致性
oMLX 官方文档明确要求,缺一不可:
| 要求 | 说明 |
|---|---|
| ① 相同的 oMLX / MLX / MLX-LM 版本 | 版本不一致时预检会直接拒绝 |
| ② 模型存放在完全相同的绝对路径 | 例如两台都是 /opt/omlx/models/mlx-community/GLM-4.7-Flash-4bit |
| ③ 用密钥方式的 SSH,且协调者账号能免密登录对端 | 图形界面不会弹密码框;用密码的 SSH 在无人值守启动时一定失败 |
1.4 两个容易忽略的前提
- 对端不能睡眠(见第 3 章)。
- 对端上要关掉占内存的大程序(浏览器、IDE 等)。可用内存是实时算的,开着 Chrome 就少几个 GiB。
第 2 章 雷电口直通(核心)
这一章是整篇教程里最值得认真做的部分。用 Wi-Fi 也能配起来,但用 Wi-Fi 配起来没有意义——分布式推理每一层都要跨机通信,Wi-Fi 的延迟和抖动会让它慢到不如单机。
2.1 物理连接
用一条雷雳线,把两台机器的任意一个雷雳口对接即可(两台 Mac mini 各有 3 个雷雳口,直连不需要关心插哪个)。
连接成功的判断依据(macOS 自带工具):
system_profiler SPThunderboltDataType | grep -E "Bus|Status|Speed"
正常应该看到恰好一个口 “Device connected”、速率 40 Gb/s:
Thunderbolt/USB4 Bus 3:
Status: No device connected
Speed: Up to 40 Gb/s
Thunderbolt/USB4 Bus 1:
Status: Device connected ← 就是这个口
Speed: 40 Gb/s
Device Name: Mac16,10
Thunderbolt/USB4 Bus 0:
Status: No device connected
如果三个口全是 “No device connected”,先换线、换口再继续。这一步没过,后面全是白费。
本教程的排查过程中就遇到过"换了线缆和两端接口"才通的情况,所以先把物理链路确认死。
2.2 关于"雷雳桥接":两台机器要保留,不要关
macOS 会把所有雷雳口桥接成一个虚拟网卡 bridge0,这就是 IP over Thunderbolt:
ifconfig bridge0
# bridge0: flags=8863<UP,BROADCAST,SMART,RUNNING,SIMPLEX,MULTICAST> mtu 1500
# member: en2 flags=3<LEARNING,DISCOVER>
# member: en3 flags=3<LEARNING,DISCOVER>
# member: en4 flags=3<LEARNING,DISCOVER>
# status: active
这里有个网上说法容易把人带偏。
Apple 技术文档 TN3205 说:当多台 Mac 连成环(loop)时要关掉雷雳桥接,否则广播帧会在环里无限转发、吃掉 CPU。
但那是 3 台及以上成环的场景。两台直连不存在环,桥接必须保留 —— oMLX 的集群通信正是跑在bridge0上的。
我们的实测链路类型就是经 bridge0 建立的(第 2.5 节有原文)。
2.3 给雷电口配静态 IP
bridge0 默认拿的是 169.254.x.x 这种自分配地址,不稳定也不好记。给两端各设一个固定 IP:
# 机器 A
sudo networksetup -setmanual "Thunderbolt Bridge" 10.9.9.1 255.255.255.0
# 机器 B
sudo networksetup -setmanual "Thunderbolt Bridge" 10.9.9.2 255.255.255.0
验证:
networksetup -getinfo "Thunderbolt Bridge"
# Manual Configuration
# IP address: 10.9.9.1
# Subnet mask: 255.255.255.0
# Router: (null) ← 没有网关是正常的,这是点对点直连
服务名就是英文的
Thunderbolt Bridge,即使系统语言是中文也不需要改。
这两条命令会写进系统配置,重启后仍然有效。
2.4 验证直连真的通了
ping -c 3 10.9.9.2
# 3 packets transmitted, 3 packets received, 0.0% packet loss
# round-trip min/avg/max = 0.499/0.516/0.534 ms
0.5 毫秒这个数字就是"在走直连"的证明(走 Wi-Fi 通常是 2~10 ms,且抖动大)。
再确认操作系统没把流量调度到 Wi-Fi 上去:
route -n get 10.9.9.2 | grep interface
# interface: bridge0 ← 走桥接(= 雷电口)✅
route -n get 192.168.203.130 | grep interface
# interface: en1 ← 对端的 Wi-Fi 地址会走 Wi-Fi ❌
这就是为什么要用 10.9.9.2 而不是主机名或 Wi-Fi 地址——只有雷电口 IP 才能保证流量不出雷电链路。
2.5 关于 RDMA / JACCL:M4 用不了,属于正常
这套方案里唯一"达不到理想状态"的地方。用 oMLX 的探测接口看真相:
curl -s -b /tmp/omlx.jar -G "http://127.0.0.1:8000/admin/api/cluster/fabric" \
--data-urlencode "hosts=remotemac@127.0.0.1,mm4@10.9.9.2" | python3 -m json.tool
实测返回(原文,有删节):
{
"ok": true,
"backend": "ring",
"backend_reason": "Thunderbolt link detected, without RDMA; falling back to the TCP ring because ... reports no RDMA device. RDMA is off by default and can only be enabled from macOS Recovery, so this cluster cannot use jaccl yet.",
"link": {
"ok": true,
"source": { "host": "remotemac@127.0.0.1", "interface": "bridge0", "address": "10.9.9.1" },
"peer": { "host": "mm4@10.9.9.2", "interface": "bridge0", "address": "10.9.9.2" },
"kind": "thunderbolt",
"reason": "remotemac@127.0.0.1 bridge0 10.9.9.1/24 and mm4@10.9.9.2 bridge0 10.9.9.2/24 share 10.9.9.0/24 over thunderbolt"
},
"rdma": { "ok": false, "rows": [], "reason": "... reports no RDMA device..." }
}
翻译成大白话:
| 结论 | 说明 |
|---|---|
thunderbolt |
oMLX 认得出这是雷电直连(10.9.9.0/24 经 bridge0) |
所以后端回退成 ring(普通 TCP 环),而不是 jaccl |
|
| TCP ring 跑在 40 Gb/s 的雷电链路上,实测集合通信吞吐 1.62 GiB/s,环延迟 141 µs |
为什么没有 RDMA? 不是配置没做对,是硬件不支持:
- RDMA over Thunderbolt 需要 Thunderbolt 5 硬件 + macOS 26.2 及以上;
- 而且必须进 macOS 恢复模式执行
rdma_ctl enable才能打开(默认关闭,图形界面里改不了); - 本环境是 M4 Mac mini = Thunderbolt 4,所以无解。
sysctl -n iogpu.wired_limit_mb # 与 RDMA 无关,这是 Metal 上限
ibv_devices 2>/dev/null # 没有输出 = 没有 RDMA 设备(正常)
实际影响:跑流水线并行(按层切)完全够用;跑张量并行(每层都要 all-reduce)会比较吃亏——这也是后面 oMLX 自动选择"用流水线而不是张量"的原因(见 5.6)。
如果你手上有 Thunderbolt 5 的机器,进恢复模式打开 RDMA 后后端会升级为jaccl,张量并行才真正有意义。
2.6 小结:为什么坚持不用 Wi-Fi
| 维度 | 雷电直连 | Wi-Fi |
|---|---|---|
| 延迟 | 0.5 ms | 2~10 ms,抖动大 |
| 带宽 | 40 Gb/s(≈5 GB/s) | 实际几百 Mbps ~ 2 Gbps,且和别的设备抢 |
| 稳定性 | 点对点,不经过路由器 | 受信道、距离、邻居 AP 影响 |
| 是否被 oMLX 认作"快链路" | thunderbolt |
第 3 章 让对端不会睡着
这是最容易被忽略、又最致命的一条。 雷雳直连和 SSH 都在,但对端一睡眠就全部失效,集群直接掉线。本教程的排查过程中就在这里卡了很久。
3.1 症状
ping -c 3 10.9.9.2
# 3 packets transmitted, 0 packets received, 100.0% packet loss
arp -an | grep 10.9.9.2
# ? (10.9.9.2) at (incomplete) on bridge0 ← (incomplete) 就是"对端没应答"
ssh mm4@10.9.9.2
# ssh: connect to host ...: Host is down
3.2 两台都关掉睡眠(需要管理员密码)
sudo pmset -a sleep 0 displaysleep 0 powernap 0 disksleep 0
pmset -g | grep -E "^\s*(sleep|displaysleep|powernap)"
对两台机器分别执行。
注意
-a是"所有电源模式",只写-c(接电源时)是不够的。
3.3 再加一道保险:防休眠断言
pmset 之后,如果有进程持有"防休眠断言",系统会更牢靠。两种做法:
做法一(临时、当前会话有效)
caffeinate -dims &
做法二(推荐,开机自启、不受终端关闭影响)
在两台各放一个 LaunchAgent ~/Library/LaunchAgents/com.omlx.nosleep.plist,让它常驻。验证:
launchctl list | grep nosleep
# 58055 0 com.omlx.nosleep ← 有输出就是加载成功
pmset -g assertions | grep -E "PreventSystemSleep|PreventUserIdleSystemSleep"
# PreventSystemSleep 0
# PreventUserIdleSystemSleep 1 ← 1 = 已阻止空闲休眠
判断"到底谁在阻止休眠"比想象中麻烦:
PreventUserIdleSystemSleep 1也可能只是"屏幕亮着"导致的(Powerd - Prevent sleep while display is on)。所以两项一起看,最好用 LaunchAgent 这种明确归属自己进程的方式。
第 4 章 把可用显存拉到最大
oMLX 的内存上限由三层阀门叠加决定,取最小值。很多人只调了一层,以为已经拉满,实际还差得远。
① 静态上限 static = 物理内存 − 2 GB
↓
② 动态上限 dynamic = 当前可回收内存(实时算)
↓
③ Metal 上限 metal_cap = 内核 iogpu.wired_limit_mb
↓
实际可用 = min(①, ②, ③)
4.1 第一层:内核 Metal 上限(kernel)
macOS 默认只允许 GPU 占用约 75% 的内存。抬高它:
# 抬到 14 GiB(14336 MB)
sudo sysctl iogpu.wired_limit_mb=14336
sysctl iogpu.wired_limit_mb
# iogpu.wired_limit_mb: 14336
永久生效(重启后仍保留):
echo "iogpu.wired_limit_mb=14336" | sudo tee -a /etc/sysctl.conf
改完必须重启 oMLX!
oMLX 主进程在启动时就把 Metal 上限读进内存缓存了(官方设计如此,避免运行中上限乱变)。不重启的话,即使sysctl显示 14336,oMLX 仍然按旧值(11.84 GiB)判断。这是后面坑点 5 的根源。
4.2 第二层:内存守卫档位
打开后台:设置 → 资源管理 → 内存守卫。
| 档位 | 行为 |
|---|---|
| 安全 / 均衡 / 激进 | 上限由 实时可回收内存 决定(vm_stat 算出来的空闲 + 非活跃) |
| 自定义(custom) | 跳过动态上限,直接用你填的数值,只受静态上限和 Metal 上限约束 |
想榨干内存,选「自定义」并填 13.5 GB:
* 自定义上限 13.5 GB GB → 有效上限 13.5 GB
为什么推荐「自定义」?因为「均衡」档会拿实时可回收内存当上限。你多开一个 Chrome,上限就掉几 GB,然后集群启动时报一个莫名其妙的"内存不够"。用「自定义」把这个不确定因素去掉。
两台机器都要改(~/.omlx/settings.json 里的 memory.memory_guard_tier),并且需要重启 oMLX 生效。
4.3 第三层(影响最大):节点角色 Headless vs Workstation
这是最容易被忽略、对可用内存影响最大的一项,藏在集群页的「MEMORY EACH ACCELERATOR GIVES」卡片里。
| 角色 | 含义 | 实际留给集群的内存 |
|---|---|---|
| Workstation | “这台 Mac 我平时还要用” | 只给一半(admission_fraction = 0.5),其余留给你办公 |
| Headless | “这台 Mac 没人用,全给集群” | 只留 10% 余量(reserve_fraction = 0.10) |
实测差距(同一台 M4 16 GB):
角色 = Workstation → 只有 5.5 GiB 可用于模型
角色 = Headless → 有 12.0 GiB 可用于模型 ← 差 2 倍以上
两台都切到 Headless,可用的模型内存从 11 GiB 直接翻到 24 GiB。
切角色的方法:在「MEMORY EACH ACCELERATOR GIVES」里,每台机器后面有
Headless/Workstation两个按钮,点Headless即可,然后点 Measure devices 重新测量。
4.4 顺手做:关掉占内存的程序
可用内存是实时计算的。跑集群之前,把两台机器上的浏览器、IDE、Docker 之类关掉,能实打实多出几个 GiB。
第 5 章 oMLX 侧配置(跟图操作)
以下全部在机器 A 的浏览器里操作:打开 http://127.0.0.1:8000/admin/dashboard,用 API 密钥登录。
5.1 第一步:开启「分布式推理」开关
这一步不做,集群标签页、集群 API、Bonjour 广播全部不存在。
路径:设置 → 全局设置 → 高级 → 分布式推理 → Enable Distributed Inference
它的说明文字很直白:
重启后启用集群选项卡、集群 API 路由和 Bonjour 广播。功能完善期间默认关闭。
两台都要开,并且都要重启 oMLX(右上角会显示「需要重启」)。本环境两台都已经开启(server.distributed_inference_enabled = true)。
5.2 第二步:两台准备同一份模型
官方硬性要求:绝对路径必须完全相同。
做法 A:两台各自下载(最省心)
export HF_ENDPOINT=https://hf-mirror.com # 直连 huggingface.co 被墙时用镜像
hf download mlx-community/GLM-4.7-Flash-4bit \
--local-dir /opt/omlx/models/mlx-community/GLM-4.7-Flash-4bit
做法 B:一台下好,用雷电口直接拷(推荐,比重新下载快得多)
实测通过雷电口 rsync 16 GB 模型用了 2 分 13 秒(约 290 MB/s):
rsync -a --partial \
-e "ssh -i ~/.ssh/omlx_cluster -o BatchMode=yes" \
/opt/omlx/models/mlx-community/GLM-4.7-Flash-4bit/ \
mm4@10.9.9.2:/opt/omlx/models/mlx-community/GLM-4.7-Flash-4bit/
macOS 自带的是
openrsync(rsync 2.6.9 兼容版),不支持--info=progress2。用--partial就好,写--info=progress2会直接打印用法帮助、什么都不做。
拷完必须校验字节数一致:
# 本地
cd /opt/omlx/models/mlx-community/GLM-4.7-Flash-4bit
for f in model-*.safetensors; do echo "$f $(stat -f%z "$f")"; done
# 对端
ssh mm4@10.9.9.2 'cd /opt/omlx/models/mlx-community/GLM-4.7-Flash-4bit && for f in model-*.safetensors; do echo "$f $(stat -f%z "$f")"; done'
两边输出必须逐字节相同:
model-00001-of-00004.safetensors 5357453035
model-00002-of-00004.safetensors 5363462379
model-00003-of-00004.safetensors 5363462333
model-00004-of-00004.safetensors 768064072
5.3 第三步:配对 SSH(oMLX 专用密钥)
切到 集群 标签页,找到「1 · VERIFY THE WORKER」卡片。
oMLX 会显示一条非常重要的提示:
Terminal SSH can work while oMLX cannot
oMLX deliberately uses its own dedicated SSH key. A successful manual SSH login may be using a different key, so complete these steps on both Macs before checking the peer.(翻译:终端能 SSH 通,不代表 oMLX 能通。oMLX 特意用自己专用的 SSH 密钥,你手敲 SSH 成功可能是用了别的密钥。)
这就是为什么"我 ssh 明明能连上,oMLX 却说过不去"。
oMLX 的专用密钥固定放在:~/.ssh/omlx_cluster(公钥 .pub)。配对流程分三步:
| 步骤 | 做什么 |
|---|---|
| Step 1 of 3 | 在一台 Mac 上生成「共享配对密钥」(至少 16 字符),复制到另一台,两台填入同一个值。它只用来给密钥交换令牌做 HMAC 校验,不是你的 SSH 密码 |
| Step 2 of 3 | 确认两台的 oMLX 密钥都处于 Active。界面上会显示指纹,例如 SHA256:0Yx7FTwnAlypzuD+CnqIbz3QYwLuH3TYIEkedWZVZhw。两台都要 Active |
| Step 3 of 3 | 双向交换密钥:A 生成交换令牌 → 复制到 B 粘贴(Pair);然后 B 生成 → 复制回 A 粘贴。必须两个方向都做,否则只有一边信任另一边 |
完成后的样子:
5.4 第四步(最大的坑):对端地址必须写 用户名@雷电IP
配对卡片下方有三个输入框:
| 字段 | 填什么 | 本环境 |
|---|---|---|
| Peer SSH name | 用户名@对端雷电IP |
mm4@10.9.9.2 |
| This Mac · address | 本机雷电口 IP | 10.9.9.1 |
| Peer · address | 对端雷电口 IP | 10.9.9.2 |
填完点 Check peer。成功时下方会出现提示:
Edited by hand — activation will use these addresses over the TCP ring.
如果你只填主机名(mm4deMac-mini.local),就会看到这个红色错误卡片:
The other Mac rejected the login
SSH connected but the peer would not accept this Mac's key.
① Generate a key here, then paste it on the peer using Pair.
② Check that the SSH username in the peer address is correct.
根本原因是第 ② 条:SSH 地址里没写用户名时,SSH 会用本机的登录用户名去登录对端。本机是 remotemac、对端账号是 mm4,所以被拒。
用命令行复现(帮助理解,不用照着执行):
# ❌ 只写主机名 → 变成 remotemac@mm4demac-mini.local → Permission denied
ssh -i ~/.ssh/omlx_cluster mm4deMac-mini.local
# remotemac@mm4demac-mini.local: Permission denied (publickey,password,keyboard-interactive).
# ✅ 带上正确用户名 → OK
ssh -i ~/.ssh/omlx_cluster mm4@10.9.9.2
# OK
对应的后端报错原文:
{"detail":"SSH to mm4deMac-mini.local failed: remotemac@mm4demac-mini.local: Permission denied (publickey,password,keyboard-interactive)."}
5.5 第五步(强烈建议):用 ~/.ssh/config 把主机名钉死
为什么还需要这一步? 因为 oMLX 有一部分探针(内存上限探测、RDMA 探测)不走你填的 SSH 名,而是直接用 Bonjour 发现出来的裸主机名(mm4deMac-mini.local)。裸主机名又会退回本机用户名 → 同一个 Permission denied。
与其到处填地址,不如让"裸主机名"本身就解析对:在两台各写一个 ~/.ssh/config。
机器 A(~/.ssh/config)
# oMLX 双机集群:把对端主机名固定到「用户名 + 雷电口直连 IP + 专用密钥」
Host mm4deMac-mini.local mm4demac-mini.local mm4deMac-mini 10.9.9.2
User mm4
HostName 10.9.9.2
IdentityFile ~/.ssh/omlx_cluster
IdentitiesOnly yes
StrictHostKeyChecking accept-new
ServerAliveInterval 30
机器 B(~/.ssh/config)
Host remotemac.local remotemac 10.9.9.1
User remotemac
HostName 10.9.9.1
IdentityFile ~/.ssh/omlx_cluster
IdentitiesOnly yes
StrictHostKeyChecking accept-new
ServerAliveInterval 30
chmod 600 ~/.ssh/config
这个配置一箭三雕:
- 补上用户名 —— 裸主机名也能正确登录;
HostName指向雷电 IP —— 即使 Bonjour 把主机名解析到 Wi-Fi 地址,流量也会强制走雷电口,从根上杜绝"偷偷回落到 Wi-Fi";IdentitiesOnly yes—— 只提供这一把密钥,避免"提供的密钥太多"导致的认证失败。
还要补上反向信任(对端的 oMLX 公钥要进本机的 authorized_keys):
# 取对端公钥
PEERKEY=$(ssh -i ~/.ssh/omlx_cluster mm4@10.9.9.2 'cat ~/.ssh/omlx_cluster.pub')
# 追加到本机 authorized_keys
echo "$PEERKEY omlx-cluster-peer" >> ~/.ssh/authorized_keys
chmod 600 ~/.ssh/authorized_keys
双向验证(一定要都测):
# 正向:本机 → 对端(用裸主机名,验证 config 生效)
ssh mm4deMac-mini.local "whoami; hostname"
# mm4
# mm4deMac-mini.local
# 反向:对端 → 本机
ssh -i ~/.ssh/omlx_cluster mm4@10.9.9.2 'ssh remotemac.local "whoami"'
# remotemac
到这里,正反两个方向都能用裸主机名免密登录,说明
~/.ssh/config+ 专用密钥 + 双向信任全部到位。
5.6 第六步:四步流程图
集群页顶部有一个四步进度条,就是你接下来要走的流程:
| 步骤 | 内容 | 本环境结果 |
|---|---|---|
| 1 · Connect the workers | 发现并配对工作者 | mm4deMac-mini.local |
| 2 · Check the link | 检测互联链路 | Detected / TCP ring · manual addresses |
| 3 · Split the model | 选择模型并切分 | 2 nodes as 2 pipeline stages |
| 4 · Activate | 启动服务 | 1 running |
① 计算池就绪
状态变成绿色的 Ready,两台都探测成功:
COORDINATOR · R0remotemac · M4 · 16 GB · 9 GiB usableWORKER · R1mm4deMac mini · M4 · 16 GB · 9 GiB usable
如果这里一直显示
Needs attention,看第 7 章的坑点排查。
② 选策略 + 选模型
在「2 · UNEQUAL-MEMORY PLANNER」里:
策略三选一:
| 选项 | 什么时候用 |
|---|---|
| Automatic(推荐) | 让 oMLX 自己判断:能均分且链路够快就用张量,否则退回流水线 |
| Tensor — faster responses | 每台机器都参与每个 token 的计算,延迟低。需要已验证的快链路,且注意力头数能被机器数整除 |
| Pipeline — bigger models | 每台机器持有不同的层。模型单机装不下时用这个;链路慢一点也能跑 |
选模型:点列表里的模型(注意每行都标了是 LLM 还是 VLM,原因见第 8 章):
GLM-4.7-Flash-4bit LLM · 16.5 GiB
Qwen3.8-27B-OptiQ-4bit VLM · 19.9 GiB ← VLM 不能上集群
gemma-4-E4B-it-oQ4e LLM · 4.3 GiB
顺手点一次「Detect link」,让链路信息刷新(否则可能残留旧报错)。
③ 测量内存 → 生成方案
先点「Measure devices」,让 oMLX 去每台机器实测可用的安全上限:
remotemac.local Headless 12 GiB for the cluster of 14 GiB this Mac can admit
mm4deMac-mini.local Headless 12 GiB for the cluster of 14 GiB this Mac can admit
(还记得 4.3 节吗——如果这里是 Workstation,数值会直接砍半。)
再点「Build plan」生成分片方案。展开「Advanced activation settings」还能看到 Tensor parallelism / Pipeline only 的微调项。
④ 看方案,然后启动
「MODEL SHARD BALANCE」把方案摊开给你看。本环境的实际方案:
| mm4deMac-mini.local(Rank 1) | remotemac.local(Rank 0) | |
|---|---|---|
| 层区间 | layers 0–24 |
layers 24–47 |
| 模型权重 | 8.0 GiB | 8.0 GiB |
| KV 缓存预留 | 0.8 GiB | 0.8 GiB |
| 保留余量 | 4.5 GiB | 4.5 GiB |
| 可支撑上下文 | 37k tokens | 41k tokens |
底部一行总结:
This cluster serves up to 37k tokens — Set by the Mac with the least room left; a request passes through every stage.
(取余量最少那台机器的能力;一个请求要穿过所有阶段。)
注意这里是 0–24 / 24–47,不是 50/50 的 23/24 —— 因为模型层数不一定能整除,oMLX 会按每层实际字节数和每台机器可用容量来分配。
方案满意后,点右上角「Start Cluster」。
启动时 oMLX 会做一整套校准:给每台机器跑一个小矩阵工作量测算力、跑小消息和 1 MiB 的集合通信测带宽,然后把测量结果代入重新平衡分片。这个过程是失败即回退的——任何一台测不出来,就退回只用内存算出来的保守方案,不会用半截数据糊弄你。
第 6 章 验收:真实推理
启动完成后,集群页会变成这样:
四步全绿:
| 步骤 | 结果 |
|---|---|
| Connect the workers | mm4deMac-mini.local |
| Check the link | Link speed unknown |
| Split the model | 2 nodes as 2 pipeline stages. Tensor parallelism was not used because no larger split fit in memory. Node compute and link measurements were applied before model staging. |
| Activate | 1 running |
计算池显示 Running on 2 devices,并给出:
NEURAL FABRIC Cluster ready for requests
TCP ring · manual addresses · 2 devices
CLUSTER LIVE GLM-4.7-Flash-4bit
OPENAI-COMPATIBLE API http://127.0.0.1:8000/v1
COORDINATOR · R0 READY remotemac · M4 · 16 GB 7.99 GiB / 13.50 GiB
WORKER · R1 READY mm4deMac mini · M4 · 16 GB 8.04 GiB / 13.50 GiB
| 指标 | 实测值 | 含义 |
|---|---|---|
| RING LATENCY | 141 µs | 环通信延迟(启动探测的最慢一跳) |
| COLLECTIVE THROUGHPUT | 1.62 GiB/s | 1 MiB all-reduce 的集合通信吞吐(最慢的 rank) |
| PROMPT PREFILL | 4.7 tok/s | 提示词预填充速度 |
| RESIDENT MEMORY | 16.03 GiB / 27.00 GiB | 常驻权重 / 集群总容量 |
![]()
Check the link显示Link speed unknown是正常的——RDMA 不可用时拿不到链路速率标称值(见 2.5)。它不影响运行。
而Split the model明确写了:用的是流水线并行,不是张量并行,原因是"没有更大的切分能塞进内存"。这和 2.5 节的判断一致。
跑一个真实请求
curl -s http://127.0.0.1:8000/v1/chat/completions \
-H 'Content-Type: application/json' \
-H 'Authorization: Bearer <你的API密钥>' \
-d '{
"model": "GLM-4.7-Flash-4bit",
"messages": [{"role":"user","content":"用一句话说明你正运行在几台机器上。"}],
"max_tokens": 80,
"temperature": 0.2
}'
实测返回(有删节):
{
"id": "chatcmpl-6f577499",
"model": "GLM-4.7-Flash-4bit",
"choices": [{ "message": { "role": "assistant", "reasoning_content": "..." }, "finish_reason": "length" }],
"usage": { "prompt_tokens": 17, "completion_tokens": 80, "total_tokens": 97, "total_time": 4.2 }
}
这一步过了,双机集群就算真正跑通了。 17 个输入 token、生成 80 个 token、耗时 4.2 秒。
模型不知道自己在几台机器上跑,会一本正经地答"我运行在云上"——这是正常的,它没有这个上下文。
第 7 章 坑点清单
坑 1~13 按「踩到的概率」从高到低排列;坑 14、15 是集群跑起来之后才会遇到的运维坑。
坑 1 · 对端 SSH 地址没写用户名
最常踩
| 现象 | 集群页顶部红色卡片:The other Mac rejected the login — SSH connected but the peer would not accept this Mac's key |
| 命令行复现 | remotemac@mm4demac-mini.local: Permission denied (publickey,password,keyboard-interactive). |
| 原因 | SSH 地址只写主机名时,用本机用户名登录对端。两台机器账号名不一样(remotemac vs mm4)就一定失败 |
| 解决 | 「Peer SSH name」写 mm4@10.9.9.2;同时用 ~/.ssh/config 把裸主机名也钉到正确用户名(5.5 节) |
坑 2 · 终端 SSH 通了,oMLX 还是说过不去
| 现象 | 手敲 ssh mm4@10.9.9.2 一切正常,但 oMLX 的 Check peer 失败 |
| 原因 | oMLX 用自己专用的密钥 ~/.ssh/omlx_cluster,和你手敲时用的密钥(可能是 ~/.ssh/id_ed25519 或 agent 里的)不是同一把 |
| 解决 | 老老实实走 5.3 的三步配对;确认两台 oMLX 密钥都是 Active |
坑 3 · 部分探针只用裸主机名,绕过你填的地址
| 现象 | Peer SSH 名已经改成 mm4@10.9.9.2,但「MEASURE DEVICES」报:memory ceiling probe failed for mm4deMac-mini.local: ... SSH to mm4deMac-mini.local failed: remotemac@mm4demac-mini.local: Permission denied |
| 原因 | 内存上限探测 / RDMA 探测走的是 Bonjour 发现出来的裸主机名,不是你在输入框里写的 SSH 名 |
| 解决 | 加 ~/.ssh/config(5.5 节)。这是最彻底的办法——让裸主机名本身就解析到正确的用户 + 雷电 IP |
坑 4 · 可用内存只有一半(节点角色是 Workstation)
| 现象 | 16 GB 的机器只显示 5.5 GiB usable,方案被"内存不够"卡住 |
| 原因 | 该机器角色是 Workstation(“我平时还要用这台 Mac”),admission_fraction = 0.5,一半内存被保留给桌面 |
| 解决 | 在「MEMORY EACH ACCELERATOR GIVES」里点 Headless,再点 Measure devices → 变成 12 GiB |
坑 5 · 改了 iogpu.wired_limit_mb 但不生效
| 现象 | 启动报 projected memory 11.91GB would exceed the metal_cap memory ceiling 11.84GB ... Raise kernel iogpu.wired_limit_mb (currently caps Metal at 11.84GB) |
| 原因 | sysctl 显示 14336 了,但 oMLX 主进程在启动时已缓存旧上限(11.84 GiB) |
| 解决 | 改完 sysctl 必须重启 oMLX。重启后 recommended_working_set_bytes 会从 11.84 变成 14.00 GiB |
验证:
sysctl iogpu.wired_limit_mb # 14336
curl -s -b /tmp/omlx.jar http://127.0.0.1:8000/admin/api/cluster/status \
| python3 -c "import json,sys;d=json.load(sys.stdin)['node'];print(d['recommended_working_set_bytes']/1024**3,'GiB')"
# 14.0 GiB ← 对了就说明重启生效了
坑 6 · 内存守卫用「均衡」档,上限随内存波动
| 现象 | 启动报 Model does not fit the current per-node memory limits; no ranks were started. rank 0 needs 11.8 GiB but can admit 8.6 GiB right now |
| 原因 | 「均衡」档的上限 = 实时可回收内存。你开着浏览器它就低,关掉就高,同一台机器两次结果不一样 |
| 解决 | 内存守卫档位改「自定义」+ 填 13.5 GB(4.2 节),去掉这个不确定因素。两台都改 |
坑 7 · 模型索引里有子目录路径
| 现象 | unsafe safetensors filename in index: 'optiq/optiq_vision.safetensors' |
| 原因 | 某些量化版(如 oQ / OptiQ)把视觉塔外置成子目录,oMLX 的 staging 模块拒绝索引里出现 / |
| 解决 | 这属于模型打包方式与 oMLX 的兼容问题。最省事的做法是换一个不依赖子目录的模型(第 8 章) |
坑 8 · VLM(带视觉)模型不能上集群
| 现象 | Model 'Qwen3.8-27B-OptiQ-4bit' is a vlm model. Distributed cluster inference currently supports text LLM models only. |
| 原因 | oMLX 的分布式推理只支持纯文本 LLM。判据是模型 config.json 里 vision_config 非空 |
| 解决 | 换纯文本模型。注意这是设计如此,不是 bug——官方文档明确写了 DFlash, SpecPrefill, MTP, VLM MTP, ... are rejected for a distributed deployment rather than ignored |
模型列表里每行都会标 LLM 还是 VLM,选之前看一眼就不会踩。
坑 9 · 两台模型路径/文件不一致
| 现象 | 预检失败,或对端加载时报找不到文件 |
| 原因 | 官方硬性要求:两台绝对路径完全相同 |
| 解决 | 用雷电口 rsync(5.2 做法 B),拷完逐字节校验 |
坑 10 · 对端睡眠导致整个集群掉线
| 现象 | 配合一切正常,过一会儿 ping 100% 丢包、arp 显示 (incomplete)、ssh: Host is down |
| 原因 | 对端 pmset sleep = 1,空闲后睡着 |
| 解决 | 第 3 章:sudo pmset -a sleep 0 ... + LaunchAgent 常驻 |
坑 11 · 8000 端口被别的程序占了
| 现象 | 登录后台返回 401/501,或浏览器打开 127.0.0.1:8000 看到的是别的页面 |
| 原因 | 有别的程序(例如某个 python3 -m http.server)抢先绑定了 8000 |
| 解决 | 找到并结束占用进程,然后重启 oMLX |
| ```bash | |
| lsof -nP -iTCP:8000 -sTCP:LISTEN | |
| ``` | |
~/.ssh、密钥文件之类的目录暴露在 0.0.0.0:8000。 |
坑 12 · 改完配置,旧报错还挂着
| 现象 | 地址/角色明明改好了,但错误提示还在,状态还是 Needs attention |
| 原因 | oMLX 是失败即停止(fail closed)的设计:有未处理的事件就不让集群启动;链路信息也是缓存的 |
| 解决 | ① 点顶部「Cluster incidents」展开 → 逐条 Dismiss;② 重新点「Detect link」刷新链路;③ 必要时重开一次「Check peer」 |
集群事件面板长这样(本环境的真实记录,四条错误正好对应上面 4 个坑):
ERROR Model 'Qwen3.8-27B-OptiQ-4bit' is a vlm model. ... ← 坑 8
ERROR ... projected memory 11.91GB would exceed the metal_cap ... ← 坑 5
ERROR Model does not fit the current per-node memory limits ... ← 坑 6
ERROR unsafe safetensors filename in index: 'optiq/optiq_vision...' ← 坑 7
这个面板是最好的排查入口。 每次启动失败都会留一条事件,带完整报错原文。点右上角「Download diagnostic report」还能导出一份包含节点健康、启动过程、失败证据的整份报告。
坑 13 · RDMA 探测失败的告警(可忽略)
| 现象 | Could not verify the peer's RDMA state / RDMA is enabled, but ibv_devices reported no RDMA interfaces. |
| 原因 | M4 是 Thunderbolt 4,而 RDMA over Thunderbolt 需要 TB5 + macOS 26.2+ 且必须在恢复模式启用 |
| 解决 | 不用管。oMLX 会自动回退到 TCP ring,功能完整(见 2.5) |
坑 14 · 集群跑起来之后,过一会儿自己掉了
| 现象 | GET /cluster/runtime 出现 phase: peer_lost / live: false,推理请求报 503 distributed job exited with code 0 |
| 原因 | 对端的 rank 进程真的死了(不是网络问题)。最常见是规划把内存塞太满 |
| 判断依据 | runtime 里看每个 rank 的 utilization 和 headroom_bytes。出现 tuning_reason: "... critical headroom ..." 或 utilization > 0.98 就是危险信号 —— 实测旧规划 headroom 只剩 0.12 GiB、利用率 99.1%,一次普通请求就让对端 worker 挂掉,而且不留任何崩溃日志 |
| 解决 | 重新 Build plan + Start Cluster。重建时把保留量压到最小(角色 Headless + 内存守卫 自定义),reserve 从 4.5 GiB 降到 1.35 GiB → 利用率降到 75%、headroom 回到 3.3 GiB,就稳了 |
怎么看 headroom:集群页的「POOLED ACCELERATOR MEMORY」下方每个 rank 都有一行,或者
curl -b $CJ http://127.0.0.1:8000/admin/api/cluster/runtime。
headroom 低于 0.5 GiB 就建议重建规划。
坑 15 · 推理返回 200,但 content 是空的
| 现象 | http=200,有 usage,但 choices[0].message.content 是 undefined,finish_reason: "length" |
| 原因 | 不是故障。 GLM-4.7-Flash 这类 thinking 模型会把 token 先灌满 message.reasoning_content,content 要等思考结束才出现。max_tokens 给小了(比如 120、300)就永远走不到那一步 |
| 解决 | max_tokens 至少给 1000。判断是否正常看 finish_reason:stop = 正常收尾,length = 被截断 |
第 8 章 哪些模型能上集群
选模型之前,先过两道门。很多人只想到第一道:
第一道门:模型类型
config.json 里有非空的 vision_config ? → 是 → VLM → ❌ 不能上集群
→ 否 → 纯文本 LLM → 过
第二道门:分片能力
MLX-LM 里有原生 shard() 实现 ? → 否 → ❌ oMLX 没有适配器,不能上集群
→ 是 → 检查注意力头数能否被机器数整除
本环境实测结论:
| 模型 | model_type |
体积 | 结果 |
|---|---|---|---|
mlx-community/GLM-4.7-Flash-4bit |
glm4_moe_lite |
15.7 GiB | |
mlx-community/Qwen3-32B-4bit |
qwen3 |
17.2 GiB | |
mlx-community/DeepSeek-R1-Distill-Qwen-32B-4bit |
qwen2 |
17.2 GiB | |
mlx-community/gpt-oss-20b-MXFP4-Q4 |
gpt_oss |
11.2 GiB | |
mlx-community/Qwen3-30B-A3B-4bit |
qwen3_moe |
— | shard()) |
Qwen3.8-27B 全家族(含 OptiQ / MTPLX / oQ4e) |
qwen3_5 |
19.9 GiB | vision_config) |
为什么推荐 GLM-4.7-Flash-4bit:
| 特性 | 说明 |
|---|---|
| 真正用满两台机器 | 15.7 GiB > 单机 13.5 GiB,必须切分 |
| MLA 压缩 KV 缓存 | kv_lora_rank = 512,每层每 token 只占约 1152 字节(稠密 32B 是 4096 字节,差 3.6 倍)→ 这就是它能撑起大上下文的根本原因 |
| 30B MoE 只激活 3B | 64 个专家每 token 只走 4 个 + 1 个共享 → 权重是 30B 的体积,算力是 3B 级别 |
| 原生长上下文 | max_position_embeddings = 202752 |
判定工具(下模型之前就能知道能不能上集群)
本环境沉淀了一个离线筛查脚本,不加载权重、不下载模型,只读配置就能判定两道门:
python3 <skill>/scripts/preflight-cluster-fit.py --nodes 2 --capacity-gib 13.5 \
mlx-community/GLM-4.7-Flash-4bit
第 9 章 日常运维
9.1 健康检查
# 集群状态(本机视角)
curl -s -b /tmp/omlx.jar http://127.0.0.1:8000/admin/api/cluster/status | python3 -m json.tool
# 链路与后端
curl -s -b /tmp/omlx.jar -G http://127.0.0.1:8000/admin/api/cluster/fabric \
--data-urlencode "hosts=remotemac@127.0.0.1,mm4@10.9.9.2" | python3 -m json.tool
# 部署记录(含层区间分配)
curl -s -b /tmp/omlx.jar http://127.0.0.1:8000/admin/api/cluster/deployments | python3 -m json.tool
部署记录里的关键字段(本环境实测):
{
"deployment_id": "GLM-4.7-Flash-4bit-cbacb0b1501d",
"backend": "ring",
"hosts": [
{ "node_id": "remotemac.local", "ssh": "127.0.0.1", "ips": ["10.9.9.1"], "rdma": [] },
{ "node_id": "mm4deMac-mini.local", "ssh": "mm4deMac-mini.local","ips": ["10.9.9.2"], "rdma": [] }
],
"assignments": [
{ "node_id": "remotemac.local", "rank": 0, "start_layer": 24, "end_layer": 47, "layer_count": 23,
"planned_weight_bytes": 9448926464, "capacity_bytes": 14495514624 }
]
}
注意
assignments里rank: 0(协调者)持有的是 24–47(后段层),rank: 1持有 0–24(前段层)。这是 oMLX 的固定约定。
9.2 停止与重启集群
- 停止:集群页「CLUSTER LIVE」区域的 Stop 按钮。
- 改配置后:如果模型之前是单机加载的,先卸载再重新加载,新的部署才会生效。
- 重新加载模型:
curl -s -X POST http://127.0.0.1:8000/v1/chat/completions \
-H 'Authorization: Bearer <API密钥>' \
-d '{"model":"GLM-4.7-Flash-4bit","messages":[{"role":"user","content":"ping"}],"max_tokens":1}'
9.3 客户端接入
集群对外就是一个标准 OpenAI 接口:
Base URL : http://127.0.0.1:8000/v1
API Key : 你在 oMLX 里设置的密钥
Model : GLM-4.7-Flash-4bit
只有 Rank 0(协调者)的 8000 端口需要被访问,工作节点不对外暴露端口。
如果想让局域网里别的设备也能用,需要在 设置 → 服务器 → 主机 改成 对外开放(0.0.0.0),并且必须先配置 API 密钥(oMLX 拒绝在没有密钥的情况下绑定非本地地址)。
9.4 重启后的检查清单
# 1. 雷电链路
ping -c 3 10.9.9.2 # 期望 0% 丢包,<1ms
# 2. 路由确实走雷电口
route -n get 10.9.9.2 | grep interface # 期望 bridge0
# 3. 双向 SSH
ssh mm4deMac-mini.local whoami # 期望 mm4
# 4. 两端 oMLX 都在跑
curl -s -o /dev/null -w "%{http_code}\n" http://127.0.0.1:8000/admin/dashboard # 期望 401(活着但要登录)
# 5. Metal 上限
sysctl iogpu.wired_limit_mb # 期望 14336
# 6. 防睡眠
pmset -g assertions | grep PreventUserIdleSystemSleep # 期望 1
第 10 章 参考资料
官方文档
| 资料 | 地址 |
|---|---|
| oMLX 分布式推理文档 | docs/distributed-cluster.md(仓库 github.com/jundot/omlx) |
| oMLX 架构说明(DeepWiki) | https://deepwiki.com/jundot/omlx/14-distributed-cluster-inference |
| Apple TN3205 · RDMA over Thunderbolt | https://developer.apple.com/documentation/technotes/tn3205-low-latency-communication-with-rdma-over-thunderbolt |
| WWDC26 · 用 MLX 做分布式推理与训练 | https://developer.apple.com/videos/play/wwdc2026/233/ |
官方文档里几条值得单独记住的话
Requirements — On every Mac:
- Run the same oMLX build and matching MLX/MLX-LM versions.
- Keep the downloaded model at the same absolute path.
- Enable Remote Login and use key-based SSH for the coordinator account.
- Pair the Macs in oMLX.
- For JACCL, configure Thunderbolt RDMA outside oMLX and confirm
rdma_ctl statusandibv_devicesreport the link.
Activation is lazy: it records an approved deployment and starts ranks when oMLX next loads that model.
(激活是"惰性"的:它只记录一份已批准的部署,等到 oMLX 下次加载该模型时才真正启动各个 rank。)
DFlash, SpecPrefill, MTP, VLM MTP, TurboQuant KV, thinking budgets, guided grammar, and
logit_biasare rejected for a distributed deployment rather than ignored.(这些特性在分布式部署下会被明确拒绝,而不是被静默忽略。)
The first GUI activation flow intentionally supports two Macs. The schema, planner, launcher, and runtime support up to 64 ranks; a multi-peer GUI is follow-up work.
(目前图形界面的激活流程只支持两台 Mac;底层数据结构支持到 64 个 rank,多节点的界面是后续工作。)
有用的命令行工具速查
| 命令 | 用途 |
|---|---|
system_profiler SPThunderboltDataType |
看雷雳口连接状态和速率 |
ifconfig bridge0 |
看雷雳桥接的 IP 和成员口 |
route -n get <IP> |
看某个 IP 走哪个网卡 |
networksetup -getinfo "Thunderbolt Bridge" |
看雷雳桥接的 IP 配置 |
sysctl iogpu.wired_limit_mb |
看/改 Metal 内存上限 |
pmset -g / pmset -g assertions |
看睡眠设置 / 防休眠断言 |
vm_stat |
看内存各页状态(free / inactive / active / wired) |
lsof -nP -iTCP:8000 -sTCP:LISTEN |
看谁占着 8000 端口 |
ssh-keygen -lf ~/.ssh/omlx_cluster.pub |
看 oMLX 专用密钥指纹(两台比对用) |
附:最短路径速查
如果你已经看过一遍,想快速复现,按这个顺序走:
① 插雷雳线 → system_profiler SPThunderboltDataType 确认 40 Gb/s
② 两端固定 IP → sudo networksetup -setmanual "Thunderbolt Bridge" 10.9.9.1/10.9.9.2 255.255.255.0
③ 两端关睡眠 → sudo pmset -a sleep 0 displaysleep 0 powernap 0
④ 两端抬 Metal 上限 → sudo sysctl iogpu.wired_limit_mb=14336 → 重启 oMLX
⑤ 两端 设置→高级→开启「分布式推理」→ 重启 oMLX
⑥ 两端 内存守卫改「自定义」13.5 GB → 重启 oMLX
⑦ 模型拷到两端同一路径 → rsync over 雷电口 → 校验字节数
⑧ 两端配 ~/.ssh/config(用户名 + 雷电 IP)→ 双向 ssh 裸主机名验证
⑨ 两端 authorized_keys 互加 oMLX 公钥
⑩ 集群页:配 SSH 三步 → Peer SSH name 填 user@雷电IP + 两个 address → Check peer
⑪ 两台角色切 Headless → Measure devices
⑫ 选纯文本 LLM 模型 → Build plan → Start Cluster
⑬ curl /v1/chat/completions 验收
最容易翻车的三步是 ⑧、⑩、⑪——分别对应坑 3、坑 1、坑 4。
本教程基于 2026-09-14 在真实双 M4 Mac mini 环境(macOS 26.6.2 / oMLX 0.6.4)上的完整实测编写。所有报错文本、数值指标、界面截图均来自该次实测。