
Windmill 如何用 # sandbox 在沙箱容器镜像中运行 bash 脚本并取回结果【免费下载链接】windmillOpen-source developer platform to power your entire infra and turn scripts into webhooks, workflows and UIs. Fastest workflow engine (13x vs Airflow). Open-source alternative to Retool and Temporal.项目地址: https://gitcode.com/GitHub_Trending/wi/windmill在 Windmill 中,一个 bash 脚本要调用某个只装了特定工具或库的容器环境(例如 Python、某个 CLI 工具),同时又不能让脚本依赖 Docker daemon、也不能信任其来源时,可以用# sandbox image注释:脚本主体在该镜像的文件系统里、在 nsjail 沙箱内运行,运行结束后按 Windmill bash 脚本的既有约定取回结果。这条路径与旧的# docker(dind/Docker daemon)是两套独立运行时,本文只覆盖# sandbox image。两种容器运行时如何区分Windmill bash 脚本里有三种容器相关的注释,含义互不重叠(docs/docker-v2-runtime.md):旧版# docker沙箱# sandbox image选择方式裸# docker# sandbox image运行时dind / Docker daemon(bollard,dindfeature)无 daemon:解压 rootfs nsjail 运行边界daemon 在 jail 之外沙箱就是该 job 自己的 nsjailnsjail不提供(面向受信任租户)必需——沙箱本体兼容性完整docker run/-d/API仅 run-a-command 子集裸的# sandbox(不带镜像)仍然表示用 nsjail 跑这个 bash 脚本,不会进入容器;只有# sandbox image才解压镜像并在其文件系统里执行命令。准备条件按 docs/docker-v2-runtime.md 的 Requirements 一节,worker 上需要:crane(google/go-containerregistry 的单一静态二进制)和tar,用于拉取并解压镜像——不需要 daemon、root 或特权容器;nsjail,必需。缺失时# sandbox image的 job 会明确报错,提示改用# docker daemon 的方案;镜像内必须有 shell:脚本体通过镜像的/bin/sh -c执行。写法:注释行声明镜像,其余脚本进容器镜像引用写在# sandbox注释行上,脚本其余部分运行在该镜像内部:# sandbox python:3.12-slim name$1 # windmill args bind positionally, like any bash script python3 -c import sys; print(hello, sys.argv[1]) $name该写法的行为规则(均来自 docs/docker-v2-runtime.md):声明为x$1这样的 Windmill 参数会追加到命令后面,即参数按位置绑定,和普通 bash 脚本一致;脚本体为空时,运行镜像自带的ENTRYPOINTCMD;镜像的Env、WorkingDir会生效;Windmill 保留变量(WM_TOKEN、BASE_INTERNAL_URL等)会注入沙箱,因此脚本里调wmill或 Windmill API 是可行的。取回结果:按顺序应用三条规则结果约定与普通 bash 脚本相同,按以下顺序取第一个命中项:./result.json(相对镜像的WorkingDir)非空时,作为 JSON 结果返回;JSON 格式错误会让 job 直接失败。它是宿主机 bind-mount 进容器的文件,因此对WorkingDir: (即/)也有效。由于它是 bind-mount 挂载点,必须原地写入( ./result.json);先写临时文件再rename会失败。否则取stdout 的最后一个非空行,作为字符串返回。stderr 被刻意排除:两条管道由公平的select合并,诊断信息若混进结果行会干扰 stdout 结果。否则返回一条完成消息。关于result.json的 bind 有两个容易踩到的边界:当无法证明目标路径留在容器内时,bind 会被跳过,并且 job 日志里会有警告:包含..的WorkingDir、解压 rootfs 内跨符号链接的路径,或位于/tmp、/proc、/dev、/sys之下的路径(这些挂载点会遮住该路径)。此时结果回落到第 2 条 stdout 规则。bind 在 rootfs 挂载之后立即应用,因此一个盖在WorkingDir上的# volume会隐藏result.json,结果同样回落到 stdout 规则。运行时:镜像如何被拉取和运行整个流程(docs/docker-v2-runtime.md How it works 一节,实现见 backend/windmill-worker/src/docker_v2.rs):拉取/解压:用crane export image把镜像的扁平 rootfs 流式导出为 tar,crane config读取 OCI 配置;tar 与配置按 digest(crane digest)做内容寻址缓存,未变化的 digest 直接复用;tar -x展开到 per-job 的{job_dir}/rootfs。运行:nsjail 将 rootfs 的每个顶层条目就地 bind,挂载标准伪文件系统(jail 自己 pid 命名空间下的/proc、tmpfs/tmp、/dev节点),把容器内 uid/gid 0 映射到 worker 用户,然后执行命令。容器本身就是 jail。由此,沙箱继承 job 的隔离:文件层面只能看到 rootfs 和该 job 的挂载(没有宿主机/、没有别的 job 目录、没有依赖缓存);/proc是 jail 自己的 pid 命名空间;单 uid jail,即使逃逸也落在无特权的 worker 用户;网络即该 job 本身的网络。镜像缓存与新鲜度配置镜像缓存按 digest 存放于{ROOT_CACHE_DIR}/sandbox_rootfs,per-job 解压出的 rootfs 随 job 删除。相关配置(docs/docker-v2-runtime.md):SANDBOX_IMAGE_PULL_POLICY,默认newer:newer/always每个 job 都重新解析 digest(一次便宜的crane digestmanifest 获取),digest 变化的移动 tag 会自动重新拉取;missing直接复用已缓存 digest,不访问 registry;never只用缓存,缺失即报错。固定 digest(imgsha256:…)是不可变的,永远不会过期。SANDBOX_IMAGE_MAX_SIZE_MB,默认 0(关闭):按压缩下载大小(crane manifest)超限的镜像在任何层下载前就被拒绝。SANDBOX_IMAGE_CACHE_MAX_MB,默认 0(关闭):运行结束后按创建时间淘汰最旧的 rootfs tar,尽力把缓存压回上限。常见问题排查worker 没有 nsjail:带# sandbox注释的脚本会报Script has #sandbox annotation but nsjail is not available on this worker. Please ensure nsjail is installed or remove the #sandbox annotation.(见 backend/windmill-worker/src/bash_executor.rs 中的处理)。解决办法是装 nsjail、去掉注释,或改用裸# docker daemon。结果不是 JSON 而是报错:result.json内容不合法时 job 会失败,按上面第 1 条规则处理——检查写入内容与是否为原地写入。result.json没生效、日志出现警告:通常是WorkingDir触发 bind 跳过条件(含..、跨符号链接、位于/tmp等受管挂载之下),或被# volume覆盖。此时结果回落到 stdout 最后一行规则。想要docker run -d式的长驻/交互能力:该运行时不提供docker run -d 后续exec/attach/logs -f,也没有docker build、compose、swarm、healthchecks,更没有任意-v宿主 bind mount、--privileged、--cap-add、--device或宿主命名空间共享——这是无 daemon、run-to-completion 设计的既定限制。镜像内降到非 root uid:单 uid jail 尚不支持子 uid 范围映射,这类镜像目前无法在沙箱内正确 chown(文档将其列为后续工作:subuid range /newuidmap)。适用范围小结# sandbox image适合一次性、跑完即走、结果经result.json或 stdout 最后一行取回的脚本任务,且希望以沙箱(而非受信任租户)模型运行容器内容。需要长驻容器、daemon API 或宿主设备/挂载时,应改用裸# docker(dind),那是一条独立路径,本文不展开。【免费下载链接】windmillOpen-source developer platform to power your entire infra and turn scripts into webhooks, workflows and UIs. Fastest workflow engine (13x vs Airflow). Open-source alternative to Retool and Temporal.项目地址: https://gitcode.com/GitHub_Trending/wi/windmill创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考