博客 · Infrastructure
如何搭建 Mac mini 集群
Mac mini 集群搭建实用指南:节点角色规划、用 Brewfile 和 Ansible 统一配置、SSH 密钥、Runner 标签、监控与分批更新。

Mac mini 集群好不好用,关键在于一致性。本文介绍如何搭建一个可以随时重建、扩容和更新的 Mac mini 集群。
先规划节点角色,再开始配置
先列出这些机器到底要做什么。大多数小型集群最终只有两三种角色,提前写清楚可以省去后期大量返工。
- 构建节点:运行 CI 任务,包括 Xcode 构建、单元测试、模拟器 UI 测试、归档与签名。需要足够的磁盘空间存放 DerivedData 和模拟器运行时。
- 工具节点:承载共享服务,例如依赖缓存、制品存储、监控采集。一台普通配置的机器通常就够了。
- 推理或批处理节点:运行本地 LLM 服务、媒体处理等长时间任务,最看重内存。
按角色选择硬件。对很多 iOS 项目来说,16GB 的 Mac mini M4 或 M6 就是合适的构建节点。大型应用、并行模拟器和较重的 Swift 编译更适合 24GB 的 Mac mini M6 或 M5 Pro。如果某个角色需要大得多的内存,比如运行 30B 或 70B 级模型,那就该考虑 Mac Studio 集群了。
同一角色尽量使用相同配置。节点一致,调度就简单:任何任务都能跑在任何带对应标签的机器上。
节点命名与 SSH 访问
选一个能体现角色和序号的命名规则,并同时设置 macOS 的三个名称,避免工具之间不一致:
sudo scutil --set ComputerName mac-ci-01
sudo scutil --set HostName mac-ci-01
sudo scutil --set LocalHostName mac-ci-01
接着让管理机通过密钥登录每个节点。建议为集群管理单独生成一把密钥,不要复用个人密钥:
ssh-keygen -t ed25519 -f ~/.ssh/mac-cluster -C "cluster-admin"
ssh-copy-id -i ~/.ssh/mac-cluster.pub admin@mac-ci-01
然后在 ~/.ssh/config 中添加配置,让包括 Ansible 在内的所有工具使用同一套设置:
Host mac-ci-*
User admin
IdentityFile ~/.ssh/mac-cluster
ServerAliveInterval 30
密钥登录确认可用后,在 /etc/ssh/sshd_config 中关闭密码登录(设置 PasswordAuthentication no),并保留至少一种备用访问方式,例如屏幕共享。如果你还不熟悉 macOS 远程访问,可以先看如何访问远程 Mac。
用同一种方式配置每个节点
Mac mini 集群里构建不稳定,最常见的原因是配置漂移:某个节点的 Xcode、Ruby 或 CocoaPods 版本和其他节点不同。解决办法是使用可以反复执行的声明式配置。
第一层:Brewfile
Homebrew Bundle 会安装 Brewfile 中列出的所有软件,已安装的会自动跳过。把它提交到所有节点都能读取的仓库:
# Brewfile
brew "git"
brew "jq"
brew "xcodes"
brew "swiftlint"
brew "node_exporter"
cask "temurin"
brew bundle --file=./Brewfile
brew bundle check --file=./Brewfile
brew bundle check 适合放进 CI 健康检查:节点一旦漂移,它会返回非零退出码。
第二层:Xcode 与系统设置
Xcode 不是普通的 Homebrew 软件包。可以用 xcodes 安装指定版本,再显式切换:
xcodes install 16.4
sudo xcode-select -s /Applications/Xcode-16.4.app
sudo xcodebuild -license accept
xcodebuild -runFirstLaunch
把电源和睡眠设置写进同一个脚本,避免构建途中节点进入睡眠:
sudo pmset -a sleep 0 displaysleep 0 disksleep 0
第三层:通过 SSH 使用 Ansible
机器超过两三台后,就应该从一个地方统一执行。Ansible 在 macOS 上很好用,因为目标机只需要 SSH 和 Python。一个最小的清单和 playbook:
# inventory.ini
[build]
mac-ci-01
mac-ci-02
mac-ci-03
[utility]
mac-util-01
# site.yml
- hosts: all
tasks:
- name: Copy Brewfile
ansible.builtin.copy:
src: Brewfile
dest: /Users/admin/Brewfile
- name: Install packages
ansible.builtin.command: /opt/homebrew/bin/brew bundle --file=/Users/admin/Brewfile
register: bundle
changed_when: "'Installing' in bundle.stdout"
ansible-playbook -i inventory.ini site.yml --limit build
--limit 参数后面会很重要:分批更新就靠它把变更只推送到部分节点。
共享缓存,但不共享状态
缓存能显著加快集群速度,但共享可变状态会带来最难排查的问题。一条好用的原则:共享下载内容,构建产物留在本地。
- DerivedData 留在各节点本地。通过网络共享既慢又容易损坏。
- 依赖集中缓存。让 Swift Package Manager、CocoaPods、npm 或 Gradle 走工具节点上的缓存代理,或者使用 CI 系统自带、以 lockfile 为键的缓存。
- 启用 macOS 内容缓存(系统设置 › 通用 › 共享),同一网络里的其他 Mac 可以从工具节点获取 Apple 软件更新。
- 编译缓存:ccache 或 sccache 对 C、C++ 和 Rust 代码很有帮助,网络允许的话可以配共享后端。
节点之间能否通过内网互通,取决于托管方式。如果你在 Hatmac 租用并需要机器互联,请告诉我们你的需求。
在集群中分发任务
带标签的 CI Runner
CI 任务的调度交给 CI 系统。每个节点注册一个 Runner,用标签描述能力。以 GitHub Actions 自托管 Runner 为例:
./config.sh --url https://github.com/your-org/your-repo \
--token YOUR_TOKEN --name mac-ci-01 \
--labels macos,arm64,xcode-16.4 --unattended
./svc.sh install
./svc.sh start
工作流里写 runs-on: [self-hosted, macos, xcode-16.4] 即可。升级 Xcode 时,给已升级的节点加新标签,再逐步迁移工作流。GitLab Runner 的 tags 和 Jenkins agent 的 labels 也是同样的思路。详见 GitHub 自托管 Runner 官方文档,或我们的 GitHub Actions Mac Runner 页面。
分布式推理
把一个大模型拆到多台 Mac 上运行是可行的,但仍在发展中。llama.cpp 有 RPC 后端,MLX 也提供分布式原语,都能把计算分摊到多台机器。节点间的网络延迟会限制吞吐,也需要不少调优。对大多数团队来说,更简单的做法效果更好:每个节点运行一个完整模型,前面放一个负载均衡器,让每个请求只落在一台机器上。
监控基础
起步不需要庞大的监控栈。每个节点先关注四项:剩余磁盘、内存压力、CPU 负载、Runner 服务是否在线。
df -h /
memory_pressure | tail -1
uptime
launchctl list | grep actions.runner
需要看板时,上面 Brewfile 装好的 node_exporter 会暴露指标,可由工具节点上的 Prometheus 采集。最先要配的告警是磁盘空间不足:Mac 上的 CI 故障,多数是 DerivedData 和模拟器运行时把磁盘占满造成的。每周跑一次清理任务会很有帮助:
rm -rf ~/Library/Developer/Xcode/DerivedData/*
xcrun simctl delete unavailable
维护:逐个节点分批更新
不要一次更新所有节点。分批推进可以让集群在你验证变更时继续处理任务。
- 摘下一个节点。暂停它的 Runner 或去掉标签,不再接新任务。
- 更新它。先查看再安装:
softwareupdate -l,然后sudo softwareupdate -i -a -R。Xcode 变更通过 playbook 完成。 - 跑一次金丝雀任务。只在这个节点上触发一次真实的构建和测试。
- 逐步推进。使用
ansible-playbook --limit mac-ci-02等命令,每次一到两个节点。 - 保留回退余地。新版本稳定运行几天之前,至少留一个节点在旧版本。
在清单中记录每个节点的 macOS 和 Xcode 版本,任何人都能一眼看清集群状态。
自建还是租用硬件
上述步骤在自有机器和租用机器上完全一样。租用省去了上架、供电和硬件故障处理。Hatmac 的每台机器都是独享的物理 Mac,不是虚拟机,拥有完整管理员权限,所以本文的配置步骤可以直接使用。Mac mini M4 和 M6 支持按月租用,租期越长月均越便宜,详见当前价格。想全面比较利弊,可以阅读租还是买 Mac mini。
常见问题
Mac mini 集群至少需要几台?
实际最少两台:一台摘下来更新时,另一台继续跑任务。CI 排队时间变长时再加节点。
用 Ansible 还是 shell 脚本?
两三台机器用脚本就够了。需要针对部分节点执行、并且能安全地重复执行变更时,Ansible 的优势就体现出来了。
一个集群里能混用不同型号的 Mac mini 吗?
可以。用 Runner 标签标明内存和芯片,让较重的任务落到配置更高的机器上。