这是本节的多页打印视图。 点击此处打印.

返回本页常规视图.

移植

移植 E2B 到其他运行环境

1 - 镜像构建

E2B 基础镜像构建

准备工作

编译 envd 二进制

在本地宿主机(linux)上克隆并编译 envd:

# 回到 base 的上级目录
cd /home/sky/work/code/e2b/envd-image-builder

# 克隆官方 infra 仓库(如果慢可以换 github 镜像源)
git clone https://github.com/e2b-dev/infra.git

# 进入 envd 目录
cd infra/packages/envd

# 静态编译出 Linux x64 的二进制文件
CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -ldflags="-s -w" -o ../../../base/envd main.go

代码定制

修改默认 host IP

https://deepwiki.com/search/envd-host-ip-169254021_afda741e-0447-47c8-b19d-a63e1c3fa86a?mode=fast

envd 默认使用的 host IP 是 169.254.0.21 ,通过硬编码的方式配置,代码在 packages/envd/internal/port/forward.go

var defaultGatewayIP = net.IPv4(169, 254, 0, 21)

移植到其他环境之后,如果需要修改这个 host IP,比如修改为其他固定值,可以简单修改这行代码。

如果是移植到 k8s 下,sandbox 运行在 pod 中,这里的 host IP 就不是固定值,而是所在 pod 的被分配的 IP 地址。则需要修改 envd 代码,通过某种方式传入 pod IP。

TODO:看怎么修改代码合适。

base 镜像

构建 base 镜像

base 镜像的 Dockerfile:

FROM debian:bookworm-slim

ENV DEBIAN_FRONTEND=noninteractive

# 安装基础系统工具
RUN apt-get update && apt-get install -y --no-install-recommends \
    bash ca-certificates curl git openssh-client procps sudo unzip wget \
    && rm -rf /var/lib/apt/lists/*

# 创建用于 AI 执行代码的普通隔离用户(让 envd 拥有 root 权限,但 AI 执行代码时可以用这个低权限用户)
RUN useradd -m -s /bin/bash sandboxuser && \
    echo "sandboxuser ALL=(ALL) NOPASSWD:ALL" >> /etc/sudoers

# 直接把我们在本地编译好的 envd 复制进去
COPY envd /usr/local/bin/envd

# 显式指定工作目录并保持以 ROOT 用户身份运行
WORKDIR /root
USER root

# 暴露官方标准的 49983 端口
EXPOSE 49983

ENTRYPOINT ["/usr/local/bin/envd"]

# 生产默认配置:开启 49983 端口,非纯 Firecracker 兼容模式,开启详细日志
CMD ["-port", "49983", "-isnotfc", "-verbose"]

构建镜像:

docker build -t e2b-base:latest .

查看本地构建好的 e2b-base 镜像:

$ docker images                    

IMAGE                                          ID             DISK USAGE   CONTENT SIZE   EXTRA 
e2b-base:latest                                28d8689e05f8        289MB         72.9MB    U   

启动 base 镜像

直接在本机用 docker 启动上面构建好的 e2b base 镜像:

docker run -d \
  --name e2b-base-test \
  --privileged \
  -p 49983:49983 \
  e2b-base:latest

使用完成之后,或者更新代码后重新构建重新运行,关闭已经运行的容器:

docker rm -f e2b-base-test

code interpreter 镜像

TODO

2 - API验证

E2B 基础镜像的功能验证

base 镜像

使用上一节构建好的 base 镜像启动 docker,然后验证相关的数据面 API。

envd API

https://e2b.dev/docs/api-reference/envd/check-the-health-of-the-service

Check the health of the service:

curl -i http://localhost:49983/health

HTTP/1.1 204 No Content
Cache-Control: no-store
Content-Type: 
Vary: Origin
Date: Mon, 01 Jun 2026 08:05:52 GMT

Get the stats of the service

curl --request GET --url 'http://localhost:49983/metrics' 

{"ts":1780301268,"cpu_count":32,"cpu_used_pct":0.77,"mem_total_mib":64085,"mem_used_mib":3932,"mem_total":67198607360,"mem_used":4123037696,"mem_cache":4104839168,"disk_used":111465947136,"disk_total":966455894016}

Get the environment variables

curl --request GET --url 'http://localhost:49983/envs' 

小结:功能都可用。

file system API

ListDir

curl --request POST --url 'http://localhost:49983/filesystem.Filesystem/ListDir'  \
	--header 'Content-Type: application/json' \
  --data '
{
  "path": "/",
  "depth": 1
}
'

{"entries":[{"name":".dockerenv", "type":"FILE_TYPE_FILE", "path":"/.dockerenv", "mode":493, "permissions":"-rwxr-xr-x", "owner":"root", "group":"root", "modifiedTime":"2026-06-01T07:59:46.609468959Z"}, {"name":"bin", "type":"FILE_TYPE_DIRECTORY", "path":"/bin", "size":"7", "mode":493, "permissions":"Lrwxrwxrwx", "owner":"root", "group":"root", "modifiedTime":"2026-05-18T00:00:00Z", "symlinkTarget":"/usr/bin"}, {"name":"boot", "type":"FILE_TYPE_DIRECTORY", "path":"/boot", "size":"4096", "mode":493, "permissions":"drwxr-xr-x", "owner":"root", "group":"root", "modifiedTime":"2026-05-08T16:15:00Z"}, {"name":"dev", "type":"FILE_TYPE_DIRECTORY", "path":"/dev", "size":"4580", "mode":493, "permissions":"drwxr-xr-x", "owner":"root", "group":"root", "modifiedTime":"2026-06-01T07:59:46.698102456Z"}, {"name":"etc", "type":"FILE_TYPE_DIRECTORY", "path":"/etc", "size":"4096", "mode":493, "permissions":"drwxr-xr-x", "owner":"root", "group":"root", "modifiedTime":"2026-06-01T07:59:46.609639982Z"}, {"name":"home", "type":"FILE_TYPE_DIRECTORY", "path":"/home", "size":"4096", "mode":493, "permissions":"drwxr-xr-x", "owner":"root", "group":"root", "modifiedTime":"2026-06-01T07:27:57Z"}, {"name":"lib", "type":"FILE_TYPE_DIRECTORY", "path":"/lib", "size":"7", "mode":493, "permissions":"Lrwxrwxrwx", "owner":"root", "group":"root", "modifiedTime":"2026-05-18T00:00:00Z", "symlinkTarget":"/usr/lib"}, {"name":"lib64", "type":"FILE_TYPE_DIRECTORY", "path":"/lib64", "size":"9", "mode":493, "permissions":"Lrwxrwxrwx", "owner":"root", "group":"root", "modifiedTime":"2026-05-18T00:00:00Z", "symlinkTarget":"/usr/lib64"}, {"name":"media", "type":"FILE_TYPE_DIRECTORY", "path":"/media", "size":"4096", "mode":493, "permissions":"drwxr-xr-x", "owner":"root", "group":"root", "modifiedTime":"2026-05-18T00:00:00Z"}, {"name":"mnt", "type":"FILE_TYPE_DIRECTORY", "path":"/mnt", "size":"4096", "mode":493, "permissions":"drwxr-xr-x", "owner":"root", "group":"root", "modifiedTime":"2026-05-18T00:00:00Z"}, {"name":"opt", "type":"FILE_TYPE_DIRECTORY", "path":"/opt", "size":"4096", "mode":493, "permissions":"drwxr-xr-x", "owner":"root", "group":"root", "modifiedTime":"2026-05-18T00:00:00Z"}, {"name":"proc", "type":"FILE_TYPE_DIRECTORY", "path":"/proc", "mode":365, "permissions":"dr-xr-xr-x", "owner":"root", "group":"root", "modifiedTime":"2026-06-01T07:59:46.670390345Z"}, {"name":"root", "type":"FILE_TYPE_DIRECTORY", "path":"/root", "size":"4096", "mode":448, "permissions":"drwx------", "owner":"root", "group":"root", "modifiedTime":"2026-05-18T00:00:00Z"}, {"name":"run", "type":"FILE_TYPE_DIRECTORY", "path":"/run", "size":"4096", "mode":493, "permissions":"drwxr-xr-x", "owner":"root", "group":"root", "modifiedTime":"2026-06-01T07:59:46.742410856Z"}, {"name":"sbin", "type":"FILE_TYPE_DIRECTORY", "path":"/sbin", "size":"8", "mode":493, "permissions":"Lrwxrwxrwx", "owner":"root", "group":"root", "modifiedTime":"2026-05-18T00:00:00Z", "symlinkTarget":"/usr/sbin"}, {"name":"srv", "type":"FILE_TYPE_DIRECTORY", "path":"/srv", "size":"4096", "mode":493, "permissions":"drwxr-xr-x", "owner":"root", "group":"root", "modifiedTime":"2026-05-18T00:00:00Z"}, {"name":"sys", "type":"FILE_TYPE_DIRECTORY", "path":"/sys", "mode":365, "permissions":"dr-xr-xr-x", "owner":"root", "group":"root", "modifiedTime":"2026-06-01T07:59:46.671390630Z"}, {"name":"tmp", "type":"FILE_TYPE_DIRECTORY", "path":"/tmp", "size":"4096", "mode":511, "permissions":"dtrwxrwxrwx", "owner":"root", "group":"root", "modifiedTime":"2026-05-18T00:00:00Z"}, {"name":"usr", "type":"FILE_TYPE_DIRECTORY", "path":"/usr", "size":"4096", "mode":493, "permissions":"drwxr-xr-x", "owner":"root", "group":"root", "modifiedTime":"2026-05-18T00:00:00Z"}, {"name":"var", "type":"FILE_TYPE_DIRECTORY", "path":"/var", "size":"4096", "mode":493, "permissions":"drwxr-xr-x", "owner":"root", "group":"root", "modifiedTime":"2026-05-18T00:00:00Z"}]}% 

Download a file

curl -i --request GET --get \
  --data-urlencode "path=/etc/hosts" \
  http://localhost:49983/files
  
HTTP/1.1 200 OK
Accept-Ranges: bytes
Content-Disposition: inline; filename=hosts
Content-Length: 172
Content-Type: text/plain; charset=utf-8
Last-Modified: Mon, 01 Jun 2026 07:59:46 GMT
Vary: Accept-Encoding
Date: Mon, 01 Jun 2026 08:27:59 GMT

127.0.0.1	localhost
::1	localhost ip6-localhost ip6-loopback
fe00::	ip6-localnet
ff00::	ip6-mcastprefix
ff02::1	ip6-allnodes
ff02::2	ip6-allrouters
172.17.0.2	a3c723dfcd0a

process API

start process:

(printf "\x00\x00\x00\x00\x2a" && printf '{"process":{"cmd":"sleep","args":["300"]}}') | curl -i --no-buffer --request POST \
  --url 'http://localhost:49983/process.Process/Start' \
  --header 'Content-Type: application/connect+json' \
  --data-binary @- \
  --output output.bin

List process:

启动 process 之后,在另外一个终端中操作:

# 1. 看看 Docker 容器里有没有这个 sleep 300 现行
docker exec e2b-base-test ps aux

# 2. 看看 envd 内部的 List 注册表有没有抓到它
curl -i --request POST \
  --url 'http://localhost:49983/process.Process/List' \
  --header 'Content-Type: application/json' \
  --data '{}'

init 内部接口

设置环境变量

先通过 init 接口设置环境变量 AI_PROVIDER 和 SANDBOX_STAGE:

 curl -i --request POST \
  --url 'http://localhost:49983/init' \
  --header 'Content-Type: application/json' \
  --data '{
    "envVars": {
      "AI_PROVIDER": "NacreAgent2",
      "SANDBOX_STAGE": "Development2"
    }
  }'
  
  
HTTP/1.1 204 No Content
Cache-Control: no-store
Vary: Origin
Date: Mon, 01 Jun 2026 09:33:02 GMT

启动进程,这个进程通过 env 命个令打印传递给它的环境变量:

(printf "\x00\x00\x00\x00\x23" && printf '{"process":{"cmd":"env","args":[]}}') | curl -i --no-buffer --request POST \
  --url 'http://localhost:49983/process.Process/Start' \
  --header 'Content-Type: application/connect+json' \
  --data-binary @- \
  --output init_env.bin
  
  
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100   419    0   379  100    40   115k  12488 --:--:-- --:--:-- --:--:--  136k

strings init_env.bin 查看输出:

strings init_env.bin


HTTP/1.1 200 OK
Connect-Accept-Encoding: gzip
Content-Type: application/connect+json
Vary: Origin
Date: Mon, 01 Jun 2026 09:35:01 GMT
Transfer-Encoding: chunked
{"event":{"start":{"pid":62}}}
{"event":{"data":{"stdout":"RTJCX1NBTkRCT1g9ZmFsc2UKVVNFUj1yb290CkhPTUU9L3Jvb3QKTE9HTkFNRT1yb290CkFJX1BST1ZJREVSPU5hY3JlQWdlbnQKUEFUSD0vdXNyL2xvY2FsL3NiaW46L3Vzci9sb2NhbC9iaW46L3Vzci9zYmluOi91c3IvYmluOi9zYmluOi9iaW4KU0FOREJPWF9TVEFHRT1EZXZlbG9wbWVudApQV0Q9L3Jvb3QK"}}}
;{"event":{"end":{"exited":true, "status":"exit status 0"}}}

把 base64 的数据打印出来

echo "RTJCX1NBTkRCT1g9ZmFsc2UKVVNFUj1yb290CkhPTUU9L3Jvb3QKTE9HTkFNRT1yb290CkFJX1BST1ZJREVSPU5hY3JlQWdlbnQKUEFUSD0vdXNyL2xvY2FsL3NiaW46L3Vzci9sb2NhbC9iaW46L3Vzci9zYmluOi91c3IvYmluOi9zYmluOi9iaW4KU0FOREJPWF9TVEFHRT1EZXZlbG9wbWVudApQV0Q9L3Jvb3QK" | base64 -d

这是 env 命令的输出:

E2B_SANDBOX=false
USER=root
HOME=/root
LOGNAME=root
AI_PROVIDER=NacreAgent
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
SANDBOX_STAGE=Development
PWD=/root

之前设置的 “AI_PROVIDER”: “NacreAgent” 和 “SANDBOX_STAGE”: “Development” 被正确的传递到新启动的进程了。验证OK!

也可以简单的直接调用 envd 的接口查看环境变量:

curl --request GET --url 'http://localhost:49983/envs' 

{"AI_PROVIDER":"NacreAgent","E2B_SANDBOX":"false","SANDBOX_STAGE":"Development"}

设置 access token

通过 init 接口设置 access token:

 curl -i --request POST \
  --url 'http://localhost:49983/init' \
  --header 'Content-Type: application/json' \
  --data '{
  	"accessToken": "your-secret-token",
    "envVars": {
      "AI_PROVIDER": "NacreAgent2",
      "SANDBOX_STAGE": "Development2"
    }
  }'

code interpreter 镜像

TODO