优先 arm64,必要时用 amd64
M 系列 Mac 在 arm64 Linux 内核上运行 Linux 容器。拉取多架构镜像(postgres、redis、node、python 及大多数官方镜像)时,Docker 会自动选择 arm64 版本,以原生速度运行。
有些镜像仍仅提供 amd64 版本:较旧的内部镜像、厂商工具,以及在 Intel 笔记本或未指定平台标志的 CI 运行器上构建的镜像。对于这些镜像,请明确指定:
services:
legacy-tool:
image: example/amd64-only-tool:1.4
platform: linux/amd64 Oxcart 通过 Apple 的 x86-64 转译技术 Rosetta 运行 amd64 镜像;Buildx 可构建并加载 amd64 镜像,再由引擎在本地运行。可用 docker run --rm --platform linux/amd64 alpine uname -m 检查容器实际运行的架构,它会输出 x86_64。
| 情况 | 处理方法 |
|---|---|
| 镜像提供 arm64 版本 | 无需操作。Compose 会拉取 arm64 版本。 |
| 镜像仅支持 amd64 | 为服务添加 platform: linux/amd64。 |
| 部署到 amd64 服务器 | 使用 docker buildx build --platform linux/amd64 构建要推送的镜像;如服务栈允许,本地开发仍可使用 arm64。 |
| "exec format error" | 二进制文件与容器架构不匹配。请设置平台或为 arm64 重新构建。 |
完整示例
一个 Postgres 数据库、一个从当前文件夹构建且等待数据库健康后才启动的 API,以及一个通过 profile 启用的 amd64 专用工具:
services:
db:
image: postgres:17-alpine
environment:
POSTGRES_PASSWORD: dev
volumes:
- pgdata:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -U postgres"]
interval: 2s
timeout: 3s
retries: 15
api:
build: .
init: true
environment:
DATABASE_URL: postgres://postgres:dev@db:5432/postgres
ports:
- "127.0.0.1:8080:8080"
depends_on:
db:
condition: service_healthy
legacy-tool:
image: example/amd64-only-tool:1.4
platform: linux/amd64
profiles: ["tools"]
volumes:
pgdata: 各部分的作用:
healthcheck告诉 Compose,Postgres 何时真正开始接受连接,而非仅仅已启动。depends_on: condition: service_healthy让 API 等待,直到该检查通过。init: true以 PID 1 运行轻量 init,让信号能传递给进程,并回收僵尸进程。这样docker stop就能正常结束,而不必等待超时。127.0.0.1:8080:8080仅在 Mac 的回环地址上发布 API。若不指定地址,发布的端口会监听 Mac 的所有 IPv4 地址,同一网络中的其他机器也能访问。pgdata是命名卷:数据在docker compose down后仍保留,仅执行down -v时才删除。
在 Apple 芯片上运行 Compose 服务栈
- 检查工具。 运行
docker compose version和docker context show。Oxcart 随附 Docker Compose 5.6.0、docker CLI 29.9.0 和 Buildx 0.38.0,上下文名为oxcart。 - 编写 compose 文件。 为应用需要等待的每个服务添加
healthcheck,并通过depends_on: condition: service_healthy让依赖方等待。 - 启动并等待健康状态。 运行
docker compose up -d --build --wait。命令会在所有服务运行且健康后返回;如有服务未达到要求,则失败。 - 检查端口。 运行
docker compose ps,或打开 Oxcart 的 Ports 视图。该视图会显示每个已发布端口是就绪、等待中还是失败,并说明原因。 - 迭代开发。 编辑代码并运行
docker compose up -d --build api。未改变的构建步骤会使用缓存。 - 清理。 运行
docker compose down停止并移除容器,或运行docker compose down -v一并删除命名卷。
更快地重新构建
Compose 通过 Buildx 和 BuildKit 构建。安排 Dockerfile 时,将依赖安装放在 COPY . . 之前,这样修改代码就能复用其上方的所有缓存层。在 Oxcart 中,基准测试项目的缓存构建为 121 ms,使用本地上下文的无缓存构建为 341 ms,两项均是我们测试的三款运行时中最快的。每次迭代节省的时间会不断累积:docker run --rm 用时 42 ms,停止并移除 20 个容器用时 77 ms。
Oxcart 0.1.2 于 2026-10-10 使用随附的 docker 29.9 客户端测量;其他运行时于同日较早时间,使用同一台 Mac、同一套测试工具和 docker 29.4 测量。Apple M5 Max,各配置 8 个 CPU 和 12 GiB,每次仅运行一款运行时。
端口、Mac 与其他服务
- 端口语法与 Docker 相同:支持 TCP 和 UDP、临时端口(
"127.0.0.1::8080"),以及用于同时监听 IPv4 与 IPv6 的"[::]:8080:80"。TCP 和 UDP 可以共用同一个主机端口号。 - 从容器访问 Mac:
host.docker.internal可从容器及构建步骤通过 TCP 访问绑定在 Mac127.0.0.1上的服务。 - 本地镜像仓库:将仓库容器发布到端口 5000,
docker push localhost:5000/app即可访问。 - 边车容器:
network_mode: service:api将容器放入另一个服务的网络命名空间,从而共享该服务的localhost。
关于 VPN、DNS、防火墙及网络路径如何受到保护,请参见 Mac 上的容器网络。
命名卷与绑定挂载
数据库或构建过程需要频繁写入的数据,请使用命名卷:例如 node_modules、软件包缓存和数据库文件。数据存储在引擎自身的存储空间中。在 Oxcart 上,命名卷完成 1,000 次小文件创建、stat、读取和删除操作仅用 17.8 ms,是我们测试的三款运行时中最快的。
在 Mac 上编辑的源代码请使用绑定挂载。Oxcart 默认以可读写方式将用户主文件夹、/private/tmp 和 /private/var/folders 共享给容器;Mac 上的修改会出现在容器中,容器中的修改也会出现在 Mac 上。需要注意一个限制:在 Mac 上修改文件不会在容器内产生 inotify 事件,因此容器内的文件监视器如需感知编辑器的修改,应使用轮询。
故障排查
| 现象 | 原因 | 解决方法 |
|---|---|---|
exec format error | arm64 容器中运行 amd64 二进制文件,或反过来 | 设置 platform: linux/amd64,或为 arm64 重新构建 |
| "no matching manifest for linux/arm64" | 镜像没有 arm64 版本 | platform: linux/amd64 |
| "address already in use" | 另一个进程或容器占用了主机端口 | 更换主机端口,或在 Oxcart 的 Ports 视图中查找占用者 |
| 已发布端口拒绝连接 | 服务器在容器内监听 127.0.0.1,或尚无程序监听 | 监听 0.0.0.0;添加健康检查并使用 --wait |
| 数据库就绪前应用已启动 | 未指定条件的 depends_on 只等待启动 | 同时使用 condition: service_healthy 与 healthcheck |
docker stop 等待 10 s | PID 1 忽略 SIGTERM | 使用 init: true,或在应用中处理 SIGTERM |
常见问题
Docker Compose 支持 Apple 芯片 Mac 吗?
支持。Compose 可在 arm64 上原生运行,大多数官方镜像都提供 arm64 版本。Oxcart 包含 Docker Compose 5.6.0,兼容性检查覆盖多服务、健康检查依赖、up --wait、build、down、服务名 DNS、init: true 和 network_mode: service:。
如何在 M 系列 Mac 上运行 amd64(x86-64)镜像?
为服务设置 platform: linux/amd64,或向 docker run 传入 --platform linux/amd64。Oxcart 通过 Rosetta 运行 amd64 镜像。有原生 arm64 镜像时优先使用;没有 arm64 构建时再用 amd64。
“exec format error” 是什么意思?
容器尝试运行与自身 CPU 架构不同的二进制文件。通常是镜像或其中的二进制文件仅支持 amd64。请为该服务添加 platform: linux/amd64,或将镜像重新构建为 arm64。
容器正在运行,为什么应用仍提示“connection refused”?
最常见的原因是服务器在容器内监听 127.0.0.1。已发布端口连接的是容器网络接口,而非回环地址,因此服务器必须监听 0.0.0.0。Oxcart 的 Ports 视图会明确指出此情况。
如何让一个服务等待另一个服务就绪?
为被依赖服务添加 healthcheck,为依赖它的服务添加 depends_on: { db: { condition: service_healthy } }。这样 docker compose up --wait 就只会在所有服务都健康后返回。
容器能访问 Mac 上运行的服务器吗?
可以。使用 host.docker.internal:在 Oxcart 中,容器和构建过程可通过它以 TCP 访问绑定在 Mac 127.0.0.1 上的服务。
数据库应使用命名卷还是绑定挂载?
数据库数据请使用命名卷,存储在引擎自身的存储空间内。Mac 上编辑的源代码使用绑定挂载。在 Oxcart 上,命名卷执行 1,000 次小文件创建、stat、读取和删除操作用时 17.8 ms。
来源与方法
- 兼容性:Oxcart 已验证的集成路径(docker CLI、带健康检查依赖的 Compose、
up --wait、init: true、network_mode: service:、Buildx,以及通过 Rosetta 运行 amd64)。 - 速度数据:Oxcart 0.1.2,2026-10-10,Apple M5 Max,8 个 CPU 和 12 GiB,预热后采样五次取中位数;其他运行时于同日较早时间,使用同一台 Mac、同一套测试工具和 docker 29.4 客户端测量。
- 随附工具:docker 29.9.0、Docker Compose 5.6.0、Buildx 0.38.0。