Apple Container Machine:把 Linux 搬进 Mac

Apple Container Machine:把 Linux 搬进 Mac

TL;DR

container machine 是 Apple Container 里容易被忽视的另一半:不是跑一个进程,而是跑一个完整的 Linux 开发环境,自动挂载你的 macOS $HOME,支持 systemd,用法更接近 WSL2 而不是 Docker。

从货箱到书房:Container Machine 是什么

这个系列已经写了四篇:

读完这四篇,你对 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 runcontainer machine
定位应用模型:跑单个进程/服务环境模型:完整 Linux 开发环境
init 系统vminitd(Apple 自研,极简)镜像自带(如 systemd
macOS $HOME不挂载自动挂载,零配置
用户身份root 或镜像内用户自动匹配宿主机用户名
生命周期进程结束即退出持久化,显式 stop
典型场景CI、跑服务、测试日常开发环境

和 Docker 的关系

container rundocker run 在精神上是同一件事:进程/应用模型,无状态,entrypoint 驱动。如果你只用过这一半,觉得 Apple Container 就是「原生的 Docker 替代」,也没错——但你只用了一半。

container machine 反而更接近 WSL2(Windows)或 Lima(Mac 上的前辈方案):持久化的 Linux 环境,和宿主机文件系统打通,可以 systemctl 管服务。

和 VMware Fusion 的关系

表面上 container machineVMware 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/initsystemd 就能完整运行。

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:屏蔽掉几个在虚拟化环境里会导致启动卡住的 unit
  • systemctl 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_HOMEhome 目录路径
CONTAINER_MACHINE_IDmachine 标识符

一个实用的例子——创建用户并配置 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 要顺手得多。

参考资料

乱世浮生微信公众号二维码

(转载本站文章请注明作者和出处乱世浮生,请勿用于任何商业用途)

comments powered by Disqus