Files
nanoclaw/docs/zh/docker-sandboxes.md
2026-05-12 13:14:17 +00:00

12 KiB
Raw Permalink Blame History

在 Docker 沙盒中运行 NanoClaw手动设置

本指南介绍如何从头开始在 Docker 沙盒中设置 NanoClaw——无需安装脚本无需预构建的 fork。你将克隆上游仓库应用必要的补丁并在完全的 hypervisor 级别隔离下运行 agent智能体

架构

宿主机 (macOS / Windows WSL)
└── Docker 沙盒 (带隔离内核的微 VM)
    ├── NanoClaw 进程 (Node.js)
    │   ├── 频道适配器 (WhatsApp, Telegram 等)
    │   └── 容器启动器 → 嵌套 Docker 守护进程
    └── Docker-in-Docker
        └── nanoclaw-agent 容器
            └── Claude Agent SDK

每个 agent 在自己的容器中运行,位于一个与你的宿主机完全隔离的微 VM 内。两层隔离:每个 agent 的容器 + VM 边界。

沙盒在 host.docker.internal:3128 提供一个 MITM 代理,处理网络访问并自动注入你的 Anthropic API 密钥。

注意: 本指南基于在 macOSApple Silicon上使用 WhatsApp 验证过的设置。其他频道Telegram、Slack 等和环境Windows WSL可能需要针对其特定 HTTP/WebSocket 客户端添加额外的代理补丁。核心补丁容器运行器、凭据代理、Dockerfile普遍适用——频道特定的代理配置各有不同。

前提条件

  • Docker Desktop v4.40+ 支持沙盒
  • Anthropic API 密钥(沙盒代理管理注入)
  • 对于 Telegram:来自 @BotFather 的 bot token 和你的 chat ID
  • 对于 WhatsApp:一部安装了 WhatsApp 的手机

验证沙盒支持:

docker sandbox version

步骤 1创建沙盒

在你的宿主机上:

# 创建工作区目录
mkdir -p ~/nanoclaw-workspace

# 创建带工作区挂载的 shell 沙盒
docker sandbox create shell ~/nanoclaw-workspace

如果你使用 WhatsApp配置代理绕过使 WhatsApp 的 Noise 协议不被 MITM 检查:

docker sandbox network proxy shell-nanoclaw-workspace \
  --bypass-host web.whatsapp.com \
  --bypass-host "*.whatsapp.com" \
  --bypass-host "*.whatsapp.net"

Telegram 不需要代理绕过。

进入沙盒:

docker sandbox run shell-nanoclaw-workspace

步骤 2安装前提依赖

在沙盒内:

sudo apt-get update && sudo apt-get install -y build-essential python3
npm config set strict-ssl false

步骤 3克隆并安装 NanoClaw

NanoClaw 必须位于工作区目录内——Docker-in-Docker 只能从共享的工作区路径进行 bind-mount。

# 先克隆到家目录virtiofs 可能在克隆期间损坏 git pack 文件)
cd ~
git clone https://github.com/nanocoai/nanoclaw.git

# 替换为你的工作区路径(你传递给 `docker sandbox create` 的宿主机路径)
WORKSPACE=/Users/you/nanoclaw-workspace

# 移入工作区,以便 DinD 挂载生效
mv nanoclaw "$WORKSPACE/nanoclaw"
cd "$WORKSPACE/nanoclaw"

# 安装依赖
pnpm install
pnpm install https-proxy-agent

步骤 4应用代理和沙盒补丁

NanoClaw 需要多个补丁才能在 Docker 沙盒内工作。这些补丁处理代理路由、CA 证书和 Docker-in-Docker 挂载限制。

4a. Dockerfile——用于容器镜像构建的代理参数

sandbox 内的 docker build 会因为沙盒的 MITM 代理提供自己的证书而出现 SELF_SIGNED_CERT_IN_CHAIN 失败。向 container/Dockerfile 添加代理构建参数:

FROM 行之后添加这些行:

# 接受代理构建参数
ARG http_proxy
ARG https_proxy
ARG no_proxy
ARG NODE_EXTRA_CA_CERTS
ARG npm_config_strict_ssl=true
RUN npm config set strict-ssl ${npm_config_strict_ssl}

并在 RUN pnpm install 行之后:

RUN npm config set strict-ssl true

4b. 构建脚本——转发代理参数

补丁 container/build.sh,将代理环境变量传递给 docker build

docker build 命令中添加这些 --build-arg 标志:

--build-arg http_proxy="${http_proxy:-$HTTP_PROXY}" \
--build-arg https_proxy="${https_proxy:-$HTTPS_PROXY}" \
--build-arg no_proxy="${no_proxy:-$NO_PROXY}" \
--build-arg npm_config_strict_ssl=false \

4c. 容器运行器——代理转发、CA 证书挂载、/dev/null 修复

src/container-runner.ts 的三处更改:

替换 /dev/null 影子挂载。 沙盒拒绝 /dev/null bind mount。找到 .env 被影子挂载到 /dev/null 的位置,将其替换为一个空文件:

// 创建一个空文件来影子 .envDocker 沙盒拒绝 /dev/null 挂载)
const emptyEnvPath = path.join(DATA_DIR, 'empty-env');
if (!fs.existsSync(emptyEnvPath)) fs.writeFileSync(emptyEnvPath, '');
// 在挂载中使用 emptyEnvPath 代替 '/dev/null'

将代理环境变量转发到生成的 agent 容器。为 HTTP_PROXYHTTPS_PROXYNO_PROXY 及其小写变体添加 -e 标志。

挂载 CA 证书。 如果设置了 NODE_EXTRA_CA_CERTSSSL_CERT_FILE,将证书复制到项目目录并挂载到 agent 容器中:

const caCertSrc = process.env.NODE_EXTRA_CA_CERTS || process.env.SSL_CERT_FILE;
if (caCertSrc) {
  const certDir = path.join(DATA_DIR, 'ca-cert');
  fs.mkdirSync(certDir, { recursive: true });
  fs.copyFileSync(caCertSrc, path.join(certDir, 'proxy-ca.crt'));
  // 挂载certDir -> /workspace/ca-cert只读
  // 在容器中设置 NODE_EXTRA_CA_CERTS=/workspace/ca-cert/proxy-ca.crt
}

4d. 容器运行时——防止自我终止

src/container-runtime.ts 中,cleanupOrphans() 函数通过 nanoclaw- 前缀匹配容器。在沙盒内,沙盒容器本身可能匹配(例如 nanoclaw-docker-sandbox)。过滤掉当前主机名:

// 在 cleanupOrphans() 中,从要停止的容器列表中过滤掉 os.hostname()

4e. 凭据代理——通过 MITM 代理路由

src/credential-proxy.ts 中,上游 API 请求需要通过沙盒代理。向出站请求添加 HttpsProxyAgent

import { HttpsProxyAgent } from 'https-proxy-agent';

const proxyUrl = process.env.HTTPS_PROXY || process.env.https_proxy;
const upstreamAgent = proxyUrl ? new HttpsProxyAgent(proxyUrl) : undefined;
// 将 upstreamAgent 传递给 https.request() 选项

4f. 安装脚本——代理构建参数

补丁 setup/container.ts,传递与 build.sh(步骤 4b相同的代理 --build-arg 标志。

步骤 5构建

pnpm run build
bash container/build.sh

步骤 6添加频道

Telegram

# 应用 Telegram 技能
pnpm exec tsx scripts/apply-skill.ts .claude/skills/add-telegram

# 应用技能后重建
pnpm run build

# 配置 .env
cat > .env << EOF
TELEGRAM_BOT_TOKEN=<your-token-from-botfather>
ASSISTANT_NAME=nanoclaw
ANTHROPIC_API_KEY=proxy-managed
EOF
mkdir -p data/env && cp .env data/env/env

# 注册你的聊天
pnpm exec tsx setup/index.ts --step register \
  --jid "tg:<your-chat-id>" \
  --name "My Chat" \
  --trigger "@nanoclaw" \
  --folder "telegram_main" \
  --channel telegram \
  --assistant-name "nanoclaw" \
  --is-main \
  --no-trigger-required

查找你的 chat ID 向你的 bot 发送任意消息,然后:

curl -s --proxy $HTTPS_PROXY "https://api.telegram.org/bot<TOKEN>/getUpdates" | python3 -m json.tool

群组中的 Telegram 在 @BotFather 中禁用群组隐私(/mybots > Bot Settings > Group Privacy > Turn off然后移除并重新添加 bot。

重要提示: 如果 Telegram 技能创建了 src/channels/telegram.ts,你需要为代理支持打补丁。添加一个 HttpsProxyAgent 并通过 baseFetchConfig.agent 将其传递给 grammy 的 Bot 构造函数。然后重建。

WhatsApp

确保你已先在步骤 1中配置了代理绕过。

# 应用 WhatsApp 技能
pnpm exec tsx scripts/apply-skill.ts .claude/skills/add-whatsapp

# 重建
pnpm run build

# 配置 .env
cat > .env << EOF
ASSISTANT_NAME=nanoclaw
ANTHROPIC_API_KEY=proxy-managed
EOF
mkdir -p data/env && cp .env data/env/env

# 认证(选择一种):

# QR 码——用 WhatsApp 相机扫描:
pnpm exec tsx src/whatsapp-auth.ts

# 或配对码——在 WhatsApp > 已关联设备 > 通过电话号码关联中输入代码:
pnpm exec tsx src/whatsapp-auth.ts --pairing-code --phone <phone-number-no-plus>

# 注册你的聊天JID = 你的电话号码 + @s.whatsapp.net
pnpm exec tsx setup/index.ts --step register \
  --jid "<phone>@s.whatsapp.net" \
  --name "My Chat" \
  --trigger "@nanoclaw" \
  --folder "whatsapp_main" \
  --channel whatsapp \
  --assistant-name "nanoclaw" \
  --is-main \
  --no-trigger-required

重要提示: WhatsApp 技能文件(src/channels/whatsapp.tssrc/whatsapp-auth.ts)也需要代理补丁——为 WebSocket 连接添加 HttpsProxyAgent 以及一个支持代理的版本获取。然后重建。

两个频道

应用两个技能,为两者打代理补丁,合并 .env 变量,并分别注册每个聊天。

步骤 7运行

pnpm start

你不需要手动设置 ANTHROPIC_API_KEY。沙盒代理拦截请求,并自动将 proxy-managed 替换为你的真实密钥。

网络细节

代理如何工作

沙盒的所有流量通过宿主机代理路由,地址为 host.docker.internal:3128

Agent 容器 → DinD 网桥 → 沙盒 VM → host.docker.internal:3128 → 宿主机代理 → api.anthropic.com

"绕过"并不意味着流量跳过代理。 它意味着代理在不做 MITM 检查的情况下传递流量。Node.js 不会自动使用 HTTP_PROXY 环境变量——你需要在每个 HTTP/WebSocket 客户端中显式配置 HttpsProxyAgent

DinD 挂载的共享路径

只有工作区目录可用于 Docker-in-Docker bind mount。工作区之外的路径会失败并显示"path not shared"

  • /dev/null → 改为项目目录中的空文件
  • /usr/local/share/ca-certificates/ → 将证书复制到项目目录
  • /home/agent/ → 改为克隆到工作区

Git clone 和 virtiofs

工作区通过 virtiofs 挂载。Git 的 pack 文件处理在 virtiofs 上可能在 clone 期间损坏。解决方法:先 clone 到 /home/agent,然后 mv 到工作区。

故障排查

pnpm install 失败并出现 SELF_SIGNED_CERT_IN_CHAIN

npm config set strict-ssl false

容器构建失败并出现代理错误

docker build \
  --build-arg http_proxy=$http_proxy \
  --build-arg https_proxy=$https_proxy \
  -t nanoclaw-agent:latest container/

Agent 容器失败并出现 "path not shared"

所有 bind-mounted 路径必须在工作区目录下。检查:

  • NanoClaw 是否克隆到了工作区?(不是 /home/agent/
  • CA 证书是否复制到了项目根目录?
  • 空的 .env 影子文件是否已创建?

Agent 容器无法访问 Anthropic API

验证代理环境变量是否转发到了 agent 容器。检查容器日志中的 HTTP_PROXY=http://host.docker.internal:3128

WhatsApp 错误 405

版本获取返回了过时的版本。确保已应用支持代理的 fetchWaVersionViaProxy 补丁——它通过 HttpsProxyAgent 获取 sw.js 并解析 client_revision

WhatsApp 立即显示 "Connection failed"

代理绕过未配置。从宿主机运行:

docker sandbox network proxy <sandbox-name> \
  --bypass-host web.whatsapp.com \
  --bypass-host "*.whatsapp.com" \
  --bypass-host "*.whatsapp.net"

Telegram bot 不接收消息

  1. 检查 grammy 代理补丁已应用(在 src/channels/telegram.ts 中查找 HttpsProxyAgent
  2. 如果在群组中使用,检查 @BotFather 中群组隐私已禁用

Git clone 失败并出现 "inflate: data stream error"

先 clone 到非工作区路径,然后移动:

cd ~ && git clone https://github.com/nanocoai/nanoclaw.git && mv nanoclaw /path/to/workspace/nanoclaw

WhatsApp QR 码不显示

在沙盒内以交互方式运行认证命令(不通过 docker sandbox exec 管道):

docker sandbox run shell-nanoclaw-workspace
# 然后在沙盒内:
pnpm exec tsx src/whatsapp-auth.ts