1.为什么 Python 代码会报错 “xxx.so: cannot open shared object file: No such file or directory”?
这是因为 dify-sandbox
的实现机制会在 /var/sandbox/sandbox-python/
目录下生成一个临时文件来保存和执行 Python 代码。在运行 Python 代码前,它会调用 syscall.Chroot
将当前进程的根目录限制为 /var/sandbox/sandbox-python/
。Python 进程只能看到该目录结构,因而能导入的 Python 模块/库(特别是基于 C 的共享对象模块 .so
)也必须在此目录结构中。
根目录说明:从 Python 进程的视角来看,/var/sandbox/sandbox-python/
是根目录,目录内容由在 config.yaml
中的 python_lib_path
配置决定,通常包括:
-
etc/
目录 -
python.so
:由dify-sandbox
编译生成的共享对象 -
usr/lib
目录 -
usr/local/
等
如果未在 config.yaml
中配置 python_lib_path
,dify-sandbox
会使用默认设置。可在 config_default_amd64.go
查看,ARM 架构请查看 config_default_arm64.go
:
var DEFAULT_PYTHON_LIB_REQUIREMENTS = []string{
"/usr/local/lib/python3.10", // 一般是 Python 安装目录;如果使用的是 conda 虚拟环境,需改为 conda 路径,例如 /root/anaconda3/envs/{env_name}
"/usr/lib/python3.10",
"/usr/lib/python3",
"/usr/lib/x86_64-linux-gnu/libssl.so.3", // Python 模块的共享对象依赖,将被复制到 /var/sandbox/sandbox-python/usr/lib/x86_64-linux-gnu/,从中加载
"/usr/lib/x86_64-linux-gnu/libcrypto.so.3", // 同上
"/etc/ssl/certs/ca-certificates.crt",
"/etc/nsswitch.conf",
"/etc/hosts",
"/etc/resolv.conf",
"/run/systemd/resolve/stub-resolv.conf",
"/run/resolvconf/resolv.conf",
}
因此,当遇到.so 文件无法找到
的报错时,应在config.yaml
中手动添加Python
代码所依赖的.so
路径。例如:
python_path:/usr/local/bin/python3
python_lib_path:
-/usr/local/lib/python3.10
-/usr/lib/python3.10
-/usr/lib/python3
-/usr/lib/x86_64-linux-gnu/libssl.so.3
-/usr/lib/x86_64-linux-gnu/libcrypto.so.3
-/etc/ssl/certs/ca-certificates.crt
-/etc/nsswitch.conf
-/etc/hosts
-/etc/resolv.conf
-/run/systemd/resolve/stub-resolv.conf
-/run/resolvconf/resolv.conf
-# *** 添加需要的其它路径 ***
注意: Go程序在启动时会加载这些路径,如果python_lib_path
过多,会显著拖慢启动速度。对于Serverless 场景,建议改为在 Docker 容器中提前构建该环境。
2.Python 代码报错 “operation not permitted”?
dify-sandbox
使用 Linux 的 seccomp
技术限制系统调用(syscall)。建议阅读源码 add_seccomp.go
。
出现该报错,通常是因为 Python 代码调用了被禁止的系统调用。默认允许的系统调用见:syscalls_amd64.go
。这些配置目前 不能通过配置文件修改,只能修改源码。
快速定位代码所需系统调用的方法如下:
(1)打开 /cmd/test/syscall_dig/test.py
,将测试代码添加到 main()
函数中,例如:
import numpy # 添加想测试的导入
defmain():
...
(2)运行 syscall 侦测工具
go run cmd/test/syscall_dig/main.go
输出可能,如下所示:
~/dify-sandbox$ go run cmd/test/syscall_dig/main.go
failed with signal: bad systemcall
...
failedwith signal: bad systemcall
Following syscalls arerequired: 0,1,3,5,8,9,10,11,12,13,14,15,16,17,24,28,35,39,60,63,105,106,131,186,202,204,217,231,233,234,237,257,262,273,281,291,318,334,435
若没有此输出格式,可能是权限问题,请尝试加 sudo
重新运行。
(3)这些系统调用(syscall)是沙箱中已经添加的:
0, 1, 3, 8, 9, 10, 11, 12, 13, 14, 15, 16, 16, 24, 25, 35, 39, 60, 96, 102, 105, 106, 110, 131, 186, 201, 202, 217, 228, 230, 231, 233, 234, 257, 262, 270, 273, 291, 318, 334
需要将这些已有系统调用与代码所需系统调用进行比较,找出额外缺失的 syscall 编号。可以使用一个简单的脚本,或者让大语言模型(LLM)帮助完成这个操作。
例如,在当前案例中,缺失的系统调用为:
5, 17, 28, 63, 204, 237, 281, 435
(4)在 /internal/static/python_syscall/syscalls_amd64.go
中添加正确的系统调用别名,可在 golang
库中找到它,比如 /usr/lib/go-1.18/src/syscall/zsysnum_linux_amd64.go
var ALLOW_SYSCALLS = []int{
// 文件IO
syscall.SYS_NEWFSTATAT, syscall.SYS_IOCTL, syscall.SYS_LSEEK, syscall.SYS_GETDENTS64,
syscall.SYS_WRITE, syscall.SYS_CLOSE, syscall.SYS_OPENAT, syscall.SYS_READ,
...
// numpy运行所需
syscall.SYS_FSTAT, syscall.SYS_PREAD64, syscall.SYS_MADVISE, syscall.SYS_UNAME,
syscall.SYS_SCHED_GETAFFINITY, syscall.SYS_MBIND, syscall.SYS_EPOLL_PWAIT, 435,
}
如果找不到某些 syscall 的别名(如 SYS_XXXX
),也可以直接使用数字编号(如 435
)添加进去。
(5)重新构建并运行整个项目。
参考文献
[0] Dify中的sandbox服务- FAQ翻译:https://z0yrmerhgi8.feishu.cn/wiki/VgtJwwGVeiwXvzkYPVPco1oTnjb
[1] dify-sandbox/FAQ.md:https://github.com/langgenius/dify-sandbox/blob/main/FAQ.md
知识星球:Dify源码剖析及答疑,Dify扩展系统源码,AI书籍课程|AI报告论文,公众号付费资料。加微信buxingtianxia21
进NLP工程化资料群,以及Dify交流群。
(文:NLP工程化)