本指南说明如何在 Sandbox 中运行 Docker,从而在安全沙箱内构建并运行 container 镜像。
在以下场景使用 Docker-in-Docker:
- 开发容器化应用 - 运行
docker build,根据 Dockerfile 创建镜像 - 在 CI/CD 中运行 Docker - 响应代码变更,并使用 Cloudflare Containers 构建和推送镜像
- 运行任意 container 镜像 - 从最终用户提供的镜像启动 container
创建一个自定义 Dockerfile,将 sandbox 二进制文件与 Docker 组合使用。Docker 守护进程以 root 身份运行:
FROM docker:dind
# Use the musl build so it runs on Alpine-based docker:dind
COPY --from=docker.io/cloudflare/sandbox:0.12.14-musl /container-server/sandbox /sandbox
COPY --from=docker.io/cloudflare/sandbox:0.12.14-musl /usr/lib/libstdc++.so.6 /usr/lib/libstdc++.so.6
COPY --from=docker.io/cloudflare/sandbox:0.12.14-musl /usr/lib/libgcc_s.so.1 /usr/lib/libgcc_s.so.1
COPY --from=docker.io/cloudflare/sandbox:0.12.14-musl /bin/bash /bin/bash
COPY --from=docker.io/cloudflare/sandbox:0.12.14-musl /usr/lib/libreadline.so.8 /usr/lib/libreadline.so.8
COPY --from=docker.io/cloudflare/sandbox:0.12.14-musl /usr/lib/libreadline.so.8.2 /usr/lib/libreadline.so.8.2
# Start dockerd without iptables or IP forwarding, wait up to 60 seconds
# for it to accept connections, then keep running
RUN printf '#!/bin/sh\n\
set -eu\n\
dockerd-entrypoint.sh dockerd --iptables=false --ip6tables=false --ip-forward=false --exec-opt native.cgroupdriver=cgroupfs &\n\
timeout 60 sh -c "until docker version >/dev/null 2>&1; do sleep 0.2; done"\n\
echo "Docker is ready"\n\
wait\n' > /usr/local/bin/start-docker.sh && chmod +x /usr/local/bin/start-docker.sh
# Mount /tmp before the sandbox starts. Otherwise the docker:dind
# entrypoint mounts a new /tmp over the sandbox's session files.
ENTRYPOINT ["/bin/sh", "-c", "mountpoint -q /tmp || mount -t tmpfs none /tmp; exec /sandbox \"$@\"", "--"]
CMD ["/usr/local/bin/start-docker.sh"]将 cloudflare/sandbox 镜像标签与 Worker 中 @cloudflare/sandbox 的版本保持一致。
sandbox 启动时会运行 CMD 脚本:
--iptables=false、--ip6tables=false和--ip-forward=false可阻止 Docker 更改 Containers 不允许更改的网络设置。--exec-opt native.cgroupdriver=cgroupfs让 Docker 直接管理 cgroups,而不是通过容器中未运行的 systemd 管理。- 如果 dockerd 在 60 秒内未接受连接,脚本会退出,sandbox 也会停止,因此日志中会显示故障。将
SANDBOX_LOG_LEVEL设为debug可记录 dockerd 的输出。请参阅 Sandbox 选项。
该脚本不会显示在 listProcesses() 中,因为 sandbox 在其进程列表之外启动该脚本。
部署后,你可以通过 sandbox 运行 Docker 命令。sandbox 启动几秒后 Docker 才会就绪,因此请先等待 docker version 成功:
import { getSandbox } from "@cloudflare/sandbox";
const sandbox = getSandbox(env.Sandbox, "docker-sandbox");
// Wait for the Docker daemon
const ready = await sandbox.exec(
"timeout 60 sh -c 'until docker version >/dev/null 2>&1; do sleep 0.2; done'",
);
if (!ready.success) {
throw new Error("Docker did not start");
}
// Build an image
await sandbox.writeFile(
"/workspace/Dockerfile",
`
FROM alpine:latest
RUN apk add --no-cache curl
CMD ["echo", "Hello from Docker!"]
`,
);
const build = await sandbox.exec(
"docker build --network=host -t my-image /workspace",
);
if (!build.success) {
console.error("Build failed:", build.stderr);
}
// Run a container
const run = await sandbox.exec("docker run --network=host --rm my-image");
console.log(run.stdout); // "Hello from Docker!"import { getSandbox } from "@cloudflare/sandbox";
const sandbox = getSandbox(env.Sandbox, "docker-sandbox");
// Wait for the Docker daemon
const ready = await sandbox.exec(
"timeout 60 sh -c 'until docker version >/dev/null 2>&1; do sleep 0.2; done'",
);
if (!ready.success) {
throw new Error("Docker did not start");
}
// Build an image
await sandbox.writeFile(
"/workspace/Dockerfile",
`
FROM alpine:latest
RUN apk add --no-cache curl
CMD ["echo", "Hello from Docker!"]
`,
);
const build = await sandbox.exec(
"docker build --network=host -t my-image /workspace",
);
if (!build.success) {
console.error("Build failed:", build.stderr);
}
// Run a container
const run = await sandbox.exec("docker run --network=host --rm my-image");
console.log(run.stdout); // "Hello from Docker!"Cloudflare Containers 中的 Docker-in-Docker 有以下限制:
- 无 iptables - 依赖 iptables 的网络隔离功能不可用
- 临时存储 - 沙箱休眠后,已构建的镜像和 container 会丢失。你必须自行持久化它们。
- 不支持本地开发 - Docker 无法在
wrangler dev下启动
- Dockerfile 参考 - 自定义 sandbox 镜像
- 执行命令 - 在 sandbox 中运行命令
- 后台进程 - 管理长期运行的进程