离线存档

在 Docker Sandboxes 中安全运行 OpenClaw

AI 核心摘要

文章由 Oleg Šelajev 撰写,详细介绍了如何在 Docker Sandboxes 中安全地运行开源 AI 编程智能体 OpenClaw。Docker Sandboxes 提供了一个隔离的微型虚拟机环境,能够通过网络代理控制 Agent 的互联网访问,并安全注入 API 密钥(如 ANTHROPIC_API_KEY),防止泄露。结合 Docker Model Runner,用户可以在本地零成本、完全隐私地运行模型(如 qwen2.5 或 gpt-oss),并在同一个沙盒中无缝切换到云端模型。文章还提供了构建自定义 OpenClaw 镜像的技术细节,包括如何创建一个轻量级的 Node.js 桥接器来绕过 Node.js 对 HTTP_PROXY 的限制,从而成功连接宿主机上的 Docker Model Runner。

Docker Sandboxes 是 Docker 生态系统中的一个新基础组件,它允许你在隔离的微型虚拟机(micro VMs)中运行 AI Agent 或任何其他工作负载。它提供了强大的隔离性、便捷的开发者体验以及强大的安全边界,其网络代理可配置为拒绝 Agent 连接到任意互联网主机。网络代理还能方便地将诸如 ANTHROPIC_API_KEYOPENAI_API_KEY 之类的 API 密钥注入其中,因此 Agent 根本无法访问这些密钥,也不可能将其泄露。

上一篇文章中,我展示了 Docker Sandboxes 如何让你将 AI Agent 可能需要的任何工具(例如用于 Java 项目的 JDK 或某些自定义 CLI)安装到与主机隔离的容器中。今天我们将更进一步:我们将通过 Docker Model Runner 在本地模型上运行 OpenClaw——一个开源的 AI 编程 Agent。

没有 API 密钥泄露风险,没有云端成本,完全私密。而且你只需大约两条命令就能搞定。

快速开始

确保你安装了 Docker Desktop,并且启用了 Docker Model Runner(设置 → Docker Model Runner → 启用),然后拉取一个模型:

docker model pull ai/gpt-oss:20B-UD-Q4_K_XL

现在创建并运行沙盒:

docker sandbox create --name openclaw -t olegselajev241/openclaw-dmr:latest shell .

docker sandbox network proxy openclaw --allow-host localhost

docker sandbox run openclaw

在沙盒内部运行:

~/start-openclaw.sh

在 Docker Sandbox 中运行 OpenClaw

就这么简单。你现在进入了 OpenClaw 的终端用户界面(TUI),正在与你机器上本地的 gpt-oss 模型对话。模型在你的宿主机上的 Docker Model Runner 中运行,而 OpenClaw 则在沙盒中完全隔离运行:它只能读取和写入你提供给它的工作区文件,并且有一个网络代理来拒绝连接到不需要的主机。

云端模型也能用

沙盒代理会自动从你的宿主机环境中注入 API 密钥。如果你设置了 ANTHROPIC_API_KEYOPENAI_API_KEY,OpenClaw 就可以运行云端模型,只需在 OpenClaw 设置中指定它们即可。代理负责凭证注入,因此你的密钥永远不会在沙盒内部暴露。

这意味着你可以使用免费的本地模型进行实验,然后在同一个沙盒中切换到云端模型进行正式工作。对于云端模型,你甚至不需要允许代理访问宿主机的 localhost,所以无需运行 docker sandbox network proxy openclaw --allow-host localhost

选择你的模型

启动脚本会自动发现你的 Docker Model Runner 中可用的模型。列出它们:

~/start-openclaw.sh list

使用特定模型:

~/start-openclaw.sh ai/qwen2.5:7B-Q4_K_M

任何你使用 docker model pull 拉取过的模型都可以使用。

工作原理(稍微涉及点技术)

预构建的镜像 (olegselajev241/openclaw-dmr:latest) 基于 shell 沙盒模板,并增加了三个东西:Node.js 22、OpenClaw 和一个微型的网络桥接器。

需要这个桥接器是因为 Docker Model Runner 运行在你的宿主机上,并绑定到 localhost:12434。但是沙盒内部的 localhost 指的是沙盒本身,而不是你的宿主机。沙盒确实有一个 HTTP 代理,位于 host.docker.internal:3128,它可以访问宿主机服务,并且我们允许它通过 docker sandbox network proxy --allow-host localhost 访问 localhost

问题在于 OpenClaw 是 Node.js 写的,而 Node.js 不遵循 HTTP_PROXY 环境变量。所以我们写了一个大约 20 行的桥接脚本,OpenClaw 连接到 127.0.0.1:54321,该脚本会明确地将请求通过代理转发,以到达宿主机上的 Docker Model Runner:

OpenClaw → 桥接器 (localhost:54321) → 代理 (host.docker.internal:3128) → Model Runner (宿主机 localhost:12434)

start-openclaw.sh 脚本启动桥接器,启动 OpenClaw 的网关(清除了代理环境变量以便直接命中桥接器),并运行 TUI。

自己动手构建

想自定义镜像或者只是想看看它是如何工作的?这是完整的构建过程。

1. 创建基础沙盒并安装 OpenClaw

docker sandbox create --name my-openclaw shell .

docker sandbox network proxy my-openclaw --allow-host localhost

docker sandbox run my-openclaw

现在让我们在沙盒中安装 OpenClaw:

# 安装 Node 22 (OpenClaw 需要它)
npm install -g n && n 22
hash -r

# 安装 OpenClaw
npm install -g openclaw@latest

# 运行初始设置
openclaw setup

2. 创建 Model Runner 桥接器

这是关键所在——一个微型的 Node.js 服务器,它将请求通过沙盒代理转发到你宿主机上的 Docker Model Runner:

cat > ~/model-runner-bridge.js << 'EOF'
const http = require("http");
const { URL } = require("url");

const PROXY = new URL(process.env.HTTP_PROXY || "http://host.docker.internal:3128");
const TARGET = "localhost:12434";

http.createServer((req, res) => {
  const proxyReq = http.request({
    hostname: PROXY.hostname,
    port: PROXY.port,
    path: "http://" + TARGET + req.url,
    method: req.method,
    headers: { ...req.headers, host: TARGET }
  }, proxyRes => {
    res.writeHead(proxyRes.statusCode, proxyRes.headers);
    proxyRes.pipe(res);
  });

  proxyReq.on("error", e => { res.writeHead(502); res.end(e.message); });
  req.pipe(proxyReq);
}).listen(54321, "127.0.0.1");
EOF

3. 配置 OpenClaw 使用 Docker Model Runner

现在将 Docker Model Runner 提供商合并到 OpenClaw 的配置中:

python3 -c "
import json
p = '$HOME/.openclaw/openclaw.json'
with open(p) as f: cfg = json.load(f)

cfg['models'] = cfg.get('models', {})
cfg['models']['mode'] = 'merge'
cfg['models']['providers'] = cfg['models'].get('providers', {})
cfg['models']['providers']['docker-model-runner'] = {
  'baseUrl': 'http://127.0.0.1:54321/engines/llama.cpp/v1',
  'apiKey': 'not-needed',
  'api': 'openai-completions',
  'models': [{
    'id': 'ai/qwen2.5:7B-Q4_K_M',
    'name': 'Qwen 2.5 7B (Docker Model Runner)',
    'reasoning': False, 'input': ['text'],
    'cost': {'input': 0, 'output': 0, 'cacheRead': 0, 'cacheWrite': 0},
    'contextWindow': 32768, 'maxTokens': 8192
  }]
}

cfg['agents'] = cfg.get('agents', {})
cfg['agents']['defaults'] = cfg['agents'].get('defaults', {})
cfg['agents']['defaults']['model'] = {'primary': 'docker-model-runner/ai/qwen2.5:7B-Q4_K_M'}
cfg['gateway'] = {'mode': 'local'}

with open(p, 'w') as f: json.dump(cfg, f, indent=2)
"

4. 保存并分享

退出沙盒并将其保存为可复用的镜像:

docker sandbox save my-openclaw my-openclaw-image:latest

将它推送到镜像仓库,以便任何人都可以使用:

docker tag my-openclaw-image:latest yourname/my-openclaw:latest docker push yourname/my-openclaw:latest

任何拥有 Docker Desktop(包含现代沙盒功能)的人都可以通过以下命令启动相同的环境:

docker sandbox create --name openclaw -t yourname/my-openclaw:latest shell .

下一步

Docker Sandboxes 使在隔离的、可重复的环境中运行任何 AI 编程 Agent 变得容易。通过 Docker Model Runner,你获得了一个完全本地化的 AI 编程设置:没有云端依赖,没有 API 成本,并且完全保护隐私。