在 Apple Silicon Mac 上用 container CLI 跑 Debian 13 并调用本地 Ollama 大模型
AIAI Summary (BLUF)
本文详细介绍了如何在 Apple Silicon Mac 上使用 Apple 的 container CLI 创建 Debian 13 虚拟机,并配置该虚拟机通过网络调用 Mac GPU 上运行的 Ollama 本地大模型。文章涵盖了从安装 container、构建带 init 的 Debian 镜像、启动虚拟机到配置 Ollama 监听的完整步骤,并提供了性能测试结果和常见问题解决方案。
核心洞察
这篇文章最有意思的地方在于,它把两个看起来不搭界的东西缝在了一起:Apple 的 container CLI 和本地 LLM。作者踩过的坑很典型——debian:13 官方镜像根本没有 /sbin/init,container machine create 会成功但永远启动不了,命令行也不会报错。如果你正好在 Apple silicon 上折腾 Linux VM 加本地模型,这篇值得从头跟到尾。
这篇文章会带你在 Apple silicon Mac 上用 container CLI 搭一台 Debian 13 机器,然后把这台机器接到 Mac 自己 GPU 上跑的本地 LLM。
VM 是放应用的地方,Mac 是跑模型的地方。 container 下的 Linux VM 拿到的是虚拟 CPU 和虚拟设备,没有 Metal 访问权限,把模型塞进去等于浪费 Mac 最值钱的那块硬件。合理的分工很直白:在真正的 Debian 环境里定制你的应用,用 container machine run 跑起来,让它通过 VM 网络调用 macOS 上的 OllamaA tool for running and managing AI models locally, supporting DeepSeek and other models.。
走通这条路分两半,第一半会静默失败。container machine create debian:13 会成功,机器也创建了,但它永远不会启动,命令行上什么提示都没有。
- 官方
debian:13镜像里没有/sbin/init,所以用它创建的机器会一直停在stopped状态。 - 加一个几行的 Dockerfile 装上
systemd-sysv就能修好,再加两行可以避免所有机器共用同一个 machine-id系统的唯一标识符,在 systemd 中用于标识主机。。 - VM 通过
192.168.64.1访问 Mac,不需要端口转发,也不用查主机名。 - Ollama 必须监听
0.0.0.0,而且brew services restart会悄悄把这个设置改回去。 - 在 Debian 里跑
gemma4:e2b,实测 45.2 tok/s,100% 加载在 GPU 上。 - Gemma 4Gemma 4,Google的轻量级开源模型系列,边缘变体(E2B/E4B)可在1.5GB RAM以下运行。 会先思考再回答,token 上限设小了会返回一个看起来像成功的空回复。
https://github.com/xbill9/apple-container-debian-tips
核心结论
Apple
containerCLI 的官方debian:13镜像不包含/sbin/init,用它创建的 machine 会一直停在stopped状态且命令行不报任何错误;通过 Dockerfile 安装systemd-sysv即可修复。构建镜像时若不清空
/etc/machine-id并删除/var/lib/dbus/machine-id,从同一镜像创建的所有 machine 会共用同一个 machine ID。VM 通过
192.168.64.1(Mac 在bridge100上的地址)访问宿主机,无需端口转发;该地址在 VM 重启后保持不变,但 Ollama 必须监听0.0.0.0才能被 VM 访问。在 Debian VM 中调用 Mac GPU 上运行的
gemma4:e2b,实测速度为 45.2 tok/s,100% 加载在 GPU 上。container machine create返回后 VM 尚未启动完成,立即执行命令会报 "Operation not supported on socket" 错误,需轮询等待(实测约 13 秒)后才能正常使用。
这台机器
以下所有操作都在 2026-09-13 跑过:
| Mac | VM | |
|---|---|---|
| 硬件 | Apple M3, 8 GB | 2 CPUs, 2 GB |
| 系统 | macOS 27.0 (26A428) | Debian GNU/Linux 13 (trixie) |
| 内核 | Darwin | Linux 6.18.35 aarch64 |
| 运行时 | container 1.4.1 |
systemd, running |
| LLM | Ollama 0.33.3 on Metal | 调用 http://192.168.64.1:8000 |
8 GB 内存是这里所有取舍的根源。 一个模型,一台 2 GB 的机器,镜像构建器不构建的时候就停掉。
到这一步你应该准备好了
- 一台 macOS 26 或更新的 Apple silicon Mac。
container跑不了其他平台。 - 一个真正的终端窗口,有两个步骤需要 TTY。
- Homebrew,用来装 Ollama。
- 大约 1.4 GB 磁盘空间给 Debian 机器镜像,再加上模型本身。
步骤 0 到 5 搭 VM。步骤 6 到 9 接模型。这两半互相独立,步骤 7 会先单独测试 Mac,不涉及 VM,这样步骤 9 出问题时容易定位。
容器和机器不是一回事
这个区别造成的困惑最多,所以放在最前面。container 跑 Linux 有两种方式:
| 容器 | 机器 | |
|---|---|---|
| 跑什么 | 镜像里的一个程序 | 一整个启动的 Linux 系统(systemd) |
| 磁盘 | 用完就扔 | 持久保存 |
| Mac 主目录 | 不挂载 | 挂载到 /Users/<你> |
| 你的身份 | root | 你自己的 Mac 用户(uid 501) |
官方 debian:13 能用吗 |
能,直接跑 | 不能 |
容器适合跑一条命令。机器适合那种你会反复定制的应用——昨天装的包还在,源码树已经挂载好了,服务像在任何 Debian 服务器上一样由 systemd 启动。
另外 container 不是 Docker。没有 Docker Desktop,没有 dockerd,也没有 containerd 进程。每个容器和每台机器都跑在自己的轻量 VM 里,由 Apple 自己的 launchd agent 管理。不过镜像就是普通的 OCI 镜像,所以 Docker Hub 照样能用。
步骤 0 — 安装 container 和内核
从 https://github.com/apple/container/releases 下载签名的 .pkg 并验证:
pkgutil --check-signature container-1.4.1-installer-signed.pkg
# Developer ID Installer: Apple Inc. - Containerization (UPBK2H6LZM)
# Notarization: trusted by the Apple notary service
sudo installer 需要一个真正的 TTY,所以要么在终端窗口里跑,要么用图形安装器:
open container-1.4.1-installer-signed.pkg
然后启动服务。container system start 会停下来问你要不要下载 Linux 内核,脚本没法回答这个提示,所以直接显式指定内核:
container system start
container system kernel set --recommended
container system status
# status running
# client.version 1.4.1
# server.version 1.4.1
这会拉取 Kata Containers 内核。没有它什么都跑不起来。
步骤 1 — 看官方镜像启动失败
这一步存在的意义是这个失败根本看不见。 拉镜像,找 init:
container image pull docker.io/library/debian:13
container run --rm debian:13 sh -c 'ls -l /sbin/init; command -v ps ip less sudo; echo done'
ls: cannot access '/sbin/init': No such file or directory
done
没有 init,也没有 ps、ip、less 或 sudo。容器从来不启动,所以它的镜像不需要 init。机器要启动内核,而 Apple 的 /sbin.machine/init 最后一行写死了 exec /sbin/init。你硬试一下,日志会告诉你:
container machine create debian:13 --name debian13-vm # 创建成功,但永远不启动
container machine logs debian13-vm
# /sbin.machine/init: 74: exec: /sbin/init: not found
container machine delete debian13-vm
机器启动不了的时候,container machine logs <n> 是第一个该看的地方。 Apple 自己的文档用 alpine:3.22,就是因为 BusyBox 恰好提供了 /sbin/init。
步骤 2 — 构建一个带 init 的 Debian 镜像
在一个空目录里创建 Dockerfile:
FROM debian:13
ENV DEBIAN_FRONTEND=noninteractive
RUN apt-get update \
&& apt-get install -y --no-install-recommends \
systemd-sysv dbus sudo ca-certificates procps less iproute2 iputils-ping curl \
&& rm -rf /var/lib/apt/lists/*
RUN systemctl mask systemd-modules-load.service
RUN : > /etc/machine-id && rm -f /var/lib/dbus/machine-id
CMD ["/sbin/init"]
每一行都有它存在的理由,少一行就出问题:
| 行 | 为什么需要 |
|---|---|
systemd-sysv |
提供 /sbin/init,这是唯一的硬性要求 |
procps less iproute2 iputils-ping curl ca-certificates |
官方镜像省掉的基础工具。curl 是后面 VM 调模型要用的 |
sudo |
首次启动时你的 Mac 用户已经拿到免密 sudo,缺的只是这个包 |
dbus |
systemd 服务依赖的系统消息总线 |
systemctl mask systemd-modules-load.service |
Apple 的内核不支持可加载模块,这个 unit 每次都会失败,systemd 会报 degraded |
: > /etc/machine-id && rm -f /var/lib/dbus/machine-id |
装 dbus 时会往镜像里写一个 machine ID。不加这行,从镜像出来的每台机器都共用同一个 ID |
CMD ["/sbin/init"] |
machine 会忽略它,但这是标记镜像类型的信号 |
machine-id 那行是我踩坑踩出来的。当时创建的每台机器 ID 都一样,换了单独构建的镜像也一样,甚至从一个 /etc/machine-id 已经清空的快照创建出来还是一样。原因是 systemd 在启动时会把 dbus 构建期留下的那份拷贝写进空的 /etc/machine-id。只清一个文件不够,另一个也得删。
开始构建。builder 跑起来会占用 2 个 CPU 和 2GB 内存,而且 container build 只负责启动它,不会帮你停:
container builder start
container build -t debian13-machine .
container builder stop
构建大概花了 10 秒。中间那些 debconf: ... Readline 的提示只是因为没有终端,不用管。
第 3 步 — 提交之前先验一下镜像
这一步我第一次做的时候跳过了:
container run --rm debian13-machine sh -c 'ls -l /sbin/init; wc -c < /etc/machine-id; ls /var/lib/dbus/machine-id'
lrwxrwxrwx 1 root root 22 Apr 13 19:38 /sbin/init -> ../lib/systemd/systemd
0
ls: cannot access '/var/lib/dbus/machine-id': No such file or directory
你要看到的是:/sbin/init 存在,machine-id 是 0 字节,dbus 那份拷贝不存在。检查用 ls,别用 readlink -f。readlink -f /sbin/init 就算文件不存在也会打印一个路径出来,骗你没商量。
第 4 步 — 创建机器,然后等它
container machine create debian13-machine --name dev-vm --cpus 2 --memory 2G
create 大概一秒就返回了,这时候 VM 还没启动完。你紧接着发命令会收到一个指向完全错误方向的报错:
Error: The operation couldn't be completed. Operation not supported on socket
这跟 socket 没关系,也不是 TTY 的问题。就是机器还没启动完。轮询等它响应,这里花了 13 秒:
until container machine run -n dev-vm --root -- true 2>/dev/null; do sleep 2; done
然后再给 systemd 几秒钟。首次启动后 systemctl is-system-running 会有一阵子显示 initializing。
第 5 步 — 确认它真的在跑
container machine run -n dev-vm -- '. /etc/os-release; echo "$PRETTY_NAME"; uname -srm; systemctl is-system-running; id; sudo -n id -u; nproc'
Debian GNU/Linux 13 (trixie)
Linux 6.18.35 aarch64
running
uid=501(xbill) gid=20(dialout) groups=20(dialout)
0
2
running而不是degraded或initializing:systemd 完全起来了。id:你是自己的 Mac 用户,首次启动时在 VM 里创建的。sudo -n id -u输出0:免密 sudo 能用。- 你的 Mac 家目录挂载在
/Users/<你>。
命令要作为一个带引号的参数传进去。 machine run 会把参数用空格拼起来,然后在 VM 里用 /bin/bash -c 重新解析,你在宿主机上的引号全丢了:
container machine run -n mkdm-test-vm --root -- echo '$0' 'a b'
# /bin/bash a b ($0 在 VM 里展开,空格被合并)
所以 -- sh -c '...' 会静默地跑错命令,什么都不输出。那段脚本本来就已经由 bash 执行了,整个交过去就行。
第 6 步 — 像真机一样定制它
这是容器做不到的部分。你装的东西都留在机器自己的磁盘上:
container machine run -n dev-vm --root -- 'apt-get update && apt-get install -y --no-install-recommends git vim-tiny'
重启之后这些改动还在:
container machine stop dev-vm # 大概 10 秒
container machine run -n dev-vm -- 'git --version' # 再次启动
# git version 2.47.3
不过重启后有个时间差,machine run 比 systemd 先就绪。头几秒里 systemctl 会报 Failed to connect to system scope bus。等一会儿再用。
删掉 machine,这些改动也一起没了。 想留住就把包写进 Dockerfile 重新构建,或者把 machine 快照成镜像。
交互式 shell 需要一个真正的终端窗口。从脚本或者 agent 工具里调用会失败,报的错跟 Step 4 里那个 "not supported" 一样:
container machine run -n dev-vm # 以你自己的身份进 shell
container machine run -n dev-vm --root # 以 root 身份进 shell
The short way
Step 2 到 Step 5 这些活,仓库里的 bin/mkdebian-machine 全干了,包括 /sbin/init 检查、启动轮询,以及如果 builder 之前是停的就把它停掉。--setup 脚本会以 root 身份在新 machine 里跑:
bin/mkdebian-machine new -t debian13-machine -m dev-vm -p sudo
bin/mkdebian-machine new -m dev-vm -p "git vim" --setup ./setup-dev.sh
-p 指定的包会烤进镜像里。--setup 只改当前这台 machine。bin/mkdebian-machine publish dev-vm <ref> 能把手工调好的 machine 快照成镜像,快照前会先把你的用户、SSH host key 和日志清掉。
为什么模型不跑在 VM 里
VM 拿不到 GPU。 container 里的 Linux 只能看到虚拟 CPU 和虚拟设备,没有 Metal 访问权限。模型在那儿跑就是纯 CPU,而且只有你分给 machine 的那 2 GB 内存。
所以模型跑在 macOS 上,Ollama 用 M3 的 GPU,VM 通过 VM 网络调它:
dev-vm 192.168.64.x (重启后会变)
│ 默认路由 + DNS → 192.168.64.1
▼
vmenet0 ─ bridge100 on the Mac 192.168.64.1 ← Mac 自己在 VM 网络上的地址
│
Ollama *:8000 → Metal GPU
192.168.64.1是 Mac 在bridge100上的地址。它既是 VM 的网关也是 DNS 服务器,而且 VM 重启它不变。变的是 VM 自己的地址。- 没有
host.docker.internal,也没有host.container.internal。直接用 IP。 - 没有端口转发。VM 直连 Mac,这能成的前提是 Ollama 监听所有网卡。绑在
127.0.0.1上 VM 就够不着。
Step 7 — 让 Ollama 监听 VM 网络
装 Ollama,再拉一个 8 GB 内存塞得下的模型:
brew install ollama
brew services start ollama
OLLAMA_HOST=127.0.0.1:8000 ollama pull gemma4:e2b
Ollama 作为 Homebrew 的 launchd 服务跑。这台 Mac 的 plist 之前已经改过,端口是 8000,还带了 OLLAMA_KV_CACHE_TYPE=q4_0 和 OLLAMA_FLASH_ATTENTION=1,所以只需要改 host。Homebrew 自己的默认是 127.0.0.1:11434。要么也把端口设成 8000,要么把下面的 URL 改掉。
P=~/Library/LaunchAgents/homebrew.mxcl.ollama.plist
cp -p "$P" "$P.bak-$(date +%Y%m%d%H%M%S)"
/usr/libexec/PlistBuddy -c 'Set :EnvironmentVariables:OLLAMA_HOST 0.0.0.0:8000' "$P"
# 重启,让 launchd 重新读 plist
launchctl bootout gui/$(id -u)/homebrew.mxcl.ollama
launchctl bootstrap gui/$(id -u) "$P"
lsof -nP -iTCP:8000 -sTCP:LISTEN # ollama *:8000
别用 brew services restart ollama。 它会按 formula 重新生成 plist,而 formula 里只设了 OLLAMA_FLASH_ATTENTION=1 和 OLLAMA_KV_CACHE_TYPE=q8_0。这会把 OLLAMA_HOST 悄悄改回 localhost,VM 那边一点没动,却突然连不上模型了。
这套配置是故意敞开的。0.0.0.0 加上 macOS 应用防火墙关着,Ollama 从 Wi-Fi 网络也能访问。在家用网络做 demo 就是要这个效果。换个环境,这是第一个要改的地方。
Step 8 — 先测 Mac 这一侧
bin/test-mac-ollama 不涉及 VM,只检查 Mac 上的 Ollama:监听端口、localhost 和 192.168.64.1、plist、原生 API、流式 API、OpenAI 兼容 API,还有 GPU 占用。退出时会卸载模型,只有全部检查通过才返回 0。
bin/test-mac-ollama
== 1. listener on port 8000
ollama *:8000
PASS ollama listens on all interfaces, so VMs can reach it
== 2. HTTP on http://127.0.0.1:8000
PASS Ollama 0.33.3 answers on http://127.0.0.1:8000
== 3. VM-facing address 192.168.64.1
PASS Ollama answers on http://192.168.64.1:8000 (bridge100), the address VMs use
== 4. launchd config
info OLLAMA_HOST in plist: 0.0.0.0:8000
== 5. models
PASS gemma4:e2b is installed
== 6. native API
23 tokens @ 43.6 tok/s, load 5.7 s
PASS gemma4:e2b answered through /api/generate
== 7. streaming
PASS streaming delivers incremental chunks
== 8. OpenAI-compatible API
PASS /v1/chat/completions returned text
== 9. GPU
PASS gemma4:e2b loaded 100% on the GPU (1.6 GB)
== 10. second model: gemma4:e2b-it-qat
33 tokens @ 43.3 tok/s, load 7.5 s
PASS gemma4:e2b-it-qat answered through /api/generate
PASS gemma4:e2b-it-qat loaded 100% on the GPU (3.3 GB)
10 passed, 0 failed
Ollama works on the Mac. Next: bin/test-vm-ollama
顺序很关键。 这一步过了而 VM 测试挂了,那问题在 VM 或网络路径上,跟 Ollama 无关。它的第 3 项检查在 container 或 machine 创建出 bridge100 之前会跳过,这也是先建 VM 的另一个理由。
第 9 步 — 从虚拟机里调用模型
现在切到 Debian 里面。下面这些命令用的是我长期保留的机器 debian13-vm,镜像类型一样,你换成 dev-vm 就行。先看原生 API:
container machine run -n debian13-vm -- 'curl -s http://192.168.64.1:8000/api/generate -d "{\"model\":\"gemma4:e2b\",\"prompt\":\"Say hello from a Debian VM in five words.\",\"stream\":false,\"think\":false}"'
# "response":"Hello from Debian VM."
再看 OpenAI 兼容接口,base URL 是 http://192.168.64.1:8000/v1:
container machine run -n debian13-vm -- 'curl -s http://192.168.64.1:8000/v1/chat/completions -H "Content-Type: application/json" -d "{\"model\":\"gemma4:e2b\",\"messages\":[{\"role\":\"user\",\"content\":\"Name one Debian release codename.\"}],\"reasoning_effort\":\"none\"}"'
# "content":"One Debian release codename is **Bookworm**."
第二个接口就是整件事的关键。虚拟机里任何说 OpenAI API 的东西——你的应用、你的 agent、你的测试框架——把 base URL 设成 http://192.168.64.1:8000/v1,就能拿到跑在 Mac GPU 上的模型。应用在 Debian 里定制、运行,推理过程完全不碰虚拟机那 2 GB 内存。
第 10 步 — 验证整条链路
bin/test-vm-ollama 会从机器内部跑完整条路径,然后去问 Mac 的 /api/ps,确认模型到底加载在哪。它需要机器里有 curl,Mac 上有 jq:
bin/test-vm-ollama # debian13-vm, gemma4:e2b + gemma4:e2b-it-qat
bin/test-vm-ollama -n dev-vm -m gemma4:e2b --no-qat
== 1. network path PASS VM reaches http://192.168.64.1:8000 (connect 0.002372 s, Ollama 0.33.3) == 2. models gemma4:e2b-it-qat gemma4:e2b gemma4:e4b PASS gemma4:e2b is listed == 3. native API reply: Debian is a free and open-source operating system that forms the basis for many other Linux distributions. 22 tokens @ 45.2 tok/s, load 5.4 s PASS gemma4:e2b answered through /api/generate == 4. streaming 14 chunks: 1, 2, 3, 4, 5 PASS streaming delivers incremental chunks == 5. OpenAI-compatible API PASS /v1/chat/completions returned text == 6. GPU (asked from the Mac) PASS gemma4:e2b loaded 100% on the GPU (1.6 GB) == 7. second model: gemma4:e2b-it-qat 35 tokens @ 38.9 tok/s, load 12.4 s PASS gemma4:e2b-it-qat answered through /api/generate PASS gemma4:e2b-it-qat loaded 100% on the GPU (3.3 GB)
8 passed, 0 failed
虚拟机这一跳几乎不花代价。连接耗时 2 毫秒。gemma4:e2b 通过虚拟机跑出 45.2 tok/s,直接从 Mac 上跑是 43.6 tok/s——各自只跑了一次,这点差距属于运行波动,不是虚拟机更快。这里的生成速度受 M3 内存限制,跟 Debian 和 macOS 之间的网络没关系。
GPU 检查读的是 /api/ps 里的 size_vram / size,和 ollama ps 打印成 100% GPU 的是同一组数字。URL 写错会在第 1 步失败,退出码 1;机器不存在或者缺 curl,退出码 2。
回复为空说明 Gemma 4 还在思考
Gemma 4 是推理模型。token 上限设得小,整个预算都花在隐藏推理上,返回的答案就是空的——HTTP 200,JSON 也合法:
| 请求 | 结果 |
|---|---|
原生,num_predict: 40 |
❌ "response":"",done_reason: length |
OpenAI,max_tokens: 20 |
❌ "content":"",finish_reason: length;message.reasoning 以 Thinking Process: 开头 |
原生,"think": false |
✅ Hello from Debian VM.,6 个 token |
OpenAI,"reasoning_effort": "none" |
✅ 11 个 token 给出答案 |
| OpenAI,不设上限 | ⚠️ 给出 Bookworm,但花了 159 个 token,大部分是推理 |
应用里要检查 finish_reason,不能只看状态码。要快速回答就把思考关掉,要么就给推理留足空间。
8 GB 内存装得下哪个模型
| 模型 | 量化 | 加载后占用 | 速度 | |
|---|---|---|---|---|
| 🥇 | gemma4:e2b |
Q4_K_M | 1.7 GB | 约 44 tok/s |
| 🥈 | gemma4:e2b-it-qat |
Q4_0,QAT(Google) | 3.6 GB | 约 38–44 tok/s |
| — | gemma4:e4b |
Q4_K_M | 没跑 | 磁盘上 9.6 GB,超过这台 Mac 的内存 |
gemma4:e2b 是舒服的选择。QAT 版本在 4 bit 下质量更好,但开着机器的时候空闲内存掉到 6%。用完之后把它卸掉,别让 3.6 GB 一直占着:
curl -s http://127.0.0.1:8000/api/generate -d '{"model":"gemma4:e2b-it-qat","keep_alive":0}'
这样拆分的好处在哪
- 应用这边是一台真正的 Debian 服务器。systemd、apt、sudo、持久磁盘,Mac 上的源码树也已经挂载好了。定制完用
mkdebian-machine publish打成快照,直接把镜像交给别人。 - 模型这边是满速的 Mac。Metal、统一内存,没有 GPU 直通要配——因为压根就没有需要配的。
- 接缝就是一个 URL。
http://192.168.64.1:8000/v1是 OpenAI 兼容端点。同一个应用换个--url就能指向别的 Ollama 或 llama.cpp 服务器,虚拟机里什么都不用改。
需要 Linux 的活虚拟机干,需要 GPU 的活 Mac 干,谁也不装成对方。
出问题的时候
| 症状 | 原因 | 修复 |
|---|---|---|
机器一直停在 stopped;日志显示 exec: /sbin/init: not found |
用的是纯 debian:13,没有 init |
第 2 步 |
Operation not supported on socket / by device |
机器还在启动,或者交互式 shell 没有 TTY | 第 4 步轮询;传一条命令进去 |
systemctl is-system-running → degraded |
systemd-modules-load.service 失败了 |
把它 mask 掉(第 2 步) |
两台机器共用 /etc/machine-id |
镜像里带着 /var/lib/dbus/machine-id |
第 2 步最后一行 RUN |
| 带引号的命令什么都不输出 | 参数被 bash 拼接后又重新解析了一遍 | 当成一个带引号的参数传 |
Plugin 'container-images' not found |
敲成了 container images ls |
container image ls |
虚拟机从 192.168.64.1:8000 拿不到 HTTP 200 |
Ollama 又回到 localhost 了,通常是 brew services restart 之后 |
第 7 步,然后跑 bin/test-mac-ollama |
回复为空,finish_reason: length |
Gemma 4 把预算全花在思考上了 | think: false / reasoning_effort: "none" |
速查表
# 0. 安装、启动、内核 container system start container system kernel set --recommended# 1. 官方镜像没法当机器启动
container run --rm debian:13 sh -c 'ls -l /sbin/init'# 2. 构建带 init 的 Debian 镜像(Dockerfile 见上文)
container builder start
container build -t debian13-machine .
container builder stop
# 3. 检查一下
container run --rm debian13-machine sh -c 'ls -l /sbin/init; wc -c < /etc/machine-id'
# 4. 创建,然后等它起来
container machine create debian13-machine --name dev-vm --cpus 2 --memory 2G
until container machine run -n dev-vm --root -- true 2>/dev/null; do sleep 2; done
# 5. 检查一下
container machine run -n dev-vm -- 'systemctl is-system-running; id; sudo -n id -u'
# 6. 按需定制
container machine run -n dev-vm --root -- 'apt-get update && apt-get install -y git'
# 2 到 5 步的快捷方式
bin/mkdebian-machine new -t debian13-machine -m dev-vm -p sudo
# 7. 让 Ollama 监听所有网卡,用 launchctl 重启
/usr/libexec/PlistBuddy -c 'Set :EnvironmentVariables:OLLAMA_HOST 0.0.0.0:8000' ~/Library/LaunchAgents/homebrew.mxcl.ollama.plist
launchctl bootout gui/$(id -u)/homebrew.mxcl.ollama
launchctl bootstrap gui/$(id -u) ~/Library/LaunchAgents/homebrew.mxcl.ollama.plist
# 8. 先测 Mac 本机
bin/test-mac-ollama
# 9. 再从虚拟机里调模型
container machine run -n dev-vm -- 'curl -s http://192.168.64.1:8000/v1/models'
# 10. 完整跑一遍往返
bin/test-vm-ollama -n dev-vm
# 清理
container machine stop dev-vm
container machine delete dev-vm
container image rm debian13-machine
<svg xmlns="http://www.w3.org/2000/svg" width="20px" height="20px" viewBox="0 0 24 24"><title>退出全屏</title>
<path d="M18 7h4v2h-6V3h2v4zM8 9H2V7h4V3h2v6zm10 8v4h-2v-6h6v2h-4zM8 15v6H6v-4H2v-2h6z"></path>
小结
这篇文章的目标是在 Apple 的 container CLI 下跑起一台持久化的 Debian 13 机器,然后让里面的应用用上 Mac GPU 上的本地大模型。解法关键在于把两件事拆开:做一个机器真能启动的镜像,模型留在 macOS 这边靠 Metal 跑,虚拟机通过 192.168.64.1 这个网络地址去访问。结果如下:
- 🟢 官方的
debian:13镜像没法作为机器启动;加上systemd-sysv再清一遍 machine-id,就得到一台 systemd 状态为running的 Debian 13,还带上了你的 Mac 用户和无密码 sudo - 🟢 机器里装的包重启之后还在
- 🟢 从虚拟机里,
gemma4:e2b通过原生、流式和 OpenAI 兼容三种 API 都能应答,速度 45.2 tok/s,100% 跑在 GPU 上 - 🟢
bin/test-mac-ollama报告10 passed, 0 failed,bin/test-vm-ollama报告8 passed, 0 failed - ⚠️
brew services restart ollama会悄悄把 Ollama 改回只监听 localhost,虚拟机就找不到模型了 - ⚠️ Gemma 4 在 token 上限设得小的时候会返回空答案,除非把 thinking 关掉
适用范围:一台 Apple M3 Mac,8 GB 内存,macOS 27.0(26A428),container 1.4.1,Debian 13 机器给 2 核 2 GB,Ollama 0.33.3,开了 OLLAMA_KV_CACHE_TYPE=q4_0 和 flash attention。每个吞吐数字都是 2026-09-13 当天测试脚本单次跑出来的,模型每次回复本身也会有波动。
在 Apple container 上跑一台定制 Debian 机器、模型放在 Mac GPU 上,这套思路通过一步步增量验证是走得通的。
参考资料
</div></div>
常见问题(FAQ)
为什么用 debian:13 官方镜像创建的 container machine 一直启动不了?
因为官方 debian:13 镜像里没有 /sbin/init,而 Apple 的 /sbin.machine/init 最后一行写死了 exec /sbin/init。机器创建会成功但永远停在 stopped 状态,命令行也不报错。用 container machine logs 能看到 exec: /sbin/init: not found。
在 Apple Silicon Mac 上,Debian 虚拟机怎么调用 Mac GPU 上跑的 Ollama?
VM 通过 192.168.64.1 访问 Mac,不需要端口转发。Ollama 必须监听 0.0.0.0,然后在 Debian 里请求 http://192.168.64.1:8000 即可。注意 brew services restart 会悄悄把监听设置改回去。
为什么在 Debian 虚拟机里跑 gemma4:e2b 会返回空回复?
Gemma 4 会先思考再回答,如果 token 上限设得太小,思考过程就会耗尽配额,返回一个看起来像成功的空回复。调大 token 上限即可解决。
版权与免责声明:本文仅用于信息分享与交流,不构成任何形式的法律、投资、医疗或其他专业建议,也不构成对任何结果的承诺或保证。
文中提及的商标、品牌、Logo、产品名称及相关图片/素材,其权利归各自合法权利人所有。本站内容可能基于公开资料整理,亦可能使用 AI 辅助生成或润色;我们尽力确保准确与合规,但不保证完整性、时效性与适用性,请读者自行甄别并以官方信息为准。
若本文内容或素材涉嫌侵权、隐私不当或存在错误,请相关权利人/当事人联系本站,我们将及时核实并采取删除、修正或下架等处理措施。也请勿在评论或联系信息中提交身份证号、手机号、住址等个人敏感信息。



