跳到主内容
快讯直播
AI智模界
教程

用 Pi pod 在自有服务器上搭建编码智能体沙箱

在 Show HN 上出现的 Pi pod,思路是把「编码智能体」跑在一个一次性的、被限制住手脚的容器里:它能读写你指定的代码目录、能执行命令,但碰不到宿主机的其他文件,也默认连不上公网。下面这份教程,是在自己的服务器上把这套沙箱搭起来、把隔离做扎实、再往里派活的完整流程。

> 说明:Pi pod 的镜像名、CLI 子命令、端口与配置项会随版本变化。下文给出的命令是通用的容器与网络写法,可直接改用的模板,具体字段请以官方文档当前版本和 --help 输出为准。

适用场景

适合已经在服务器上跑模型 API、想让编码智能体帮忙改代码、跑测试、做批量重构,但又不放心把整台机器交给它的人。典型场景有三类:让智能体自动修 CI 报错、在隔离环境里跑不可信的第三方脚本、给团队里多个成员各发一个互不干扰的临时工作区。核心诉求是「给它一个目录和一条任务,跑完就回收,出不了圈」。

环境与前置条件

  • 操作系统:Linux(x86_64 或 arm64),内核 5.x 以上,推荐 Ubuntu 22.04 / Debian 12 一类的长期支持发行版。
  • 运行时:Docker Engine 与 Docker Compose v2 插件(docker compose 子命令形式)。安装方式以 Docker 官方文档当前版本为准。
  • 权限:能执行 docker 的普通用户即可,不建议全程用 root 操作;把用户加入 docker 组。
  • 资源建议:单任务沙箱 2 核 CPU、2~4 GB 内存起步,工作区磁盘按仓库大小预留,另外给 /var/lib/docker 留出至少 20 GB。并发几个沙箱就按倍数放大。
  • 网络:服务器本身能出网(拉镜像、调模型 API),沙箱容器默认不出网。
  • 凭据:模型服务的 API Key 或自建推理服务的地址,放在环境变量文件里,不要写进镜像。

分步骤部署

第 1 步:建一个专用目录,放配置和密钥

```bash

sudo mkdir -p /opt/pi-pod/{config,workspaces,secrets}

sudo chown -R "$USER":"$USER" /opt/pi-pod

cd /opt/pi-pod

```

config 放沙箱的配置文件,workspaces 放各个任务的工作目录,secrets 放模型凭据。把三者分开,是为了后面备份和权限收窄都方便。

第 2 步:准备模型凭据文件

```bash

cat > /opt/pi-pod/secrets/model.env <<'EOF'

MODEL_BASE_URL=https://your-endpoint.example.com/v1

MODEL_API_KEY=replace-with-your-key

MODEL_NAME=your-model-name

EOF

chmod 600 /opt/pi-pod/secrets/model.env

```

这个文件只被 compose 读取并注入容器环境变量,不打进镜像,也不会随工作区一起挂载进去。

第 3 步:写一份 docker-compose.yml

下面这份模板把隔离项都打开了:只读根文件系统、丢弃全部 Linux capability、禁止提权、限制进程数、非 root 用户运行。

```yaml

/opt/pi-pod/docker-compose.yml

services:

pi-pod:

image: ${PI_POD_IMAGE:?请在 .env 中设置镜像名}

container_name: pi-pod

restart: unless-stopped

env_file:

  • ./secrets/model.env

environment:

  • SANDBOX_WORKSPACE=/workspace
  • SANDBOX_NETWORK_POLICY=deny # 默认拒绝出网
  • HTTP_PROXY=${SANDBOX_PROXY:-}
  • HTTPS_PROXY=${SANDBOX_PROXY:-}

volumes:

  • ./config:/etc/pi-pod:ro
  • ./workspaces:/workspace

tmpfs:

  • /tmp:size=512m,mode=1777

read_only: true

user: "1000:1000"

cap_drop:

  • ALL

security_opt:

  • no-new-privileges:true

pids_limit: 512

mem_limit: 4g

cpus: 2.0

networks:

  • sandbox_net

healthcheck:

test: ["CMD", "pi-pod", "healthcheck"]

interval: 30s

timeout: 5s

retries: 3

start_period: 20s

networks:

sandbox_net:

driver: bridge

internal: true # 关键:不给这个网络配默认网关

```

同时建一个 .env:

```bash

cat > /opt/pi-pod/.env <<'EOF'

PI_POD_IMAGE=以官方文档当前版本的镜像名为准

EOF

```

internal: true 让容器只有内部 IP、没有默认路由,沙箱里 curl 外网会直接失败。如果模型需要联网调用,把 SANDBOX_PROXY 指向一个白名单代理,只放通模型域名,比直接给沙箱开公网安全得多。

第 4 步:准备第一个工作区

```bash

mkdir -p /opt/pi-pod/workspaces/demo

cd /opt/pi-pod/workspaces/demo

git clone --depth 1 https://example.com/your/repo.git .

```

注意容器内的用户是 UID 1000,宿主机上工作区目录的属主也要对得上,否则沙箱写文件会报权限错误:

```bash

sudo chown -R 1000:1000 /opt/pi-pod/workspaces/demo

```

第 5 步:启动沙箱

```bash

cd /opt/pi-pod

docker compose pull

docker compose up -d

docker compose ps

```

docker compose ps 里 STATUS 一列从 starting 变成 healthy,就说明沙箱起来了。首次启动会拉镜像,耐心等一会儿。

第 6 步:下发一个任务

沙箱本身只是个执行环境,任务是「扔进去」的。两种常见方式:

方式一,用 CLI 一次性下发(跑完即走):

```bash

docker compose exec -T pi-pod \

pi-pod run \

--workspace /workspace/demo \

--task "修复测试失败:先运行测试,定位失败用例,改代码后重新运行验证" \

--timeout 600

```

方式二,走 HTTP 接口,适合接到自己的流水线里:

```bash

curl -fsS -X POST http://127.0.0.1:8080/tasks \

-H 'Content-Type: application/json' \

-d '{

"workspace": "/workspace/demo",

"task": "把 README 里的安装步骤更新为当前实际的依赖安装方式",

"timeout_seconds": 300

}'

```

接口路径、端口与字段名请以官方文档当前版本为准;pi-pod --help 与 pi-pod run --help 是最快的确认方式。

验证部署是否成功

分四层验证,逐层过一遍:

第一层,容器在跑且健康:

```bash

docker compose ps

docker inspect --format '{{.State.Health.Status}}' pi-pod

```

预期输出 healthy。

第二层,隔离确实生效。检查只读根和网络:

```bash

docker inspect --format '{{.HostConfig.ReadonlyRootfs}}' pi-pod

docker inspect --format '{{.HostConfig.NetworkMode}}' pi-pod

docker compose exec -T pi-pod sh -c 'touch /should-fail' || echo "根文件系统只读,符合预期"

docker compose exec -T pi-pod sh -c 'curl -m 5 -sS https://example.com >/dev/null && echo 能出网 || echo 出网被拦,符合预期'

```

预期是第一条 ReadonlyRootfs 为 true,第二条输出「根文件系统只读,符合预期」,第三条输出「出网被拦,符合预期」。

第三层,工作区可写、任务有产物:

```bash

docker compose exec -T pi-pod sh -c 'echo hello > /workspace/demo/.sandbox-probe'

cat /opt/pi-pod/workspaces/demo/.sandbox-probe

```

宿主机能看到 hello,说明挂载和 UID 都对。

第四层,真的能干活。下发一个最小任务:

```bash

docker compose exec -T pi-pod \

pi-pod run --workspace /workspace/demo --task "在当前目录新建 NOTES.md,写一行今天的日期"

ls -l /opt/pi-pod/workspaces/demo/NOTES.md

```

文件出现在宿主机目录里,且容器日志里能看到任务从开始到结束的完整过程,就算部署成功。

常见报错与解决

报错:permission denied while trying to connect to the Docker daemon socket at unix:///var/run/docker.sock

原因:当前用户不在 docker 组,没有权限访问 Docker 守护进程。

解决:

```bash

sudo usermod -aG docker "$USER"

newgrp docker # 或退出后重新登录

docker info

```

报错:容器启动后立刻退出,日志里出现 401 Unauthorized 或 MODEL_API_KEY is not set

原因:凭据没注入成功,通常是 env_file 路径写错,或者变量名和沙箱期望的不一致。

解决:

```bash

cd /opt/pi-pod

docker compose config | grep -A3 environment # 确认变量被正确解析

docker compose logs --tail 100 pi-pod

```

确认变量名与官方文档一致后,docker compose up -d --force-recreate。

报错:沙箱内 git clone 或调用模型接口超时 connection timed out

原因:internal: true 的网络没有出口,这是隔离生效的表现。需要出网的任务必须走代理。

解决:在 .env 里设置 SANDBOX_PROXY=http://宿主内网IP:代理端口,代理侧只放通模型与代码托管域名,然后 docker compose up -d 重建容器。

报错:沙箱写工作区时 Permission denied

原因:容器内是 UID 1000,宿主机工作区目录属主不是它。

解决:

```bash

sudo chown -R 1000:1000 /opt/pi-pod/workspaces/<任务目录>

```

报错:任务中途被 Killed,或 docker compose up 报磁盘不足

原因:内存或磁盘触顶,也可能是进程数被 pids_limit 卡住。

解决:

```bash

docker system df # 看镜像和卷占了多少

docker system prune -f # 清理停止的容器、悬空镜像

```

再按需调大 compose 里的 mem_limit、cpus、pids_limit。

后续维护

备份。 需要备份的是三样:/opt/pi-pod/config(沙箱配置)、/opt/pi-pod/workspaces(任务产物)、以及你自己的 .env 与凭据文件。镜像是可以重新拉的,不必备份。工作区本质是 Git 仓库,产物尽量通过提交和推送落到远端,宿主机上只留短期副本。

升级。 镜像不要用 latest 之类的浮动标签,改用带版本或 digest 的固定标签,升级前先在 /opt/pi-pod/staging 里用同一份 compose 起一个副本试跑一个任务,确认没问题再切生产。升级流程固定为:docker compose pull → docker compose up -d → 跑一遍上面的四层验证。

日志。 容器日志默认走 json-file 驱动,长期跑会吃满磁盘,给它加上轮转:

```yaml

logging:

driver: json-file

options:

max-size: "20m"

max-file: "5"

```

排查问题时用 docker compose logs --since 30m pi-pod 看时间窗内的日志。每个任务建议在日志里打出任务 ID、工作区路径、耗时和退出码,方便事后对齐。

监控。 healthcheck 已经在 compose 里配好了,再把它接到监控系统上:用 node-exporter 看宿主机的 CPU、内存、磁盘,用 cAdvisor 看容器级资源,用 Prometheus + Alertmanager 对「容器不健康」「磁盘使用率超过 80%」「任务连续失败」三类情况告警。沙箱的价值在于「圈得住」,而圈得住的前提是你知道它在里面干了什么。

收尾习惯。 每个任务用一个独立的工作区目录,跑完把目录归档或删掉,不要多个任务共用同一个目录;API Key 定期轮换;定期执行一次 docker system prune,并复核 cap_drop、read_only、internal 这三个隔离开关有没有被谁改掉。

AI 生成本文由 AI 基于公开信息自动生成,仅供参考。