Alpine镜像中Python多进程队列触发Errno24文件描述符不足问题
解决python:alpine3.19镜像中multiprocessing同步对象触发文件描述符耗尽问题
问题概述
在python:alpine3.19 Docker镜像中创建multiprocessing.Queue、Value、Lock、Semaphore等多进程同步对象时,频繁触发OSError: [Errno 24] No file descriptors available错误。即使调整Docker的nofile ulimit至较高值(如4096),仍然会比基于glibc的官方python镜像更早出现该错误;而线程同步对象及multiprocessing.Pipe不受影响,该问题在Windows 11(Docker Engine v24.0.7)和CentOS 8(Docker v24.0.7)上均能复现。
复现步骤
Dockerfile
# FROM python FROM python:alpine3.19 WORKDIR test COPY . . CMD [ "python", "test.py"]
test.py
import multiprocessing import time import subprocess if __name__ == '__main__': queues = [] print((subprocess.check_output(['cat', '/proc/1/limits'])).decode()) for i in range(1000): print(f"Appending multiprocessing.Queue() n. {i} ", end="") queues.append(multiprocessing.Queue()) print("ok") time.sleep(0.1) print("all ok")
构建与运行命令
docker build -t test . docker run test
测试对比结果
- 官方
python镜像:nofile限制为128时,创建到第63个队列触发错误;限制调高至4096时可完成1000个队列创建。 python:alpine3.19镜像:nofile限制为128时同样在第63个队列报错;限制调高至4096时仅能创建86个队列就触发错误。
原因分析
核心差异来自Alpine使用的musl libc与官方python镜像使用的glibc在进程间同步原语实现上的区别:
- glibc下,Python的多进程同步对象依赖匿名共享内存+信号量,文件描述符占用较少;
- musl libc对POSIX同步原语的实现会为每个同步对象分配更多的文件描述符,且默认系统信号量参数较低,进一步加剧了文件描述符耗尽的问题。
解决方案
1. 调整系统资源参数
通过Docker启动参数同时调高nofile限制和内核信号量参数,弥补musl的资源占用差异:
docker run \ --ulimit nofile=16384:16384 \ --sysctl kernel.sem=250 32000 32 4096 \ test
参数说明:
--ulimit nofile=16384:16384:将进程的软/硬文件描述符限制调高至16384;--sysctl kernel.sem=250 32000 32 4096:调整内核信号量参数,分别对应:每个进程的最大信号量数、系统范围内的最大信号量数、每个信号量集的最大信号量数、系统范围内的最大信号量集数。
2. 使用multiprocessing.Manager创建同步对象
multiprocessing.Manager通过独立的管理进程统一维护同步对象,当前进程仅通过RPC调用访问,不会直接占用自身的文件描述符:
修改test.py的队列创建逻辑:
import multiprocessing import time import subprocess if __name__ == '__main__': # 初始化Manager manager = multiprocessing.Manager() queues = [] print((subprocess.check_output(['cat', '/proc/1/limits'])).decode()) for i in range(1000): print(f"Appending multiprocessing.Queue() n. {i} ", end="") # 使用Manager创建队列 queues.append(manager.Queue()) print("ok") time.sleep(0.1) print("all ok")
3. 切换至基于glibc的Python镜像
如果不需要Alpine镜像的轻量特性,直接使用官方python镜像(基于Debian/Ubuntu,使用glibc)即可避免该问题,同步对象的文件描述符占用效率更高。
内容的提问来源于stack exchange,提问作者Jan H
相关产品推荐
相关产品推荐

