用 Apple Container 在 Mac 上跑本地中间件
用比 Docker Desktop 更轻的 Apple Container,在 Apple Silicon Mac 上跑 Postgres、Redis、MinIO;覆盖快速安装、本地域名、跨平台构建,以及单文件挂载触发 VZErrorDomain 的踩坑。
本地开发离不开 Postgres、Redis、MinIO 这些中间件。以前我用 Docker Desktop,它能用,但整机常驻一个 Linux VM,占内存,开机也慢。我需要的其实很简单:本机起几个中间件,偶尔打个镜像。
Apple 在 WWDC 2025 开源了 Apple Container。CLI 叫 container,只跑在 Apple Silicon macOS 上,每个容器各起一台轻量 Linux VM。用了一段时间,对我这种场景刚好够用。
和 Docker Desktop 比,主要差在这一块:
| 对比项 | Apple Container | Docker Desktop |
|---|---|---|
| 运行方式 | 每个容器独立轻量 VM | 所有容器共享一个 Linux VM |
| 隔离级别 | VM 级隔离 | Linux Namespace + 共享 VM |
| 镜像格式 | OCI | OCI |
| 底层实现 | Virtualization Framework | LinuxKit VM |
| 平台 | Apple Silicon macOS | Windows / macOS / Linux |
快速安装并启动
用 Homebrew 装最省事,也可以下 GitHub Release 里的签名 pkg:
brew install --cask container装完要先起系统服务。第一次没有默认内核,会提示从 Kata Containers 装一份,选 y 即可:
container system start
# Verifying apiserver is running...
# No default kernel configured.
# Install the recommended default kernel from [https://github.com/kata-containers/kata-containers/releases/download/3.17.0/kata-static-3.17.0-arm64.tar.xz]? [Y/n]: y
# Installing kernel...
container --version
# container CLI version 0.5.0 (build: release, commit: 48230f3)国内拉镜像慢,我把默认 registry 指到了阿里云镜像站:
container system property set registry.domain mirror.aliyuncs.com
# 恢复官方:container system property set registry.domain docker.io升级不走 brew upgrade 那一套。先 container system stop,再跑 uninstall-container.sh -k(-k 保留用户数据),装好新版本后 container system start。-d 会连用户数据一起删,别手滑。
跑起本地 Postgres、Redis、MinIO
服务起来后,就可以跑中间件了。数据我只用两种方式存:named volume,或者挂目录。千万别把宿主机上的单个文件 -v 进去,原因见后面的踩坑。
Postgres 17,数据放 volume:
container volume create postgres-data
container run -d --name postgres \
-e TZ=Asia/Shanghai \
-e POSTGRES_USER=postgres \
-e POSTGRES_PASSWORD=postgres@2026 \
-e POSTGRES_DB=postgres \
-p 5432:5432 \
-v postgres-data:/var/lib/postgresql/data \
arm64v8/postgres:17Redis:
container volume create redis-data
container run -d --name redis \
-p 6379:6379 \
-v redis-data:/data \
redis:7-alpineMinIO 的 API 和控制台分两个端口。这里演示挂目录,宿主机目录要先建好:
mkdir -p ${HOME}/opt/minio/data
container run -d --name minio \
-e MINIO_ROOT_USER=minioadmin \
-e MINIO_ROOT_PASSWORD=minioadmin \
-p 9000:9000 \
-p 9001:9001 \
-v ${HOME}/opt/minio/data:/data \
minio/minio server /data --console-address ":9001"本机应用直接连 localhost:5432、6379、9000 就行。
用 *.local 访问容器,不用记 IP
容器多了,记 IP 很烦。container 自带 DNS,支持 <容器名>.<域名>。配一次 local,之后就能用 postgres.local 这样的名字:
sudo container system dns create local
container system property set dns.domain local
container run -d --name myapp nginx:latest
curl http://myapp.local这一步要 sudo,因为它会往 /etc/resolver 写配置,让 macOS 重载解析。
从 Docker 切过来,命令怎么对应
日常就那几个动作:看、查日志、停、删、拉镜像。和 Docker 的对应关系:
| 想做的事 | Docker | Apple Container |
|---|---|---|
| 列出容器(含已停止) | docker ps -a | container ls --all |
| 跟踪日志 | docker logs -f | container logs -f |
| 查看详情 | docker inspect | container inspect |
| 删除容器 | docker rm | container delete |
| 拉镜像 | docker pull | container image pull |
| 搜索镜像 | docker search | 无,Docker Hub 上查 |
| 多容器编排 | docker compose | 无 |
平时就这些:
container ls --all # 含已停止
container logs -f postgres
container inspect postgres # 启动参数、网络等
container stop postgres # 或 container stop --all
container delete postgres
container image pull redis:7-alpine
container image prune # 清未使用镜像
container image save my-app:latest -o my-app.tar.gz
container image load -i ./my-app.tar.gz推私有仓库也是熟悉的三步:container registry login、image tag、image push。
没有搜索命令这一点刚开始不太习惯。镜像名和 tag 我都是去 Docker Hub 上查。
本地构建,顺带打 amd64 镜像
除了跑中间件,我偶尔也在本机打镜像。构建前先起 builder,CPU 和内存按机器给:
container builder start --cpus 8 --memory 32g
container builder status只给自己用,打本机架构就够了:
container build -f docker/Dockerfile.prod -t my-app:prod .服务器或 CI 是 amd64 时,一次打两个架构:
container build --arch arm64 --arch amd64 -t web-test --file Dockerfile .amd64 默认走 Rosetta。只要原生 arm64、不想要翻译层,就关掉它:
container system property set build.rosetta false它为什么比 Docker Desktop 轻
轻,主要是因为架构不一样:Docker Desktop 是所有容器共享一个大 VM;Apple Container 用 Containerization(Swift 写的)管镜像和进程,通过 macOS Virtualization Framework 给每个容器单独起一台轻量 VM,不用的时候不占着。
CLI 通过 container-apiserver 和镜像、网络等 XPC 辅助进程通信,system start / system stop 就是启停这套服务。镜像进出都是标准 OCI,和 Docker 镜像互通。
挂单个配置文件报 VZErrorDomain
我踩得最狠的一个坑在 0.6.0 上:想把自己的 postgresql.conf 挂进容器,结果启动直接失败:
container run -d --name=postgres \
-e TZ=Asia/Shanghai \
-e POSTGRES_USER=postgres \
-e POSTGRES_PASSWORD=postgres@2026 \
-e POSTGRES_DB=wyhao_admin \
-p 5432:5432 \
-v ${HOME}/opt/postgres/conf/postgresql.conf:/etc/postgresql/postgresql.conf \
-v ${HOME}/opt/postgres/data:/var/lib/postgresql/data \
arm64v8/postgres:17
# Error: ... Error Domain=VZErrorDomain Code=2
# "A directory sharing device configuration is invalid."
# ... NSPOSIXErrorDomain Code=20 "Not a directory"报错里的 Not a directory 是线索。我挨个看了两个挂载源:
ls -ld ${HOME}/opt/postgres/conf/postgresql.conf
# -rwxrwxrwx ... postgresql.conf ← 文件
ls -ld ${HOME}/opt/postgres/data
# drwxrwxrwx ... data ← 目录问题就在第一个。Virtualization 的目录共享只认目录,把单个文件当成共享设备,就会报 Not a directory。解决办法:数据用 container volume,配置挂所在目录,再在容器里引用目录下的文件。
哪些场景还得用 Docker
除了上面的坑,下面这些是我实际碰到的边界:
- 只支持 Apple Silicon macOS,Intel Mac 和 Windows 用不了。
- 没有 Compose。中间件多了,就是一条条
container run;依赖编排的项目还是用 Docker Compose。 - 没有镜像搜索命令。
- 升级、卸载走自带脚本,和
brew upgrade的习惯不一样。
所以我的用法是:本机起几个中间件、偶尔打镜像,用 Apple Container;需要 Compose 一键起整套环境的,继续用 Docker。