在华为欧拉操作系统上部署dify, 遇到“OpenBLAS blas_thread_init: pthread_create failed for thread 1 of 16: Operation not permitted OpenBLAS blas_thread_init: RLIMIT_NPROC -1 current, -1 max”报错。
2025-12-29 11:35 千里马365 阅读(22) 评论(0) 收藏 举报问题分析
- 报错来自 OpenBLAS 在初始化线程时调用 pthread_create 失败,错误是 Operation not permitted。日志中的 RLIMIT_NPROC 为 -1,通常表示“无限”,因此更可能是安全策略或内核系统调用被拦截,而不是进程数上限不足。
- 在 openEuler(华为欧拉)内核上,glibc 可能优先使用新 syscall clone3 创建线程;如果容器的 seccomp 配置未允许 clone3,就会得到 EPERM(Operation not permitted)。Ubuntu 上默认 seccomp 通常已放开 clone3,所以不会报错。
通过docker部署
- 你通过 Docker 部署,在无授权或受限 seccomp 的容器里,OpenBLAS多线程会触发大量 pthread_create,最容易踩到这个限制。
快速规避
- 把 BLAS/OMP 设为单线程,避免创建额外线程:
- 环境变量:
- OPENBLAS_NUM_THREADS=1
- OMP_NUM_THREADS=1
- GOTO_NUM_THREADS=1(可选)
- Python 中在 import numpy/scipy 之前设置:
import os os.environ["OPENBLAS_NUM_THREADS"] = "1" os.environ["OMP_NUM_THREADS"] = "1" # 接着再 import numpy/scipy - 如果使用 PyTorch:
import torch torch.set_num_threads(1) - Docker 运行时添加环境变量:
docker run -e OPENBLAS_NUM_THREADS=1 -e OMP_NUM_THREADS=1 your/image
- 环境变量:
容器级修复
- 放宽容器 seccomp(验证用或作为临时解决方案):
- 运行时禁用 seccomp:
docker run --security-opt seccomp=unconfined your/image - 或使用自定义 seccomp profile,允许 clone/clone3:
- 创建 seccomp.json(示例,仅放开必要 syscall)
{ "defaultAction": "SCMP_ACT_ERRNO", "architectures": ["SCMP_ARCH_X86_64", "SCMP_ARCH_AARCH64"], "syscalls": [ { "names": ["clone", "clone3", "fork", "vfork"], "action": "SCMP_ACT_ALLOW" } ] } - 运行容器时加载该配置:
docker run --security-opt seccomp=/path/to/seccomp.json your/image
- 创建 seccomp.json(示例,仅放开必要 syscall)
- 如果以 rootless Docker 或更严格策略运行,先用
--privileged做一次对比测试:
如果问题消失,基本可以确认是 seccomp/能力限制导致。docker run --privileged your/image
- 运行时禁用 seccomp:
资源与系统检查
- 在容器内确认进程/线程限制是否异常:
ulimit -u # 用户进程数限制 cat /proc/sys/kernel/threads-max cat /proc/sys/kernel/pid_max - 检查 Docker/内核对 seccomp 的支持和默认策略:
docker info | grep -i seccomp getconf GNU_LIBC_VERSION uname -a - 如果
ulimit -u返回很小的数值,可在运行时提高:docker run --ulimit nproc=8192:8192 your/image
长期建议
- 升级 Docker/Moby 到包含允许 clone3 的默认 seccomp 版本,或升级 openEuler 的容器引擎到默认放开 clone3 的版本。
- 如果业务允许,考虑使用“串行版” BLAS 库(如 Debian/Ubuntu 的 libopenblas0-serial)或改用 BLIS/MKL,并将线程数控制在 1。
- 在镜像或入口脚本中统一设置线程环境变量,避免因不同运行环境导致不一致。
排查路径
- 先用
--security-opt seccomp=unconfined验证是否是 seccomp 限制。(这一步可能是最有效的) - 若验证通过,再选择:
- 保持单线程(最低风险的快速修复),或
- 配置自定义 seccomp profile,仅放开 clone/clone3。
- 同步将 OPENBLAS/OMP 线程数设置为 1,以减少资源占用和避免类似问题在其他环境复现。
在docker-compose部署
解决方案概览
- 根因是容器在 openEuler 上初始化线程时碰到安全策略(seccomp)限制,导致 OpenBLAS 的 pthread 创建失败(常见于 clone/clone3 被拦截)。
- 两条修复路径:
- 最稳妥:将 BLAS/OMP 线程数控制为 1,避免大量线程创建
- 放宽容器 seccomp(临时或按需):允许 clone/clone3,或直接使用 unconfined
docker-compose 修改(推荐最小改动)
- 在需要使用 NumPy/Scipy/PyTorch 的服务中加入环境变量,限制线程为 1
- 可选提高进程数限制(nproc)
示例(将以下内容合并到你的服务配置中):
# 片段,用于你的 docker-compose.yaml 某个服务(例如 api)
services:
api:
environment:
OPENBLAS_NUM_THREADS: "1"
OMP_NUM_THREADS: "1"
GOTO_NUM_THREADS: "1"
NUMEXPR_MAX_THREADS: "1" # 如使用 numexpr
MKL_NUM_THREADS: "1" # 如使用 MKL
ulimits:
nproc: 8192
nofile:
soft: 65536
hard: 65536
# 若需快速验证 seccomp 是否导致问题,可临时放开:
# security_opt:
# - seccomp:unconfined
# 注意:生产环境建议使用自定义 seccomp profile,而不是 unconfined
更精细的 seccomp 方案(按需启用)
- 创建一个自定义 seccomp profile,仅放开线程相关 syscall(clone/clone3/fork/vfork)
- 在 docker-compose 中引用该 profile
示例 seccomp 文件(请根据实际路径保存):
{
"defaultAction": "SCMP_ACT_ERRNO",
"architectures": ["SCMP_ARCH_X86_64", "SCMP_ARCH_AARCH64"],
"syscalls": [
{ "names": ["clone", "clone3", "fork", "vfork"], "action": "SCMP_ACT_ALLOW" },
{ "names": ["rt_sigreturn", "rt_sigprocmask", "rt_sigaction"], "action": "SCMP_ACT_ALLOW" },
{ "names": ["futex"], "action": "SCMP_ACT_ALLOW" },
{ "names": ["mmap", "munmap", "mremap", "brk"], "action": "SCMP_ACT_ALLOW" },
{ "names": ["close", "openat", "read", "write", "lseek"], "action": "SCMP_ACT_ALLOW" },
{ "names": ["sched_yield", "sched_getaffinity", "sched_setaffinity"], "action": "SCMP_ACT_ALLOW" }
]
}
在 docker-compose 中引用:
services:
api:
security_opt:
- seccomp:/opt/dify/seccomp-openblas.json
- 说明:上述 profile 为演示用途。实际生产建议基于 Docker 默认 seccomp 配置做增量放开(clone/clone3),而不是用“全拒+少量放开”的策略,以免遗漏其他必要的 syscall。
验证建议
- 优先尝试仅环境变量线程数=1 的方案;多数场景足以避免错误,且风险最低
- 若仍报错,再临时加入
security_opt: - seccomp:unconfined验证是否为 seccomp 所致;验证通过后改用自定义 profile - 同时检查容器内资源限制:
- nproc 是否正常(已在 compose 提高)
- openEuler 的 Docker/内核版本是否较旧(旧版默认 seccomp 可能不含 clone3 允许项)
补充说明
- 如果你的应用入口在 Python 中能提前设置环境变量(import numpy/scipy 之前),也可以在代码层面加固:
import os os.environ["OPENBLAS_NUM_THREADS"] = "1" os.environ["OMP_NUM_THREADS"] = "1" os.environ["GOTO_NUM_THREADS"] = "1" os.environ["NUMEXPR_MAX_THREADS"] = "1"
浙公网安备 33010602011771号