我正在尝试让 systemd 启动一个守护进程并向其传递 8,192 个监听套接字。我有一个.service
and.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 次,其中name
是FileDescriptorName
设置)放入服务的环境中。FileDescriptorName
默认为套接字单元文件的完整基名。这很容易溢出 Linux 对单个环境变量名称+值(通常为 128k)长度的相当低的固定限制MAX_ARG_STRLEN
,从而导致execve(2)
失败E2BIG
。
对于不关心名称的守护进程,解决方法是
UnsetEnvironment=LISTEN_FDNAMES
在里面服务文件([Service]
部分)。这样可以消除问题环境变量并使内核正常运行。
我不知道你会怎么做,如果你真的需要fd 为某物命名并且您达到了这个限制。