跳到主要内容

DockerSandbox

在本机的 Docker 容器中执行命令。使用长期运行的容器,并通过 docker exec 执行命令。适用于本地开发、CI/CD、隔离网络部署,以及无需云端 Sandbox、对成本敏感的场景。有关接口详情,请参阅 WorkspaceSandbox 接口

安装
安装的直接链接

npm install @mastra/docker

要求宿主机上运行 Docker Engine

用法
用法的直接链接

DockerSandbox 添加到 Workspace,并将其分配给 Agent:

import { Agent } from '@mastra/core/agent'
import { Workspace } from '@mastra/core/workspace'
import { DockerSandbox } from '@mastra/docker'

const workspace = new Workspace({
sandbox: new DockerSandbox({
image: 'node:22-slim',
}),
})

const agent = new Agent({
id: 'dev-agent',
name: 'dev-agent',
model: 'anthropic/claude-opus-4-7',
workspace,
})

构造函数参数
构造函数参数的直接链接

id?:

string
= 自动生成
此 Sandbox 实例的唯一标识符。用于基于 label 重新连接。

name?:

string
= Sandbox 的 `id`
通过 --name 传给 Docker 的容器显示名称。[a-zA-Z0-9_.-] 之外的字符会替换为 -;如果结果不是以字母或数字开头,则会为其添加前缀。

image?:

string
= 'node:22-slim'
容器使用的 Docker 镜像。

command?:

string[]
= ['sleep', 'infinity']
容器 entrypoint 命令。必须让容器持续运行,以便通过 exec 执行命令。

env?:

Record<string, string>
要在容器中设置的环境变量。

volumes?:

Record<string, string>
宿主到容器的 bind mount。key 是宿主路径,value 是容器路径。

network?:

string
要加入的 Docker 网络。

privileged?:

boolean
= false
以 privileged 模式运行。

memory?:

number
以字节为单位的内存限制。Docker 将 0 视为无限制。映射到 Docker HostConfig.Memory。

memorySwap?:

number
以字节为单位的内存加 swap 总量。映射到 Docker HostConfig.MemorySwap。

cpuShares?:

number
CPU 份额的相对权重。映射到 Docker HostConfig.CpuShares。

cpuQuota?:

number
每个周期内以微秒为单位的 CPU 配额。映射到 Docker HostConfig.CpuQuota。

cpuPeriod?:

number
以微秒为单位的 CPU 周期。映射到 Docker HostConfig.CpuPeriod。

pidsLimit?:

number
容器中的最大进程 ID 数量。映射到 Docker HostConfig.PidsLimit。

readonlyRootfs?:

boolean
以只读方式挂载容器根文件系统。映射到 Docker HostConfig.ReadonlyRootfs。

capDrop?:

string[]
要移除的 Linux capability。使用 ['ALL'] 可先移除所有 capability,再添加特定 capability。映射到 Docker HostConfig.CapDrop。

capAdd?:

string[]
移除后要重新添加的 Linux capability,例如 NET_BIND_SERVICE。映射到 Docker HostConfig.CapAdd。

securityOpt?:

string[]
Docker 安全选项,例如 ['no-new-privileges:true']。映射到 Docker HostConfig.SecurityOpt。

ulimits?:

Array<{ name: string; soft: number; hard: number }>
容器的 ulimit 条目。映射到 Docker HostConfig.Ulimits。

tmpfs?:

Record<string, string>
tmpfs 挂载路径和选项。映射到 Docker HostConfig.Tmpfs。

workingDir?:

string
= '/workspace'
容器内的工作目录。

labels?:

Record<string, string>
额外的容器 label。始终包含 Mastra label(mastra.sandbox、mastra.sandbox.id)。

timeout?:

number
= 300000(5 分钟)
以毫秒为单位的默认命令超时时间。

dockerOptions?:

Docker.DockerOptions
直接传递 dockerode 连接选项,用于自定义 socket 路径、远程 host 或 TLS 证书。

instructions?:

string | function
覆盖 getInstructions() 所返回默认 instructions 的自定义 instructions。传入空字符串可禁止 instructions。

属性
属性的直接链接

id:

string
Sandbox 实例标识符。

name:

string
Provider 名称('DockerSandbox')。

provider:

string
Provider 标识符('docker')。

status:

ProviderStatus
'pending' | 'starting' | 'running' | 'stopping' | 'stopped' | 'destroying' | 'destroyed' | 'error'

container:

Container
底层 dockerode Container 实例。如果 Sandbox 尚未启动,则抛出 SandboxNotReadyError。

processes:

DockerProcessManager
后台进程管理器。请参阅 SandboxProcessManager 参考

后台进程
后台进程的直接链接

DockerSandbox 内置了用于生成和管理后台进程的进程管理器。进程使用 docker exec 在容器内运行。

const sandbox = new DockerSandbox({ id: 'dev-sandbox' })
await sandbox._start()

// Spawn a background process
const handle = await sandbox.processes.spawn('node server.js', {
env: { PORT: '3000' },
onStdout: data => console.log(data),
})

// Interact with the process
console.log(handle.stdout)
await handle.sendStdin('input\n')
await handle.kill()

有关完整 API,请参阅 SandboxProcessManager 参考

环境变量
环境变量的直接链接

使用 env 在容器级别设置环境变量。生成进程时也可以传入每个命令专属的环境变量:

const sandbox = new DockerSandbox({
image: 'node:22-slim',
env: {
NODE_ENV: 'production',
DATABASE_URL: 'postgres://localhost:5432/mydb',
},
})

Bind mount
Bind mount的直接链接

使用 volumes 选项将宿主目录挂载到容器中:

const sandbox = new DockerSandbox({
image: 'node:22-slim',
volumes: {
'/my/project': '/workspace/project',
'/shared/data': '/data',
},
})

Bind mount 会在创建容器时应用。宿主路径必须在 Sandbox 启动前存在。

安全强化
安全强化的直接链接

使用 Docker 专属资源和安全强化选项来限制 Sandbox 容器。以下示例对内存和进程数量设置上限,同时通过相同的 cpuPeriodcpuQuota 值将 CPU 限制为一个核心。它还会移除 Linux capability,并将根文件系统设为只读,同时把 /tmp 挂载为可写的临时空间:

const sandbox = new DockerSandbox({
image: 'node:22-slim',
memory: 512 * 1024 * 1024,
memorySwap: 512 * 1024 * 1024,
cpuPeriod: 100_000,
cpuQuota: 100_000,
pidsLimit: 256,
readonlyRootfs: true,
capDrop: ['ALL'],
capAdd: ['NET_BIND_SERVICE'],
securityOpt: ['no-new-privileges:true'],
ulimits: [{ name: 'nofile', soft: 1024, hard: 2048 }],
tmpfs: {
'/tmp': 'rw,noexec,nosuid,size=64m',
},
})

这些选项会直接映射到 Docker HostConfig 字段,并且只有在传入时才会设置。

启用安全强化前,请考虑以下权衡:

  • readonlyRootfs:容器内的软件包安装,以及写入挂载路径之外位置的 Tool 可能会失败。请为 /tmp 等可写临时路径添加 tmpfs 条目;如有需要,也可为 ~/.npm 等软件包管理器缓存挂载 tmpfs 或 volume。
  • capDrop:移除所有 capability 会禁用需要 Linux capability 的命令,包括 ping 和 mount 操作。基于 FUSE 的 Tool 也会被禁用。请仅重新添加工作负载需要的 capability。
  • memory:Docker 将 0 视为无限制。只有在不需要内存上限时,才应省略 memory 或传入 0
  • memorySwap:Docker 的内存和 swap 行为取决于宿主和 Docker daemon 配置。如果设置 memory 而未设置 memorySwap,Docker 默认允许使用最多为内存限制两倍的 swap。如果要禁用容器 swap,请将 memorySwap 设为与 memory 相同的值;Docker 也接受 -1,表示 swap 不受限制。
  • pidsLimit:过低的值可能会破坏 docker exec 工作负载,因为每个命令都会在长期运行的容器内启动额外进程。
  • privileged:Privileged 容器会绕过 capability 和安全选项控制。除非工作负载需要,否则不要将 privileged: true 与 capability 或安全选项结合使用。
  • 重新连接:当 Sandbox ID 匹配时,DockerSandbox 会复用现有容器;如果检查到的 HostConfig 安全强化值不同,则发出警告。要应用更改后的安全强化选项,请销毁并重新创建 Sandbox。Docker 可能会规范化检查到的值;如果原始容器采用 Docker 默认 swap 行为,重新连接时更改 memorySwap 可能会触发警告。
  • Docker Desktop:在 macOS 和 Windows 上,资源限制应用于 Docker Desktop 虚拟机内部,因此 VM 分配的资源可能会限制容器实际可用的资源。

重新连接
重新连接的直接链接

DockerSandbox 可以通过匹配 label 重新连接到现有容器。调用 start() 时,它会检查是否存在 mastra.sandbox.id label 与 Sandbox ID 匹配的容器。如果找到:

  • 正在运行的容器会被直接复用。
  • 已停止的容器会重新启动。
// First run — creates a new container
const sandbox = new DockerSandbox({ id: 'persistent-sandbox' })
await sandbox._start()

// Later — reconnects to the existing container
const sandbox2 = new DockerSandbox({ id: 'persistent-sandbox' })
await sandbox2._start()

Docker 连接选项
Docker 连接选项的直接链接

通过 dockerOptions 连接到远程 Docker host 或使用自定义 socket 路径:

// Remote Docker host
const sandbox = new DockerSandbox({
dockerOptions: {
host: '192.168.1.100',
port: 2376,
ca: fs.readFileSync('ca.pem'),
cert: fs.readFileSync('cert.pem'),
key: fs.readFileSync('key.pem'),
},
})

// Custom socket path
const sandbox = new DockerSandbox({
dockerOptions: {
socketPath: '/var/run/docker.sock',
},
})