Skip to content

用 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 ContainerDocker Desktop
运行方式每个容器独立轻量 VM所有容器共享一个 Linux VM
隔离级别VM 级隔离Linux Namespace + 共享 VM
镜像格式OCIOCI
底层实现Virtualization FrameworkLinuxKit VM
平台Apple Silicon macOSWindows / macOS / Linux

快速安装并启动 ​

用 Homebrew 装最省事,也可以下 GitHub Release 里的签名 pkg:

bash
brew install --cask container

装完要先起系统服务。第一次没有默认内核,会提示从 Kata Containers 装一份,选 y 即可:

bash
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 指到了阿里云镜像站:

bash
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:

bash
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:17

Redis:

bash
container volume create redis-data

container run -d --name redis \
  -p 6379:6379 \
  -v redis-data:/data \
  redis:7-alpine

MinIO 的 API 和控制台分两个端口。这里演示挂目录,宿主机目录要先建好:

bash
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 这样的名字:

bash
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 的对应关系:

想做的事DockerApple Container
列出容器(含已停止)docker ps -acontainer ls --all
跟踪日志docker logs -fcontainer logs -f
查看详情docker inspectcontainer inspect
删除容器docker rmcontainer delete
拉镜像docker pullcontainer image pull
搜索镜像docker search无,Docker Hub 上查
多容器编排docker compose无

平时就这些:

bash
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 和内存按机器给:

bash
container builder start --cpus 8 --memory 32g
container builder status

只给自己用,打本机架构就够了:

bash
container build -f docker/Dockerfile.prod -t my-app:prod .

服务器或 CI 是 amd64 时,一次打两个架构:

bash
container build --arch arm64 --arch amd64 -t web-test --file Dockerfile .

amd64 默认走 Rosetta。只要原生 arm64、不想要翻译层,就关掉它:

bash
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 镜像互通。

diagram showing container functional organization

挂单个配置文件报 VZErrorDomain ​

我踩得最狠的一个坑在 0.6.0 上:想把自己的 postgresql.conf 挂进容器,结果启动直接失败:

bash
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 是线索。我挨个看了两个挂载源:

bash
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。

了解更多 ​

总访问量次
总访问人数 人次