通过 systemd 套接字激活传递 8192 个套接字 - E2BIG 失败

通过 systemd 套接字激活传递 8192 个套接字 - E2BIG 失败

我正在尝试让 systemd 启动一个守护进程并向其传递 8,192 个监听套接字。我有一个.serviceand.socket文件,它能够在更“正常”数量的监听套接字下可靠地工作,如下所示:

# a-daemon.socket
[Unit]
Description=A Daemon (sockets)
After=network.target

[Socket]
Accept=no
ListenStream=8192

# a-daemon.service
[Unit]
Description=A Daemon
After=network.target
Requires=a-daemon.socket

[Install]
WantedBy=multi-user.target

[Service]
Type=notify
ExecStart=/usr/local/sbin/a-daemon

但如果我换成a-daemon.socket有 8,192 行的版本ListenStream,即从 8192 到 16383 的每个 TCP 端口一行,那么守护进程将不再启动。插座设备可以正常启动,但服务单元失败;我收到的唯一错误消息是

systemd[17563]: a-daemon.service: Failed to execute command: Argument list too long
systemd[17563]: a-daemon.service: Failed at step EXEC spawning /usr/local/sbin/a-daemon: Argument list too long

据我了解,这实际上不是参数列表,因为 systemd 不会将套接字 fd 编号放在守护进程的命令行或类似的东西上。我猜这是同时打开文件数限制的问题,所以我设置了DefaultLimitNOFILE=32768/etc/systemd/system.conf中的等效设置/etc/security/limits.conf并重新启动。没有变化。然后我把ExecStartPre=/usr/sbin/prlimit -n了 .service 文件并尝试重新启动它,这证实了增加的限制生效:

prlimit[18134]: RESOURCE   DESCRIPTION                             SOFT      HARD UNITS
prlimit[18134]: NOFILE     max number of open files               32768     32768 files

但服务仍然以同样的方式失败。现在我束手无策了。你能建议我可以尝试做些什么来让它正常工作吗?

(我知道监听 8,192 个连续的 TCP 端口是一件很奇怪的事情。请相信我,我有一个不能分享的充分理由。)

答案1

在进一步查看 systemd 手册页后,我意识到systemd 放置在该argv区域中的东西,其大小与监听套接字的数量成比例:

sd_listen_fds_with_names()类似于sd_listen_fds(),但如果可用并且 names 参数非空,还可以选择返回传递的文件描述符的标识名称字符串数组。该信息是从$LISTEN_FDNAMES[environment] 变量中读取的,该变量可能包含以冒号分隔的名称列表。对于套接字激活的服务,这些名称可以通过FileDescriptorName= 套接字单元文件中的设置来配置,systemd.socket(5)详情请参阅。

(粗体 - 我强调)这意味着,如果ListenStream套接字单元文件中有 8192 个条目,systemd 将尝试将LISTEN_FDNAMES=[name]:[name]:...(重复 8192 次,其中nameFileDescriptorName设置)放入服务的环境中。FileDescriptorName默认为套接字单元文件的完整基名。这很容易溢出 Linux 对单个环境变量名称+值(通常为 128k)长度的相当低的固定限制MAX_ARG_STRLEN,从而导致execve(2)失败E2BIG

对于不关心名称的守护进程,解决方法是

UnsetEnvironment=LISTEN_FDNAMES

在里面服务文件([Service]部分)。这样可以消除问题环境变量并使内核正常运行。

我不知道你会怎么做,如果你真的需要fd 为某物命名并且您达到了这个限制。

相关内容