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

360 lines
12 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 在 Docker 沙盒中运行 NanoClaw手动设置
本指南介绍如何从头开始在 [Docker 沙盒](https://docs.docker.com/ai/sandboxes/)中设置 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](https://t.me/BotFather) 的 bot token 和你的 chat ID
- 对于 **WhatsApp**:一部安装了 WhatsApp 的手机
验证沙盒支持:
```bash
docker sandbox version
```
## 步骤 1创建沙盒
在你的宿主机上:
```bash
# 创建工作区目录
mkdir -p ~/nanoclaw-workspace
# 创建带工作区挂载的 shell 沙盒
docker sandbox create shell ~/nanoclaw-workspace
```
如果你使用 WhatsApp配置代理绕过使 WhatsApp 的 Noise 协议不被 MITM 检查:
```bash
docker sandbox network proxy shell-nanoclaw-workspace \
--bypass-host web.whatsapp.com \
--bypass-host "*.whatsapp.com" \
--bypass-host "*.whatsapp.net"
```
Telegram 不需要代理绕过。
进入沙盒:
```bash
docker sandbox run shell-nanoclaw-workspace
```
## 步骤 2安装前提依赖
在沙盒内:
```bash
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。
```bash
# 先克隆到家目录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` 行之后添加这些行:
```dockerfile
# 接受代理构建参数
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` 行之后:
```dockerfile
RUN npm config set strict-ssl true
```
### 4b. 构建脚本——转发代理参数
补丁 `container/build.sh`,将代理环境变量传递给 `docker build`
`docker build` 命令中添加这些 `--build-arg` 标志:
```bash
--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` 的位置,将其替换为一个空文件:
```typescript
// 创建一个空文件来影子 .envDocker 沙盒拒绝 /dev/null 挂载)
const emptyEnvPath = path.join(DATA_DIR, 'empty-env');
if (!fs.existsSync(emptyEnvPath)) fs.writeFileSync(emptyEnvPath, '');
// 在挂载中使用 emptyEnvPath 代替 '/dev/null'
```
**将代理环境变量转发**到生成的 agent 容器。为 `HTTP_PROXY``HTTPS_PROXY``NO_PROXY` 及其小写变体添加 `-e` 标志。
**挂载 CA 证书。** 如果设置了 `NODE_EXTRA_CA_CERTS``SSL_CERT_FILE`,将证书复制到项目目录并挂载到 agent 容器中:
```typescript
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`)。过滤掉当前主机名:
```typescript
// 在 cleanupOrphans() 中,从要停止的容器列表中过滤掉 os.hostname()
```
### 4e. 凭据代理——通过 MITM 代理路由
`src/credential-proxy.ts` 中,上游 API 请求需要通过沙盒代理。向出站请求添加 `HttpsProxyAgent`
```typescript
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构建
```bash
pnpm run build
bash container/build.sh
```
## 步骤 6添加频道
### Telegram
```bash
# 应用 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 发送任意消息,然后:
```bash
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](#步骤-1创建沙盒)中配置了代理绕过。
```bash
# 应用 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.ts``src/whatsapp-auth.ts`)也需要代理补丁——为 WebSocket 连接添加 `HttpsProxyAgent` 以及一个支持代理的版本获取。然后重建。
### 两个频道
应用两个技能,为两者打代理补丁,合并 `.env` 变量,并分别注册每个聊天。
## 步骤 7运行
```bash
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
```bash
npm config set strict-ssl false
```
### 容器构建失败并出现代理错误
```bash
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"
代理绕过未配置。从**宿主机**运行:
```bash
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 到非工作区路径,然后移动:
```bash
cd ~ && git clone https://github.com/nanocoai/nanoclaw.git && mv nanoclaw /path/to/workspace/nanoclaw
```
### WhatsApp QR 码不显示
在沙盒内以交互方式运行认证命令(不通过 `docker sandbox exec` 管道):
```bash
docker sandbox run shell-nanoclaw-workspace
# 然后在沙盒内:
pnpm exec tsx src/whatsapp-auth.ts
```