Dify 框架连接 PGSQL 数据库与 Sandbox 环境下的 Linux 系统调用权限问题
背景
在使用 Dify 框架进行开发时,遇到了两个主要的技术挑战:
- 代码节点连接到 PGSQL(PostgreSQL)数据库。
- 解决沙盒环境中由于系统调用限制导致的“operation not permitted”错误。
本文档将详细描述如何解决这两个问题,并介绍 psycopg2-binary
Python 依赖的作用及其配置方法。同时,我们将强调配置文件修改后正确重启服务的重要性。
技术解决方案
PGSQL 数据库连接
为了使基于 Python 的 Dify 应用能够与 PostgreSQL 数据库通信,我们在 volumessandboxdependenciespython-requirements.txt
文件中添加了 psycopg2-binary==2.9.10
依赖。该库提供了一个 PostgreSQL 数据库适配器,它包含了预编译的二进制文件,简化了安装过程,避免了需要单独编译 C 扩展的问题。
# volumes/sandbox/dependencies/python-requirements.txt
psycopg2-binary==2.9.10
当 Docker Compose 构建服务时,它会读取此文件并安装列出的所有 Python 包,包括 psycopg2-binary
,从而为应用程序提供与 PGSQL 数据库交互的能力。安装完成后,通过代码节点配置应用中的数据库连接参数(如主机、端口、用户名、密码等),实现了代码节点与 PGSQL 数据库的成功连接。
Sandbox 中的 Linux 系统调用权限问题
在尝试运行代码节点时,遇到了如下错误信息:
error: operation not permitted
此错误表明应用程序尝试执行的操作被操作系统或沙盒环境所禁止。经分析,确定这是由于沙盒环境中对特定 Linux 系统调用的访问权限不足造成的。
为了解决这个问题,采取了以下步骤:
- 调整沙盒策略:在
volumessandboxconfconfig.yaml
文件中扩展了allowed_syscalls
列表,以允许必要的系统调用。这确保了沙盒内的应用程序可以执行所需的底层操作系统功能,而不会遇到权限错误。
下面是一个简化的 config.yaml
配置示例,其中列出了部分关键系统调用编号,表示这些操作是被允许的:
allowed_syscalls:
# 基础文件操作
- 0 # read - 从文件描述符读取数据
- 1 # write - 向文件描述符写入数据
- 2 # open - 打开文件
- 3 # close - 关闭文件描述符
- 4 # stat - 获取文件状态
- 5 # fstat - 获取文件描述符状态
- 6 # lstat - 获取符号链接状态
- 7 # poll - 等待文件描述符上的事件
- 8 # lseek - 重新定位读/写文件偏移量
- 9 # mmap - 将文件或设备映射到内存
- 10 # mprotect - 设置内存区域的保护
- 11 # munmap - 取消内存映射
- 12 # brk - 改变数据段大小
# 系统操作
- 13 # rt_sigaction - 检查或修改信号处理
- 14 # rt_sigprocmask - 检查或修改阻塞信号
- 15 # rt_sigreturn - 从信号处理程序返回
- 16 # ioctl - 控制设备
- 17 # pread64 - 从指定偏移量读取
- 18 # pwrite64 - 向指定偏移量写入
- 19 # readv - 从文件描述符读取数据到多个缓冲区
- 20 # writev - 从多个缓冲区写入数据到文件描述符
- 21 # access - 检查文件访问权限
- 22 # pipe - 创建管道
- 23 # select - 同步 I/O 多路复用
- 24 # sched_yield - 让出处理器
- 25 # mremap - 重新映射虚拟内存地址
# 高级内存管理
- 26 # msync - 同步内存与物理存储
- 27 # mincore - 确定内存页是否驻留在内存中
- 28 # madvise - 给出内存使用建议
- 29 # shmget - 获取共享内存段
- 30 # shmat - 附加共享内存段
- 31 # shmctl - 共享内存控制
- 32 # dup - 复制文件描述符
- 33 # dup2 - 复制文件描述符到指定编号
- 34 # pause - 挂起进程直到收到信号
# 进程管理
- 35 # nanosleep - 高精度睡眠
- 36 # getitimer - 获取定时器值
- 37 # alarm - 设置定时器
- 38 # setitimer - 设置定时器
- 39 # getpid - 获取进程ID
- 40 # sendfile - 在文件描述符之间传输数据
# 网络操作
- 41 # socket - 创建套接字