Skip to content
Go back

Docker 容器内 nslcd 启动卡死 (Hang) 问题排查

Edit page

Docker 容器内 nslcd 启动卡死 (Hang) 问题排查

在 Docker 容器(特别是基于较新内核或 ARM64 架构的基础镜像)中部署 LDAP 客户端时,可能会遇到 nslcd 进程启动后长时间无响应的问题。本文记录了该问题的排查过程、原因分析及解决方案。

故障现象

执行启动命令后,程序进入假死状态:

root@container:/# nslcd -n -d

现象特征:

排查过程

为了分析进程在“假死”期间到底在做什么,我们使用 strace 跟踪系统调用:

strace -f -tt -T nslcd -n -d

输出片段分析:

08:36:49.585825 close(1073694641) = -1 EBADF (Bad file descriptor) <0.000020>
08:36:49.585906 close(1073694640) = -1 EBADF (Bad file descriptor) <0.000040>
...
08:36:49.586643 close(1073694632) = -1 EBADF (Bad file descriptor) <0.000033>

观察结论:

  1. 密集系统调用:进程正在密集执行 close() 操作。
  2. 大量无效调用:返回值均为 EBADF (Bad file descriptor),说明试图关闭的文件描述符并不存在。
  3. 异常数值:操作的文件描述符编号高达 10 亿级别(如 1073694641)。

检查容器环境的资源限制:

ulimit -n
# 输出: 1073741816 (或 unlimited)

原因分析

此问题是 遗留代码逻辑现代容器环境默认配置 冲突的结果。

1. 代码逻辑 (Legacy Code)

nslcd(以及许多老旧的守护进程)在启动时的初始化阶段,为了确保环境纯净,会尝试关闭从父进程继承的所有文件描述符。其实现逻辑通常是简单的遍历:从 3 开始,一直循环到 sysconf(_SC_OPEN_MAX)

2. 环境差异

3. 结果

进程试图执行 10 亿次 close() 系统调用。尽管单次 close() 调用在返回 EBADF 时非常快,但 10 亿次的累积耗时导致进程在用户态看起来像是卡死了数分钟。

解决方案

核心思路是降低容器内的最大打开文件数限制(ulimit -n)。

方案 A:运行时临时修复(验证用)

在容器内手动降低限制后启动服务:

ulimit -n 1024
nslcd -n -d

方案 B:Docker Compose 配置(生产推荐)

docker-compose.yml 中为服务显式配置 ulimits

services:
  nslcd-service:
    image: your-image
    ulimits:
      nofile:
        soft: 1024
        hard: 1024

方案 C:Docker Run 参数

如果是通过 docker run 启动,添加 --ulimit 参数:

docker run --ulimit nofile=1024:1024 ...

参考资料


Edit page
Share this post on:

Previous Post
排查 VMware 虚拟机中 vmwgfx 与 wlroots 的 DMA-BUF 导入失败问题
Next Post
ArchLinux下NetworkManager导入OpenVPN及Mihomo分流配置