你的仓库里同时躺着一个 iOS App 和它的后端服务:后端用 docker-compose 拉起数据库、消息队列和几个微服务,集成测试要真的连上这些容器才能跑。iOS 部分租了一台 HireVPS 的云端 Mac mini 来做 Xcode 构建,一切都很顺,直到你想让 CI 在同一台机器上顺手跑一遍后端集成测试——才发现 Apple Silicon 上没有原生 Linux 内核,docker run 直接报错找不到守护进程。这不是配置问题,是架构问题:Docker 在 macOS 上从来就是"借壳"运行的。
场景:为什么在云端 Mac 上也要跑 Linux 容器
monorepo 越来越常见,一次 PR 可能同时改动 iOS 客户端和后端接口,只测 iOS 侧根本发现不了协议不匹配的问题。理想的流水线是:同一台 runner 上,先用 Xcode 构建并跑单元测试,再拉起 docker-compose 定义的后端依赖,跑一轮端到端集成测试,最后统一上报结果。这样做的好处很直接——不用维护两套 CI 环境,also 不用在网络策略上为跨机器通信开洞。前提是这台云端 Mac 得能跑 Linux 容器。
Apple Silicon 上没有原生的 Linux 内核,任何"跑 Docker"方案本质上都是先启动一台隐藏的 Linux 虚拟机——性能好坏几乎全看这台虚拟机分到多少 CPU 和内存。
准备工作:选一套轻量虚拟化方案
macOS 上能跑容器的路子主要两条:Docker Desktop 和 Colima(配合原生 docker CLI)。两者底层都基于 Apple 的 Virtualization.framework 启动一台精简 Linux VM,区别在形态和适配场景。
Docker Desktop 与 Colima 的取舍
| 维度 | Docker Desktop | Colima |
|---|---|---|
| 交互形式 | 图形界面 + 后台守护 | 纯命令行 |
| 启动方式 | 手动或登录时自启 | 脚本一条命令拉起 |
| 资源占用 | 较高,常驻多个进程 | 轻量,按需启停 |
| 适合场景 | 本地日常调试 | 无人值守的 CI runner |
| 授权限制 | 企业规模需付费许可 | 开源免费 |
对跑在无人值守 runner 里的 CI 来说,Colima 明显更合适:它不依赖图形会话,可以在 shell 脚本里一条命令拉起、跑完就关,不会有孤儿进程占着内存。
安装与资源分配
在云端 Mac mini 上,先用 Homebrew 装好工具链:
brew install colima docker docker-compose docker-buildx
colima start \
--cpu 4 \
--memory 12 \
--disk 60 \
--vm-type=vz \
--mount-type=virtiofs
docker context use colima
docker compose -f docker-compose.ci.yml up -d --wait
--vm-type=vz 用的是 Apple 原生虚拟化后端(比早期的 QEMU 模式快不少),--mount-type=virtiofs 让容器挂载的项目目录读写性能接近本机磁盘,这两个参数不加的话,构建速度和文件 I/O 都会明显打折。
资源分配没有万能公式,但有个经验起点:给 Colima 的虚拟机留主机内存的 50%~70%,CPU 核心数留一半左右给 Xcode 构建用。按机型内存对照,大致可以这样起步(具体以控制台确认当前可选配置为准):
| 机型内存 | Colima 建议内存 | Colima 建议 CPU |
|---|---|---|
| 16GB | 6GB | 2 核 |
| 32GB | 12GB | 4 核 |
| 48GB | 20GB | 6 核 |
| 64GB | 28GB | 8 核 |
如果 Xcode 构建和 Colima 同时跑,内存分配过高会让两边互相抢占导致 swap,构建时间反而变长——这个平衡点值得在正式接入 CI 前用一次完整流水线跑几遍来校准。
接入 CI:让 self-hosted runner 认得 Linux 容器
如果 runner 是 GitHub Actions self-hosted 类型,在 job 里加一步启动 Colima、跑完测试再清理即可,不需要额外的服务容器声明:
jobs:
hybrid-ci:
runs-on: [self-hosted, macos, cloud-mac]
steps:
- uses: actions/checkout@v4
- name: Xcode build & unit test
run: |
xcodebuild -scheme App -destination "platform=iOS Simulator,name=iPhone 15" test
- name: Bring up backend containers
run: |
colima start --cpu 4 --memory 12 --vm-type=vz --mount-type=virtiofs
docker compose -f docker-compose.ci.yml up -d --wait
- name: Integration tests
run: ./scripts/run-integration-tests.sh
- name: Teardown
if: always()
run: |
docker compose -f docker-compose.ci.yml down -v
colima stop
if: always() 那一步很关键:即便测试失败也要把容器和虚拟机关掉,不然长租的云端 Mac 上会越积越多孤儿虚拟机,占着内存拖慢下一次构建。
缓存策略:镜像层与依赖复用
Colima 虚拟机重启后,如果每次都重新拉镜像、重新装依赖,CI 时间会被拉得很长。两个简单有效的做法:
- 镜像层缓存落盘:不要每次
colima delete,只用colima stop/colima start,虚拟机磁盘(默认在~/.colima)会保留已拉取的镜像层,重启后不用重新下载。 - 构建缓存挂载卷:后端 Dockerfile 里给
node_modules、vendor、.cargo等依赖目录声明具名 volume,配合docker compose的--wait,首次构建慢一点,后续增量构建能省下大半时间。
配合前面提到的 virtiofs 挂载,项目目录的读写基本不成为瓶颈,真正值得盯的是网络层——如果集成测试要访问外部 API,记得给测试环境单独配 mock 或沙箱地址,不要让 CI 悄悄打到生产接口。
踩坑清单与检查项
实际接入过程里容易栽的坑,按优先级列一遍:
- 忘记切 docker context:装了 Colima 之后如果之前用过 Docker Desktop,
docker命令可能还指向旧的 context,docker context use colima这一步不能省。 - VM 类型选错:老版本 Colima 默认可能用
qemu,在 Apple Silicon 上一定显式指定--vm-type=vz,性能差距明显。 - 磁盘无限增长:镜像层不清理会一直占用
--disk参数设的空间,建议 CI 流程里定期跑一次docker system prune -af --volumes。 - 多任务并发抢资源:如果同一台机器上还并行跑其他 Xcode 构建任务,Colima 的内存分配要相应下调,避免 OOM。
- 架构不匹配的镜像:后端如果引用了只支持 x86_64 的基础镜像,在 Apple Silicon 上会走 QEMU 模拟层,速度骤降,尽量确认镜像有原生
arm64变体。
把这套流程跑顺之后,同一台云端 Mac mini 就能同时扮演 iOS 构建机和后端集成测试环境,不用再为跨机器协调网络和凭据操心,PR 提交后一条流水线就能给出完整的通过/失败信号。
常见问题
Apple Silicon 的云端 Mac 能直接跑 Docker 吗?
不能原生跑,Docker Desktop、Colima 等方案本质都是先起一台隐藏的 Linux 虚拟机(基于 Virtualization.framework),容器实际跑在这台虚拟机里,所以性能取决于分给虚拟机的 CPU 和内存。
Colima 和 Docker Desktop 该选哪个接入 CI?
CI 场景优先选 Colima:它是命令行工具,资源占用小、启动快,适合脚本化在 runner 里自动拉起;Docker Desktop 更适合本地图形界面日常调试,两者可以并存但不建议在无人值守的 runner 上装 Docker Desktop。
机型内存不同,Colima 该怎么分配资源?
一般给 Colima 虚拟机留出主机内存的 50%~70%,比如 32GB 内存的机型可以给 Colima 分 12GB、4 核,具体配置以控制台确认当前可选配置后再压测调整为准。
HireVPS
今天就试试独享云 Mac mini
按天起租,2 分钟拿到 SSH/VNC 凭据,配置随时可升级。