Linux 上容器直接共享内核,macOS 上却必须先在虚拟机里跑一个 Linux,再把容器塞进去。这个架构性差异决定了本地开发的全部体验上限:文件挂载慢、内存占用高、启动要等半天,根因都在虚拟机与宿主之间的文件系统桥接。Docker Desktop、Colima、OrbStack 三条路线给出的答案不同,本篇把差异落到具体参数与实测数字上。
1. 三条路线的架构差异
Docker Desktop:
macOS → HyperKit/Virtualization.framework VM → dockerd → 容器
闭源 GUI + 商业许可(大企业需付费)
Colima:
macOS → Lima(QEMU 或 Virtualization.framework) → 发行版 VM → dockerd/containerd → 容器
开源,CLI 驱动,可脚本化
OrbStack:
macOS → 自研轻量 VM → dockerd → 容器
商业产品(个人免费),深度优化文件共享与启动速度
三者的共同点是「都要一个 Linux VM」,差异集中在三点:
| 维度 | Docker Desktop | Colima | OrbStack |
|---|---|---|---|
| 开源 | 否 | 是(MIT) | 否 |
| 许可成本 | 大企业需订阅 | 免费 | 个人免费,商用付费 |
| GUI | 完整 | 无(CLI) | 轻量 GUI |
| VM 后端 | Virtualization.framework | QEMU / vz | 自研 |
| 文件共享 | virtiofs / gRPC-FUSE | sshfs / virtiofs / 9p | 自研(宣称接近原生) |
| 内置 K8s | 是 | 是(k3s) | 是 |
| 内存占用 | 较高 | 中等 | 低 |
| 可脚本化 | 一般 | 强 | 中等 |
选择逻辑通常是:需要图形界面与团队支持选 Docker Desktop;需要纯 CLI、可纳入 dotfiles 管理选 Colima;在意挂载性能与内存占用、能接受商业许可选 OrbStack。
2. Colima:把运行时写进 dotfiles
Colima 本身不实现虚拟化,它是 Lima 的上层封装,负责在 VM 里装好 Docker 或 containerd 并暴露 socket。
2.1 安装与首次启动
brew install colima docker docker-buildx docker-compose
colima start
# 默认:2 CPU / 2GB 内存 / 60GB 磁盘 / QEMU 后端 / docker 运行时
默认参数对现代项目明显偏小,通常需要显式配置:
colima start \
--cpu 6 \
--memory 12 \
--disk 120 \
--vm-type vz \
--mount-type virtiofs \
--vz-rosetta \
--runtime docker
各参数含义:
| 参数 | 作用 | 建议 |
|---|---|---|
| –cpu / –memory | VM 资源上限 | 按宿主一半配置,避免与 IDE 抢内存 |
| –disk | 虚拟磁盘大小 | 按镜像数量估,扩容需重建 |
| –vm-type | QEMU 或 vz | Apple Silicon 一律用 vz |
| –mount-type | 挂载实现 | vz 后端配 virtiofs,性能最好 |
| –vz-rosetta | Rosetta 转译 x86 镜像 | Apple Silicon 跑 amd64 镜像时开启 |
| –runtime | docker / containerd | 用 docker CLI 就选 docker |
--vz-rosetta 是 Apple Silicon 上的关键开关:没有它,linux/amd64 镜像要靠 QEMU 全指令模拟,慢一个数量级;开启后由 Rosetta 做二进制转译,速度接近原生。
2.2 profile:多套环境并存
Colima 的 profile 机制允许同时跑多个 VM,各自绑定独立的 docker context:
# 默认 profile
colima start
# 建一个跑 K8s 的 profile
colima start k8s --kubernetes --cpu 4 --memory 8
# 列出与查看
colima list
colima status k8s
# 切换 context(Colima 会自动注册)
docker context ls
docker context use colima-k8s
这套机制在需要同时维护「普通 Docker 环境」与「K8s 环境」时特别有用,两个 VM 互不干扰,切换只是一条 docker context use。关于本地 K8s 与 Compose 的配合,可参考 Docker Compose 编排指南
。
2.3 生命周期与数据位置
colima stop # 停止 VM,容器随之停止
colima stop --force # 强制停止
colima delete # 删除 VM(数据全丢)
colima ssh # 进入 VM
VM 数据存放在 ~/.colima/<profile>/,镜像与卷都在虚拟磁盘内,删除 VM 等于清空所有镜像。这与 Docker Desktop 的数据位置不同,做备份或迁移时要注意。
2.4 用 containerd 运行时替代 dockerd
Colima 也支持把 VM 内的运行时换成 containerd,此时客户端换成 nerdctl:
colima start --runtime containerd
brew install nerdctl
nerdctl run --rm alpine echo hi
nerdctl -n k8s.io ps # 查看 K8s 命名空间下的容器
containerd 运行时省掉 dockerd 一层,VM 内存占用更低,适合只用 K8s 或只需跑容器的场景;代价是 docker 命令不可用,Compose 也要换成 nerdctl compose,且构建能力(nerdctl build)依赖 BuildKit 独立部署。
2.5 常见故障与处理
| 现象 | 原因 | 处理 |
|---|---|---|
colima start 卡在 provisioning | 拉取发行版镜像慢或网络受限 | 配置代理或换 --arch aarch64 基础镜像源 |
docker 命令连不上 socket | context 未切换或 DOCKER_HOST 残留 | docker context use colima,unset DOCKER_HOST |
| 容器内 DNS 解析失败 | VM 内 resolver 未同步宿主 | colima stop && colima start --dns 8.8.8.8 |
| 磁盘写满但宿主还有空间 | 虚拟磁盘是稀疏文件,需在 VM 内清理 | colima ssh -- sudo fstrim -av 后 colima stop |
| Rosetta 未生效 | 启动时未加 --vz-rosetta | 删除后重建,参数无法热加 |
# 查看 VM 内部磁盘占用
colima ssh -- df -h
# 清理未使用的镜像与卷
docker system prune -a --volumes
# 压缩虚拟磁盘(停止 VM 后)
colima stop && colima start
磁盘回收是容易被忽略的一项:虚拟磁盘只增不减,长期使用后 ~/.colima 可能涨到几十 GB,定期 fstrim 与 prune 是必要的维护动作。
3. OrbStack:为挂载性能做的取舍
OrbStack 的核心卖点是「用一个自研的轻量 VM + 自研文件共享协议」换掉通用方案的额外开销。它对外暴露的能力包括:
docker/kubectl/podman三个客户端共用一套后端。- 容器与 VM 的启动在秒级,空闲时内存占用显著低于通用 VM。
- 内置域名
*.orb.local,容器可直接用域名访问,无需查端口。 - 文件共享双向,支持 macOS 路径直接挂进容器。
brew install orbstack
orb start
docker context use orbstack
# 容器直接以域名访问
docker run -d --name web nginx
curl http://web.orb.local
# 也可以用 <container>.<machine>.orb.local 形式
3.1 orb.local 域名与端口
orb.local 解决的是「本地端口冲突」这个高频痛点:不再需要 -p 8080:80 并记住映射关系,直接访问容器名即可。若确实需要固定端口:
docker run -d -p 8080:80 --name web nginx
# 两种方式都能访问
curl http://web.orb.local
curl http://localhost:8080
3.2 内置 K8s
orb start k8s
kubectl get nodes
kubectl config use-context orbstack
OrbStack 的 K8s 是单节点集群,启动快、资源占用低,适合本地验证 manifest。它的定位不是替代 kind/k3d 的多节点测试,而是让「随手跑一下 K8s」的成本降到接近零。
3.3 Linux 机器与文件直访
OrbStack 还提供了一台常驻的 Linux 机器,可以从 macOS 直接访问其文件系统:
orb # 进入 Linux 机器 shell
orb -m ubuntu ls / # 在指定机器中执行命令
ls ~/OrbStack/ubuntu/ # 从 macOS 侧直接读写 VM 文件
这个能力在调试「容器内文件权限」类问题时很省事:不用 docker cp,直接在 Finder 或终端里看 VM 内的目录。Colima 对应的是 colima ssh,只能进 shell,没有宿主侧直访路径。
3.4 许可与团队使用
OrbStack 个人使用免费,商业环境需按席位付费。团队引入前建议先确认许可口径,尤其是 CI 机器人与远程开发机上是否属于商业使用。相比之下 Colima 是 MIT 许可,可以无顾虑地写进自动化脚本与内部镜像。
4. 实测差异:启动、内存与挂载
以下量级来自 M 系列芯片、16GB 内存的机器,具体数值会随项目与文件数量变化,但相对关系稳定。
| 指标 | Docker Desktop | Colima(vz+virtiofs) | OrbStack |
|---|---|---|---|
| 冷启动到可 run | 30~60s | 15~25s | 5~10s |
| 空闲内存占用 | 3~4GB | 1~2GB | 0.5~1GB |
| 大量小文件挂载(如 node_modules) | 慢 | 中等 | 快 |
首次 npm install(约 3 万文件) | 10~20 分钟 | 5~12 分钟 | 2~5 分钟 |
| x86 镜像运行 | QEMU 模拟 | Rosetta(需开启) | Rosetta |
| 磁盘镜像占用 | 较大 | 中等 | 较小 |
挂载性能的差异来自协议实现:gRPC-FUSE 逐次系统调用穿越 VM 边界,开销最大;sshfs 与 9p 类似;virtiofs 通过共享内存映射降低开销;OrbStack 的自研实现进一步做了元数据缓存。
4.1 规避挂载慢的工程手段
无论用哪个运行时,下面三条都能显著改善体验:
- 把依赖目录放进卷而非绑定挂载:
# compose.yaml
services:
web:
image: node:22
volumes:
- .:/app
- node_modules:/app/node_modules # 匿名卷屏蔽宿主目录
volumes:
node_modules:
- 在容器内构建,不把宿主构建产物带进去,避免宿主与容器两套二进制混用。
- 只在需要热重载的目录做绑定挂载,其余(如静态资源、数据文件)打进镜像。
5. 迁移与团队统一
切换运行时本质上只是换一个 docker context,但有几处细节会咬人。
5.1 context 与 DOCKER_HOST
docker context ls
# NAME DESCRIPTION DOCKER ENDPOINT
# default Current DOCKER_HOST based conf unix:///var/run/docker.sock
# colima colima unix:///Users/me/.colima/default/docker.sock
# orbstack OrbStack unix:///Users/me/.orbstack/run/docker.sock
docker context use orbstack
export DOCKER_HOST=unix:///Users/me/.orbstack/run/docker.sock # 需要时显式指定
注意 DOCKER_HOST 环境变量会覆盖 context。如果 shell 配置里残留了指向 Docker Desktop 的 DOCKER_HOST,docker context use 会看似无效——先 unset DOCKER_HOST 再排查。
5.2 团队统一的做法
本地运行时不一致会导致「我这儿能跑」的老问题,建议在仓库里放一份检测脚本,而不是强求所有人用同一个产品:
#!/usr/bin/env bash
set -euo pipefail
ctx=$(docker context show)
echo "docker context: $ctx"
docker info --format 'runtime={{.OperatingSystem}} driver={{.Driver}} cgroup={{.CgroupVersion}}'
docker run --rm alpine uname -m
关键是对齐三件事:
| 项目 | 为什么重要 |
|---|---|
| 默认平台 | Apple Silicon 本地默认 arm64,CI 常是 amd64,需显式 --platform |
| cgroup 版本 | 本地与生产不一致会导致内存限制行为不同 |
| 镜像 tag 策略 | 本地 latest 与 CI 的 SHA tag 混用会掩盖问题 |
跨架构构建时统一用 --platform 或 Bake 里声明的 platforms,可参考 多平台镜像构建与 Buildx
。
5.3 回退到 Docker Desktop
回退是幂等的:
colima stop
docker context use desktop-linux
# 或
unset DOCKER_HOST
open -a Docker
Colima 的 VM 数据不会被删除,随时 colima start 可恢复;OrbStack 同理。因此「先试两周再决定」是低风险策略。
6. 与 CI 和生产的一致性
本地运行时只影响开发体验,不改变镜像内容——但有两个例外会真实影响产物:
- 构建平台。本地 arm64 构建的镜像推到 amd64 集群会失败或走模拟,必须在构建时声明
--platform linux/amd64,linux/arm64并推 manifest list。 - 构建缓存。本地 BuildKit 缓存与 CI 缓存互不共享,若依赖
--cache-from type=gha之类的后端,本地构建的缓存对 CI 无帮助,反之亦然。CI 侧在容器内构建的常见形态见 GitHub Actions 容器作业 。
# 本地也走与 CI 相同的 builder 与缓存后端
docker buildx create --name ci --driver docker-container --use
docker buildx bake --set '*.cache-from=type=registry,ref=reg/app:cache'
把本地构建也纳入同一套 Bake 配置,能让「本地能构建、CI 失败」的概率降到最低;相关配置方法见前述 Bake 相关章节。开发环境本身的声明式管理(dotfiles、Homebrew Bundle、Nix)可参考 nix-darwin 与 macOS 声明式配置
,把 colima start 的参数固化进配置文件,避免换机重装时凭记忆敲命令。
6.1 平台不匹配的告警怎么读
本地跑 amd64 镜像时最常见的提示是:
WARNING: The requested image's platform (linux/amd64) does not match the detected host platform
(linux/arm64/v8) and no specific platform was requested
这条告警说明容器正在走模拟或转译执行,性能与行为都可能与生产不同。处理方式有两种:明确接受模拟并保持原样(适合一次性验证),或拉取对应架构的镜像:
docker pull --platform linux/arm64 nginx:1.27
docker run --platform linux/arm64 nginx:1.27
多架构镜像本身包含两种平台的 manifest,docker pull 默认按宿主架构选择,因此只要镜像提供 arm64 变体,这条告警就不会出现。
7. 小结
macOS 上选本地容器运行时,本质上是在「许可成本」「挂载性能」「可脚本化」三者之间排序:
- 要纯 CLI、可纳入 dotfiles、零成本,选 Colima,并务必开启
--vm-type vz与--vz-rosetta。 - 要最快的挂载与最低的内存占用,且能接受商业许可,选 OrbStack。
- 团队已有 Docker Desktop 授权与统一运维,继续用即可,收益不足以支撑迁移成本。
无论选哪个,都建议做两件事:把运行时的启动参数写进版本控制的脚本,以及在 CI 中用相同的平台与缓存配置构建,让本地与流水线的差异只剩「VM 快慢」这一项无关紧要的变量。
继续阅读
探索更多技术文章
浏览归档,发现更多关于系统设计、工具链和工程实践的内容。