文件描述符耗尽是 docker 容器“连接被拒绝”的隐蔽原因,表现为无法建连、打不开日志或崩溃;需通过 ulimit -n 和 ls /proc/1/fd | wc -l 检查是否接近上限,临时修复用 --ulimit,永久修复需配置 systemd 和 daemon.json,并适配应用自身限制。

文件描述符(file descriptor)耗尽是 Docker 容器出现“连接被拒绝”(Connection refused)的隐蔽但常见原因——它往往不报错“connection refused”,而是让应用无法建立新连接、无法打开日志、甚至直接崩溃,表现为服务不可达或响应超时。根本在于:每个 TCP 连接、socket、打开的文件都占用一个 fd,一旦达到 ulimit -n 限制,新连接就会失败。
确认是否为文件描述符耗尽
进入容器检查当前使用量和上限:
- 执行 docker exec -it sh 进入容器
- 运行 ulimit -n 查看软限制(通常就是生效值)
- 运行 ls /proc/1/fd | wc -l 查看 PID 1 进程实际打开的 fd 数量(Java/Node 等常驻进程适用)
- 若数值接近或等于 ulimit -n(如 1024),且应用日志中出现 EMFILE 或 unable to allocate file descriptor table,即可确认
临时修复:启动时指定 ulimit
适用于调试或单次运行,不持久:
- 用 --ulimit nofile=65535:65535 启动容器:
docker run -d --ulimit nofile=65535:65535 -p 8080:8080 my-java-app - 注意:硬限制(第二个值)不能超过宿主机对 dockerd 进程的限制;若报错“invalid ulimit”,需先调高宿主机的 systemd 服务限制(见下一条)
永久生效:宿主机 + Docker daemon 双层配置
仅改容器参数不够,必须确保底层支撑到位:
- 编辑 /etc/systemd/system/docker.service.d/override.conf(不存在则新建),添加:
[Service]
LimitNOFILE=65536
LimitNPROC=65536 - 重载并重启 Docker:
sudo systemctl daemon-reload && sudo systemctl restart docker - 验证是否生效:systemctl show docker | grep LimitNOFILE 应输出 65536
- 此后所有新容器默认继承该上限,也可在 /etc/docker/daemon.json 中统一设
"default-ulimits": {"nofile": {"Name": "nofile", "Hard": 65536, "Soft": 65536}}
容器内进程自身也要适配
某些应用(如 Java、Nginx)不会自动用满系统上限,需显式配置:
- Java 应用:启动命令加 -XX:+UseContainerSupport -XX:MaxRAMPercentage=75.0(JDK8u191+),避免因内存估算不准间接限制 fd
- Nginx:在配置中明确设 worker_rlimit_nofile 65535;,并在 events 块中设 worker_connections 65535;
- Node.js:启动前加 ulimit -n 65535 && node app.js(写进容器启动脚本)











