通過 systemd 套接字啟動傳遞 8192 個套接字 - E2BIG 失敗
我正在嘗試讓 systemd 啟動一個守護程序並將 8,192 個監聽套接字傳遞給它。我有一個
.service
和.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,192ListenStream
行的版本,每個 TCP 埠從 8192 到 16383(含)一個,那麼守護程序將不再啟動。套接字單元可以正常啟動,但服務單元失敗;我得到的唯一錯誤資訊是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
據我了解,這實際上不是參數 list的問題,因為 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 埠是一件很奇怪的事情。請相信我,我有一個很好的理由,我不能分享。)
再看一下 systemd 聯機幫助頁後,我意識到systemd 在該區域中放置了一些東西,其
argv
大小與偵聽套接字的數量成正比:
sd_listen_fds_with_names()
類似於sd_listen_fds()
,但也可以選擇返回一個字元串數組,其中包含傳遞的文件描述符的標識名稱,如果可用且 names 參數為非 NULL 的話。**此資訊是從$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 名稱並且您達到了此限制,我不知道您會做什麼。