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 run container 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),双击安装后执行:

sh 复制代码
container system start

注意:官方目前只支持 macOS 26 ,macOS 15 上发现的问题不再修复。安装完整步骤可参考 开箱实践,但注意文中版本信息已过时。

创建并进入第一个 machine

sh 复制代码
container machine create alpine:latest --name dev

创建好之后,直接进交互 shell:

sh 复制代码
container machine run -n dev

进去之后跑两条命令验证一下:

sh 复制代码
whoami   # 输出你的 macOS 用户名,不是 root
pwd      # /Users/<你的用户名>,就是你的 Mac home 目录

这两行的输出结果,是 container machine 和普通容器最直观的差别------你不是在一个陌生的 Linux 里,你带着自己的身份和文件进来了。

也可以不进 shell,直接在 machine 里跑单条命令:

sh 复制代码
container machine run -n dev uname -a
container machine run -n dev -- cat /proc/cpuinfo   # 命令带 flag 时加 --

container machine run 启动时如果 machine 处于停止状态,会自动先把它启动起来,不需要手动 start

设置默认 machine

每次都带 -n dev 有点烦,可以设一个默认:

sh 复制代码
container machine set-default dev

之后直接 container machine run 就操作 dev,不需要再指定名字。

常用命令

生命周期管理

sh 复制代码
container machine ls              # 列出所有 machine
container machine inspect dev     # 查看详细信息(JSON)
container machine stop dev        # 停止
container machine rm dev          # 删除,同时清除持久化存储

调整资源

sh 复制代码
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 和宿主机文件系统完全隔离
sh 复制代码
container machine set -n dev home-mount=ro

m 缩写

machine 有个别名 m,所有子命令都能用:

sh 复制代码
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:

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:不起图形界面,直接到命令行

构建并创建:

sh 复制代码
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 管理服务:

sh 复制代码
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:

sh 复制代码
#!/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 要顺手得多。

参考资料

相关推荐
a44931536210 小时前
iPhone Face ID 故障的分层诊断——原深感系统与主板加密配对机制
macos·ios·电脑·cocoa·iphone
白玉cfc11 小时前
【iOS】内存五大分区
macos·ios·cocoa
风曦Kisaki17 小时前
#企业级docker私有仓库构建:harbor仓库与阿里云镜像仓库
阿里云·docker·容器
期待着201317 小时前
docker 安装 ,在centos7.9
运维·docker·容器
骑上单车去旅行19 小时前
Docker Compose 命令完全指南:从构建到运维
docker·容器·eureka
a44931536220 小时前
iPhone 基带故障诊断——无服务/搜索不到网络的芯片级维修实录
网络·macos·ios·电脑·iphone
spider_xcxc21 小时前
K8s 部署学习笔记
docker·容器·kubernetes·云计算·k8s
java_logo21 小时前
Docker Compose 部署 Apache Superset:轻松搭建开源 BI 平台
docker·开源·apache·superset·轩辕镜像·superset部署方案·docker superset
zy happy21 小时前
VMware虚拟机添加新的硬盘
java·linux·jvm·docker
a44931536221 小时前
MacBook USB-C不充电故障诊断:从PD握手到主板供电链路
c语言·开发语言·macos·电脑