在我们众多 PHP pod 中的两个(我们在 Kubernetes 下运行)中,我们遇到了 Unix 套接字泄漏问题。我们的 PHP CLI 进程似乎正在打开但没有关闭许多 unix 套接字,直到在一个不相关的位置 PHP 崩溃并显示
PHP Warning: stream_select(): You MUST recompile PHP with a larger value of FD_SETSIZE.
我们的其他 Pod 也打开了一些 UNIX 套接字,但数量很少,所以这不是问题。
令人困惑的是我们没有使用 UNIX 套接字来做任何事情。与其他进程的所有通信均纯粹通过 TCP/IP。所有插座均为:
ESTABLISHED
状态ss -x
表示这些套接字上没有发送或接收数据netstat
表示他们都有 RefCount
3
,如果这意味着什么的话ss -xp
还表明,每个套接字的两端都由同一个(且唯一)PHP 进程持有。因此...由于某种原因,PHP 程序正在创建 UNIX 套接字,将它们保持打开状态,并抓住该套接字的两端(两个不同的文件描述符),并且不让它们离开。
此外,代码中没有调用
stream_socket_pair
或socket_create_pair
。
我从中收集到的是,这些套接字是作为我正在使用的其他一些 PHP 内置函数的副作用而创建的。 CURL 是这里的主要嫌疑人,但它也在其他 Pod 中大量使用,没有出现问题。事实上,我想不出这两个 pod 正在做而其他 pod 没有做的任何事情。
哪些内置 PHP 函数(包括默认与 PHP 捆绑在一起的扩展中的函数)可能会导致无意中创建 UNIX 套接字对?
补充:
ss -xp
看起来像:
# ss -xp
Netid State Recv-Q Send-Q Local Address:Port Peer Address:Port Process
u_str ESTAB 0 0 * 481304571 * 481304572 users:(("php",pid=7,fd=494))
u_str ESTAB 0 0 * 480934363 * 480934362 users:(("php",pid=7,fd=289))
u_str ESTAB 0 0 * 480899260 * 480899259 users:(("php",pid=7,fd=255))
u_str ESTAB 0 0 * 480853228 * 480853229 users:(("php",pid=7,fd=207))
u_str ESTAB 0 0 * 478659551 * 478659550 users:(("php",pid=7,fd=82))
u_str ESTAB 0 0 * 481263776 * 481263775 users:(("php",pid=7,fd=484))
u_str ESTAB 0 0 * 481014545 * 481014546 users:(("php",pid=7,fd=339))
u_str ESTAB 0 0 * 480847844 * 480847845 users:(("php",pid=7,fd=221))
u_str ESTAB 0 0 * 479446282 * 479446283 users:(("php",pid=7,fd=185))
u_str ESTAB 0 0 * 479430966 * 479430965 users:(("php",pid=7,fd=162))
u_str ESTAB 0 0 * 481391710 * 481391711 users:(("php",pid=7,fd=515))
u_str ESTAB 0 0 * 481013994 * 481013995 users:(("php",pid=7,fd=352))
u_str ESTAB 0 0 * 480884372 * 480884373 users:(("php",pid=7,fd=233))
.... many, may more lines ...
我自己找到了答案。在这里发帖是为了帮助接下来的人。罪魁祸首是... *鼓声*... CURL。特别是,PHP 8.2 中的
curl_multi_init()
打开一对 UNIX 套接字,可能是为了某种跨线程同步。
这是重现它的一段代码:
$handles = [];
for ( $i = 0; $i < 10; $i++ )
$handles[] = curl_multi_init();
echo "Sleeping...";
sleep(10); // Here we have 20 unix sockets open
foreach ($handles as $h)
curl_multi_close($h); // According to PHP docs, this doesn't do anything in PHP 8, but it's good style anyway.
$handles = [];
echo "Sleeping again...";
sleep(10); // Here we have 2 unix sockets open... because the $h variable still holds the last handle
echo "Goodbye!";
我不知道为什么我的本地计算机上没有出现此行为。可能和具体的CURL或者PHP版本有关系。特别是:
我有 PHP 8.2.13 和 CURL 8.5.0(这没有问题),而 Kubernetes pod 有 PHP 8.2.16 和 CURL 7.88.1(这有问题)。
如果有人知道为什么我的机器上没有发生这种情况,请告诉我!
好吧,现在只是为了追查我自己的
CurlMultiHandle
漏洞 - 这是一项更容易管理的任务,超出了这个问题的范围。