
Apple Container Machine:把 Linux 搬进 Mac
TL;DR
container machine 是 Apple Container 里容易被忽视的另一半:不是跑一个进程,而是跑一个完整的 Linux 开发环境,自动挂载你的 macOS $HOME,支持 systemd,用法更接近 WSL2 而不是 Docker。
从货箱到书房:Container Machine 是什么
这个系列已经写了四篇:
- 苹果发布 Containerization Framework:开启 macOS 容器化新纪元——讲框架本身,为什么每个容器独立一个轻量 VM,和 Docker Desktop、Kata Containers 的架构差异
- Apple Container 开箱实践——Container CLI 入门,安装、拉镜像、
container run、DNS,对照 Docker CLI 逐条对比 - macOS 原生容器 Apple Container 架构解析——深入 Virtualization Framework、网络栈、文件系统的底层实现
- Apple Container 0.8.0:从初生到成熟的七个月演进之路——七个月九个版本的演进,0.7.1 数据完整性危机,以及生产就绪的分级评估
读完这四篇,你对 Apple Container 的印象大概是:每个容器独立一个 VM,container run nginx:latest 起来就是一个跑在 VM 里的 nginx 进程,用法和 Docker 接近。
但 Apple Container 从 0.1.0 开始就带着另一个子命令:container machine。它很少被提及,文档也相对简短。
container machine 做的事情,用一句话说是:把一个完整的 Linux 系统跑起来,而不只是跑一个进程。
具体来说,启动一个 container machine 时:
- VM 里运行的是镜像自带的 init 系统(比如
systemd),而不是苹果自己的极简vminitd - 你的 macOS 用户名和
$HOME目录自动挂载进去,不需要任何配置 - 进入环境后,你就是你——用户名不变,
/Users/<username>就是你在 Mac 上的家目录
这和普通容器有本质的区别。普通容器是应用模型:给一个进程一个隔离的执行环境,跑完就结束。container machine 是环境模型:把一个完整的 Linux 系统搬进来,Mac 是客厅,Linux 是书房,两间房之间的门永远开着。
在整个 Apple Container 系列里,这篇文章处理的是第四块拼图:框架介绍 → 普通容器入门 → 演进评估 → 开发环境。如果你在 Mac 上有「想直接用 Linux 工具链,但不想维护一台虚拟机」的需求,container machine 就是为这个场景设计的。
Container vs Container Machine:两种模式
二者底层用的是同一套引擎——都是 OCI 镜像,都是独立轻量 VM,都通过同一个 CLI 管理。区别只在启动时的引导方式:
container run→ 忽略镜像的 init,直接拉起 entrypoint 进程,跑完即止container machine create→ 把引导权交给镜像自带的 init 系统(如systemd),整个 Linux 环境活起来
同一个 Ubuntu 镜像,两种用法,两种结果。
container run | container machine | |
|---|---|---|
| 定位 | 应用模型:跑单个进程/服务 | 环境模型:完整 Linux 开发环境 |
| init 系统 | vminitd(Apple 自研,极简) | 镜像自带(如 systemd) |
macOS $HOME | 不挂载 | 自动挂载,零配置 |
| 用户身份 | root 或镜像内用户 | 自动匹配宿主机用户名 |
| 生命周期 | 进程结束即退出 | 持久化,显式 stop |
| 典型场景 | CI、跑服务、测试 | 日常开发环境 |
和 Docker 的关系
container run 和 docker run 在精神上是同一件事:进程/应用模型,无状态,entrypoint 驱动。如果你只用过这一半,觉得 Apple Container 就是「原生的 Docker 替代」,也没错——但你只用了一半。
container machine 反而更接近 WSL2(Windows)或 Lima(Mac 上的前辈方案):持久化的 Linux 环境,和宿主机文件系统打通,可以 systemctl 管服务。
和 VMware Fusion 的关系
表面上 container machine 和 VMware Fusion 最像——都跑完整 Linux,都有 systemd,都持久化。但有一个本质差异:
VMware Fusion 是历史积累型。你在里面装软件、改配置,VM 本身随时间变得越来越「你的」,状态不可复现,坏了就头疼。
container machine 是声明型。环境由 Dockerfile 定义,出问题了 container machine rm 删掉重建一个,和新的一模一样。你的文件在 Mac 的 $HOME 里,一根毛没少。
核心区别不是重量,是环境和数据的关系:VMware Fusion 把环境和数据混在一起,都住在 VM 里;container machine 把二者分离——环境是声明出来的,数据在 Mac 上。这一点,它更接近 devcontainer 的精神:用一个 Dockerfile 或配置文件描述开发环境,环境本身可抛弃、可重建、可分享给团队,代码和数据始终在宿主机上。
快速上手
前提是已经安装好 Apple Container 并启动了服务。如果还没安装,从 GitHub releases 页面 下载最新的签名安装包(当前为 1.0.0),双击安装后执行:
container system start
注意:官方目前只支持 macOS 26,macOS 15 上发现的问题不再修复。安装完整步骤可参考 开箱实践,但注意文中版本信息已过时。
创建并进入第一个 machine
container machine create alpine:latest --name dev
创建好之后,直接进交互 shell:
container machine run -n dev
进去之后跑两条命令验证一下:
whoami # 输出你的 macOS 用户名,不是 root
pwd # /Users/<你的用户名>,就是你的 Mac home 目录
这两行的输出结果,是 container machine 和普通容器最直观的差别——你不是在一个陌生的 Linux 里,你带着自己的身份和文件进来了。
也可以不进 shell,直接在 machine 里跑单条命令:
container machine run -n dev uname -a
container machine run -n dev -- cat /proc/cpuinfo # 命令带 flag 时加 --
container machine run启动时如果 machine 处于停止状态,会自动先把它启动起来,不需要手动start。
设置默认 machine
每次都带 -n dev 有点烦,可以设一个默认:
container machine set-default dev
之后直接 container machine run 就操作 dev,不需要再指定名字。
常用命令
生命周期管理
container machine ls # 列出所有 machine
container machine inspect dev # 查看详细信息(JSON)
container machine stop dev # 停止
container machine rm dev # 删除,同时清除持久化存储
调整资源
container machine set -n dev cpus=4 memory=8G
container machine stop dev
container machine run -n dev -- nproc # 验证生效
资源调整在下次启动后生效,改完记得 stop 再 run。默认内存是宿主机内存的一半。
挂载模式也可以调整,$HOME 的挂载方式支持三种:
rw(默认):可读写,Mac 和 Linux 两侧实时同步ro:只读,Linux 里不能写入 Mac 的文件none:不挂载,machine 和宿主机文件系统完全隔离
container machine set -n dev home-mount=ro
m 缩写
machine 有个别名 m,所有子命令都能用:
container m ls
container m run
container m stop dev
container m set -n dev cpus=4 memory=8G
敲起来省事不少,尤其是高频操作 container m run 几乎和 ssh 进虚拟机一样顺手。
自带 Linux 服务:systemd 实战
这是 container machine 和普通容器差别最大的地方。普通容器里你跑不起来 systemd——vminitd 只负责拉起 entrypoint,没有 service 管理的概念。但 container machine 用的是镜像自带的 init,只要镜像里有 /sbin/init,systemd 就能完整运行。
alpine 镜像没有 systemd,要体验这个能力需要自己构建一个支持 systemd 的镜像。官方文档给了一个 Ubuntu 24.04 的 Dockerfile:
FROM ubuntu:24.04
ENV container container
RUN apt-get update && \
apt-get install -y \
dbus systemd openssh-server net-tools iproute2 iputils-ping curl wget vim-tiny man sudo && \
apt-get clean && \
rm -rf /var/lib/apt/lists/* && \
yes | unminimize
RUN >/etc/machine-id
RUN >/var/lib/dbus/machine-id
RUN systemctl set-default multi-user.target
RUN systemctl mask \
dev-hugepages.mount \
sys-fs-fuse-connections.mount \
systemd-update-utmp.service \
systemd-tmpfiles-setup.service \
console-getty.service
RUN systemctl disable \
networkd-dispatcher.service
RUN sed -i -e 's/^AcceptEnv LANG LC_\*$/#AcceptEnv LANG LC_*/' /etc/ssh/sshd_config
几个关键点:
>/etc/machine-id和>/var/lib/dbus/machine-id:清空 machine-id,让系统首次启动时自动生成唯一 ID,避免多个 machine 共用相同标识systemctl mask:屏蔽掉几个在虚拟化环境里会导致启动卡住的 unitsystemctl set-default multi-user.target:不起图形界面,直接到命令行
构建并创建:
container build --dns 8.8.8.8 -t local/ubuntu-machine:latest .
container machine create local/ubuntu-machine:latest --name ubuntu
container machine run -n ubuntu
构建时容器内默认没有可用的 DNS,
apt-get update会因无法解析域名而失败。加上--dns 8.8.8.8可以解决。
进去之后,就是一个完整的 Ubuntu 环境,可以用 systemd 管理服务:
sudo systemctl start ssh
sudo systemctl status ssh
sudo systemctl start postgresql # 装好之后直接 start
和在一台真实的 Linux 机器上没有区别。
自定义镜像与用户初始化
镜像要求
任何 Linux 镜像,只要根目录下有 /sbin/init,就可以用作 container machine 的底座。不一定要 systemd,其他 init 系统(如 OpenRC)也行。
用户初始化
第一次启动时,container machine 会自动运行一个内置脚本,在 Linux 里创建和宿主机用户名、UID、GID 完全匹配的用户。这就是为什么进去之后 whoami 直接是你自己。
如果内置的初始化逻辑不够用——比如你想在用户创建的同时装 sudo 权限、写入 SSH 公钥、配置 dotfiles——可以在镜像里放一个自定义脚本:
/etc/machine/create-user.sh
这个脚本在首次启动时以 root 身份执行一次,可以拿到以下环境变量:
| 变量 | 含义 |
|---|---|
CONTAINER_USER | 用户名 |
CONTAINER_UID | 用户 ID |
CONTAINER_GID | 组 ID |
CONTAINER_HOME | home 目录路径 |
CONTAINER_MACHINE_ID | machine 标识符 |
一个实用的例子——创建用户并配置 sudo:
#!/bin/sh
useradd -m -u "$CONTAINER_UID" -g "$CONTAINER_GID" "$CONTAINER_USER"
echo "$CONTAINER_USER ALL=(ALL) NOPASSWD:ALL" >> /etc/sudoers
脚本只跑一次,之后重启不会重复执行。如果你想重置用户初始化,container machine rm 掉重建即可——这也是「声明型环境」的好处,状态清晰,没有历史包袱。
小结
container machine 和普通容器共用同一套引擎,但解决的是完全不同的问题。
普通容器的答案是:给一个进程一个干净的执行环境。container machine 的答案是:把 Linux 搬进来,Mac 上的文件不用动,身份不用切换,用完环境坏了删掉重建,代码还在。
这个设计在 1.0.0 的 release notes 里被专门点名——Apple 显然认为它是这个项目值得关注的能力。如果你在 Mac 上有「需要真实 Linux 环境」的场景,不管是跑 systemd 服务、测试多发行版兼容性,还是单纯想要一个不污染 Mac 的开发沙箱,container machine 都值得试一试。
毕竟比起养一台 VMware 虚拟机,container machine run 要顺手得多。


