Docker中的PHP- fopen()或file_put_contents()无法写入/proc/1/fd/2以进行标准输出写入

slwdgvem  于 2023-11-16  发布在  PHP
关注(0)|答案(4)|浏览(175)

在基于CentOS7的Docker容器中运行php-fpm应用程序。
由于我使用Docker,我必须将所有进程的日志输出写入PID 1的输出。这样Docker引擎可以捕获日志以供以后处理(因此它们显示在docker logs中)。
我可以通过将所有日志写入/proc/1/fd/2来实现这一点,而不会在任何应用程序中出现问题。我可以用nginx和echo "test" > /proc/1/fd/2来实现这一点,它在docker logs上显示得很好。
然而,使用php软件,我想做同样的事情,写入/proc/1/fd/2,但无论我做什么,我似乎不能得到文件流去它。

PHP-FPM会产生很多子进程,因此我必须显式地将PID 1写入stdout!

所以我在做这样的事情:

$handle = fopen("/proc/1/fd/2", "w");
fwrite($handle, $msg);
fclose($handle);

字符串

我只得到Warning: fopen(/proc/1/fd/2): failed to open stream: No such file or directory
**注意:**我也用https://www.php.net/manual/en/function.file-put-contents.php尝试过,结果也是一样的。它本质上是fopen,fwrite,fclose的 Package 器。

我试着把它扔到一个符号链接上:

bash-4.2$ ls -l /tmp/stdout
lrwxrwxrwx. 1 nginx nginx 12 Apr 10 11:19 /tmp/stdout -> /proc/1/fd/2


然后像fopen("/tmp/stdout", "w");一样将方法指向它,
我得到完全相同的错误Warning: fopen(/tmp/stdout): failed to open stream: No such file or directory
下面是我需要写入的输出:

bash-4.2$ ls -l /proc/1/fd/2
l-wx------. 1 nginx nginx 64 Apr 10 11:21 /proc/1/fd/2 -> pipe:[3523867]


我可以使用我尝试过的任何其他应用程序(包括nginx和bash)毫无问题地(直接或通过符号链接)写入它。

PHP进程以拥有输出流的用户身份运行

bash-4.2$ ps -ef
UID        PID  PPID  C STIME TTY          TIME CMD
nginx        1     0  0 11:21 ?        00:00:00 php-fpm: master process (/etc/php-fpm.conf)
nginx        7     1  0 11:21 ?        00:00:00 php-fpm: pool www
nginx        8     1  0 11:21 ?        00:00:00 php-fpm: pool www
nginx        9     1  0 11:21 ?        00:00:00 php-fpm: pool www
nginx       10     1  0 11:21 ?        00:00:00 php-fpm: pool www
nginx       11     1  0 11:21 ?        00:00:00 php-fpm: pool www
nginx       12     1  0 11:21 ?        00:00:00 php-fpm: pool www

bash-4.2$ ls -l /proc/1/fd/2
l-wx------. 1 nginx nginx 64 Apr 10 11:21 /proc/1/fd/2 -> pipe:[3523867]


我试着在它扔一些野生权限,他们不采取:

bash-4.2$ chmod 777 /proc/1/fd/2
bash-4.2$ ls -l /proc/1/fd/2
l-wx------. 1 nginx nginx 64 Apr 10 11:21 /proc/1/fd/2 -> pipe:[3523867]


下面是/dev/目录,我不能使用这些,因为它们指向proc/self而不是/proc/1

lrwxrwxrwx. 1 root root   11 Apr 10 11:21 core -> /proc/kcore
lrwxrwxrwx. 1 root root   13 Apr 10 11:21 fd -> /proc/self/fd
crw-rw-rw-. 1 root root 1, 7 Apr 10 11:21 full
drwxrwxrwt. 2 root root   40 Apr 10 11:21 mqueue
crw-rw-rw-. 1 root root 1, 3 Apr 10 11:21 null
lrwxrwxrwx. 1 root root    8 Apr 10 11:21 ptmx -> pts/ptmx
drwxr-xr-x. 2 root root    0 Apr 10 11:21 pts
crw-rw-rw-. 1 root root 1, 8 Apr 10 11:21 random
drwxrwxrwt. 2 root root   40 Apr 10 11:21 shm
lrwxrwxrwx. 1 root root   15 Apr 10 11:21 stderr -> /proc/self/fd/2
lrwxrwxrwx. 1 root root   15 Apr 10 11:21 stdin -> /proc/self/fd/0
lrwxrwxrwx. 1 root root   15 Apr 10 11:21 stdout -> /proc/self/fd/1


我试着改变/dev/fd符号链接指向/proc/1/fd/2,然后试图写入php://fd/2,它也不工作,同样的错误。

7hiiyaii

7hiiyaii1#

php-fpm在nginx用户下运行,我打赌nginx用户没有访问/proc/1/fd/2的权限
要查看nginx用户可以走多远,运行:

sudo -u nginx /bin/sh -c 'namei -l /proc/1/fd/2'

字符串
我打赌它不会达到2,namei会告诉你错误发生在哪里,修复你的权限。
为了记录在案,当我在Debian Linux系统上运行它时,我得到:

root@x2ratma:~# sudo -u www-data /bin/sh -c 'namei -l /proc/1/fd/2'
f: /proc/1/fd/2
drwxr-xr-x root root /
dr-xr-xr-x root root proc
dr-xr-xr-x root root 1
dr-x------ root root fd
                     2 - Permission denied


告诉我www-data用户没有/proc/1/fd文件夹的读访问权。(一个肮脏的快速修复将是:sudo chmod a+rx /proc/1/fd,这意味着give everybody read access to /proc/1/fd

xu3bshqb

xu3bshqb2#

我通过在php.ini中将error_log设置为/proc/1/fd/2来解决这个问题,并使用error_log将日志发送到PHP的系统日志器。

error_log($msg, 0);

字符串

ojsjcaue

ojsjcaue3#

php确实没有能力使用fopen和其他函数打开/proc/*/fd/*
在测试之前,检查- php必须由运行/proc/1/fd/2的同一用户运行
写入/proc/1/fd/2的最佳方法是使用Direct IO php extension。使用Direct IO可以保持与使用php://stderr相同的性能水平。
直接IO示例:

// Flags from error_log php sources (php_log_err_with_severity)
$fp = \dio_open('/proc/1/fd/2', O_CREAT | O_APPEND | O_WRONLY, 0644);
\dio_write($fp, $str);
\dio_close($fp);

字符串
接下来是其他可能的方法来解决这个问题,但是,他们没有为我工作,因为一些问题:
使用popen的性能太低。使用示例:

$fd = popen('cp /dev/stdin /proc/1/fd/2', 'wb');
fwrite($fp, $str);
pclose($fp);


使用error_log会在日志中添加一个前缀,这是我希望避免的。用法示例:

$previous = ini_get('error_log');
ini_set('error_log', '/proc/1/fd/2');
error_log($str, 0);
// I can't set error_log globally
ini_set('error_log', $previous);

kfgdxczn

kfgdxczn4#

我也面临着同样的问题。现在,作为一种解决方法,我将在真实的日志文件上尝试后台“tail -f”,并将输出从tail重定向到/proc/1/fd/1,类似于这样:

tail -f file.log > /proc/1/fd/1 &

字符串
不是最好的解决方案,但它可以完成工作

相关问题