Dify 自定义 Sandbox 陷入死循环:修复"Operation not permitted"后出现"ModuleNotFoundError",两者无法共存

环境信息:

  • 系统: Windows 11 + WSL2 (Ubuntu 22.04)

  • 部署: Docker Compose 源码部署

  • 需求: 在 Sandbox 中运行数学建模代码(需 networkxpulp + 系统级求解器 glpk/cbc)。

问题描述: 我自定义了 sandbox 镜像并修改了 docker-compose.yml,现在陷入了一个诡异的“报错循环”,无法同时满足依赖存在权限足够两个条件:

核心矛盾(The Loop):

  1. 状态 A:报错 “ModuleNotFoundError”

    • 当我尝试重新构建镜像确保库存在时,Web 端提示找不到库。

    • 但此时进入容器 docker exec 检查,库是存在的。

  2. 状态 B:报错 “error: operation not permitted”

    • 为了解决 PuLP 求解器的权限问题,我在 docker-compose.yml 中配置了 security_opt: seccomp:unconfinedprivileged: true

    • 一旦权限似乎生效(或者我重启服务刷新配置后),报错就变成了 “No module named ‘networkx’”

    • 如果我设法让库能被找到,代码运行到 prob.solve() 时又会变回 “operation not permitted”

我的配置:

1. Dockerfile.sandbox (如下)

FROM langgenius/dify-sandbox:latest
USER root
RUN apt-get update && apt-get install -y coinor-cbc glpk-utils

RUN pip install --no-cache-dir --default-timeout=1000 -i Simple Index
numpy
pandas
scipy

RUN pip install --no-cache-dir --default-timeout=1000 -i Simple Index
networkx
pulp
scikit-opt
statsmodels
scikit-learn
scienceplots
openpyxl

2. docker-compose.yml (确保放开了权限)

sandbox:
image: my-math-sandbox:local
container_name: docker-sandbox-1
restart: always

privileged: true
security_opt:
- seccomp:unconfined
- apparmor:unconfined
cap_add:
- SYS_PTRACE

已验证的事实:

  1. 镜像构建无误: 使用 docker run --rm -it my-math-sandbox:local python3 -c "import pulp; print('ok')" 测试,库和求解器都工作正常。

  2. 容器运行中: docker ps 显示 sandbox 确实加载了 my-math-sandbox:local 镜像。

  3. Exec 测试成功: 在容器运行时,使用 docker exec -it docker-sandbox-1 python3 手动运行脚本,完全正常,无权限错误,无缺库错误.

求助核心: 为什么 Dify Web 端 (Go Runner) 执行代码的环境,与我 docker exec 进入的环境表现如此不同?

  • 是否 Dify 的 Runner 在启动 Python 进程时,会忽略 Docker Compose 的 security_opt

  • 或者当开启特权模式后,Runner 会挂载不同的文件系统导致我的 pip install 失效?

恳请大佬指点排查方向!被这个循环折磨两天了

@Mahu
你好,

Dify Sandbox 提供了一个基于 Seccomp 的沙箱环境。
这是在 Dify Sandbox 服务内部独立实现的,与容器级别的 security_opt.seccomp 设置无关。
因此,代码块中可用的 Python 模块以及可以执行的系统调用会受到限制。

我还没有完全测试所有情况,但希望以下信息能给你一些提示。

自定义 Dockerfile

使用 apt 添加软件包应该可以像你尝试的那样正常工作。然而,pip 在这里不会有任何效果,因此你的 Dockerfile 中以下三行就足够了:

FROM langgenius/dify-sandbox:latest
USER root
RUN apt-get update && apt-get install -y coinor-cbc glpk-utils

或者,你可以保留你的 pip 命令不变,然后通过在 config.yaml 中设置 python_lib_path(我稍后会解释),添加一个系统级的模块路径,但我自己尚未尝试过这种方法。

添加 Python 模块

在 Dify Sandbox 中,如果你在容器内放置一个 /dependencies/python-requirements.txt 文件,它会自动安装。
格式与标准的 requirements.txt 文件相同。
你尝试用 pip 安装的所有模块都应该列在这个文件中。我建议将此文件从主机绑定挂载到容器中。

默认为空:dify-sandbox/dependencies/python-requirements.txt at main · langgenius/dify-sandbox · GitHub

控制系统调用

“操作不允许”错误通常发生在所需的系统调用未被允许时。
你可以在容器内的 /conf/config.yaml 文件中控制 Dify Sandbox 的设置——仓库中有一个默认文件:dify/docker/volumes/sandbox/conf/config.yaml at main · langgenius/dify · GitHub

在这里,你会找到 allowed_syscalls 键;通过传递一个整数数组,你可以允许相应的系统调用。

...
allowed_syscalls:
  - 1
  - 2
  - 3
  # 添加你需要的所有系统调用
...

我建议像 python-requirements.txt 一样绑定挂载此文件。

或者,你也可以通过向 ALLOWED_SYSCALLS 环境变量传递一个以逗号分隔的数字列表(如 1,2,3)来实现相同的效果。

要准确找出你的 Python 代码需要哪些系统调用并不容易,但 FAQ 中有一些提示:dify-sandbox/FAQ.md at main · langgenius/dify-sandbox · GitHub

如果你能容忍任何潜在的安全风险,一个想法是简单地允许所有系统调用。

替代方案

如果设置或故障排除过程对你来说太复杂或困难,你可以考虑开发一个专用插件。

使用插件,你将不会受到 Seccomp 限制的影响,并且可以更自由地引入任何你需要的模块。

参见:Dify Plugin - Dify Docs

希望这对你有帮助!

我忘记提一件事了。

默认情况下,Dify Sandbox 会每隔 30 分钟根据 /dependencies/python-requirements.txt 重新安装 Python 模块。

根据您的环境,这可能会导致内存使用量持续增加,或周期性地造成高 CPU 负载。
如果这是一大顾虑,请考虑通过调整相关环境变量 PYTHON_DEPS_UPDATE_INTERVAL 来更改模块安装的间隔时间。

详见:There is a memory leak issue with the sandbox version 0.2.11 when the service is deployed in a containerized environment. · Issue #168 · langgenius/dify-sandbox · GitHub

由于代码块可能会有些限制,我个人倾向于自由地创建插件。
我鼓励您也考虑将自己的想法转化为插件——如果您有编程经验,这应该不会太难。

非常感谢您的帮助。我会按照您的方法尝试解决问题,您的想法对我启发很大。:smiley:

1 个赞