我正在使用https://launchpad.net/~ondrej/+archive/ubuntu/php提供的最新 PHP 包。
当我构建和安装 OCI8 扩展时,一切似乎都井井有条,但尽管在 PHP-FPM 配置中启用了扩展,但它的存在并未反映在phpinfo()
.
以下要点详细说明了我用于配置、构建和安装 OCI8 PHP 扩展的确切过程:
https://gist.github.com/cbj4074/fa761f60b6f8db431539d76ebfba828e
相同的过程和配置在 Ubuntu 16.04 LTS 上运行得非常好,因此在 Ubuntu 18.04 LTS 上似乎存在一些根本差异,无论是使用操作系统还是所讨论的 PHP 包。
作为一些重要的(我怀疑与此问题相关)背景信息,在 Ubuntu 18.04 LTS 上,扩展无法在 CLI 环境中加载,并出现以下错误:
PHP 警告:PHP 启动:无法加载动态库 '/usr/lib/php/20160303/oci8.so' - libmql1.so:无法打开共享对象文件:第 0 行的未知中没有此类文件或目录
我这样解决了这个问题:
# echo 'LD_LIBRARY_PATH="/opt/oracle/instantclient_12_2"' >> /etc/environment
我认为也许添加LD_LIBRARY_PATH
到 PHP-FPM 环境配置可能会解决那里的等效问题:
# echo "env['LD_LIBRARY_PATH'] = /opt/oracle/instantclient_12_2" >> /etc/php/7.2/fpm/pool.d/www.conf
# systemctl restart php7.2-fpm
这确实会导致LD_LIBRARY_PATH
指定的值反映在Environment
(phpinfo()
当通过 PHP-FPM + NGINX 呈现并从浏览器请求时)PHP Variables
和$_SERVER['LD_LIBRARY_PATH']
.
奇怪的是,即使 PHP-FPM 的日志记录设置为debug
,我也看不到任何libmql1.so
使用 CLI 遇到的错误的痕迹。OCI8 扩展只是无法静默加载。display_startup_errors = On
在 PHP-FPM 中也是有效的php.ini
。
我选择查看 OCI8 扩展是否可以在同一台服务器上的 Apache 中工作,并且它确实可以,只要我添加export LD_LIBRARY_PATH=/opt/oracle/instantclient_12_2
到/etc/apache2/envvars
; 在其缺席的情况下,Apache 在启动时抱怨:
PHP警告:PHP启动:无法加载动态库'oci8.so'(试过:/usr/lib/php/20170718/oci8.so(libmql1.so:无法打开共享对象文件:没有这样的文件或目录),/ usr/lib/php/20170718/oci8.so.so(/usr/lib/php/20170718/oci8.so.so:无法打开共享对象文件:没有这样的文件或目录))在第0行的未知
在 Ubuntu 16.04 LTS 上,这些业务都不LD_LIBRARY_PATH
是必需的,根据我在此的观察和关于https://stackoverflow.com/a/45242468/1772379的评论,在 Ubuntu 17.10 和 Ubuntu 18.04 LTS 中发生了变化。
有没有其他人在 Ubuntu 18.04 LTS 上试过这个,特别是?
我已经在两个不同的 Vagrant VM 上进行了尝试,即laravel/homestead
box 6.0.0 和ubuntu/bionic64
box v20180509.0.0,两者的行为是相同的。
任何其他想法将不胜感激!
编辑 1:
我在包维护者的 GitHub 跟踪器上询问了这个问题,他认为问题源于未能RPATH
在编译时设置适当的值。
我在回复中解释说我正在设置一个适当的值,但问题仍然关闭。
然而,我确实注意到了一个有趣的细节,那就是 Ubuntu 18.04 上的已编译扩展使用RUNPATH
(而不是RPATH
Ubuntu 16.04 中使用的 )。如果 PHP-FPM 忽略RUNPATH
并且只查找RPATH
,它将解释这种行为。
编辑 2:
这份仍然开放的报告看起来像是引入观察到的行为的绝佳候选者:
https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=859732
(通过评论发现使用 RPATH 但不是 RUNPATH?)
编辑 3:
根据评论者的建议,我ld
在构建扩展之前重新检查了更新配置并解决了问题!我以前试过这个,但在构建尝试之间一定忽略了一些东西:
# echo /opt/oracle/instantclient_12_2 > /etc/ld.so.conf.d/oracle-instantclient.conf
# ldconfig
我仍然不知道为什么LD_LIBRARY_PATH
在这种情况下不能正常工作,但是将 Instant Client 库路径添加到链接器配置似乎是一种更好的方法。
编辑 4:
我在之前的编辑中说过,修改 是ldconfig
一种更好的方法,但我意识到(根据评论者的好建议)这样做会导致不良的库冲突,因为影响是系统范围的。
事后看来,通过将运行时库链接修改限制在执行环境中,通过LD_LIBRARY_PATH
. 因此,我有动力确定为什么这在 Ubuntu 18.04 LTS 上不起作用。
我觉得我已经明确地确定 PHP-FPM 守护程序LD_LIBRARY_PATH
在 Ubuntu 上会忽略(并且至少从 Ubuntu 16.04 LTS 开始;请参阅评论以获取解释)。
ld.so(8)
手册页状态(与搜索运行时库路径的顺序有关):
使用环境变量 LD_LIBRARY_PATH(除非可执行文件在安全执行模式下运行;见下文)。[原文如此] 在这种情况下,它会被忽略。
到目前为止,我想不出任何其他原因会忽略该路径。其中secure-execution mode
,同一份文件说:
Secure-execution mode
For security reasons, the effects of some environment variables are voided or modified if the dynamic linker determines that the binary
should be run in secure-execution mode. (For details, see the discussion of individual environment variables below.) A binary is exe‐
cuted in secure-execution mode if the AT_SECURE entry in the auxiliary vector (see getauxval(3)) has a nonzero value. This entry may
have a nonzero value for various reasons, including:
* The process's real and effective user IDs differ, or the real and effective group IDs differ. This typically occurs as a result of
executing a set-user-ID or set-group-ID program.
* A process with a non-root user ID executed a binary that conferred capabilities to the process.
* A nonzero value may have been set by a Linux Security Module.
首先,安全执行模式似乎没有生效,因为 PHP 可执行文件没有显示此标志(AT_SECURE
is 0
):
LD_SHOW_AUXV=1 /usr/sbin/php-fpm7.1 -daemonize --fpm-config /etc/php/7.1/fpm/php-fpm.conf
AT_SYSINFO_EHDR: 0x7ffc569e1000
AT_HWCAP: 178bfbff
AT_PAGESZ: 4096
AT_CLKTCK: 100
AT_PHDR: 0x55ceab0c4040
AT_PHENT: 56
AT_PHNUM: 9
AT_BASE: 0x7f823c77f000
AT_FLAGS: 0x0
AT_ENTRY: 0x55ceab19e360
AT_UID: 0
AT_EUID: 0
AT_GID: 0
AT_EGID: 0
AT_SECURE: 0
AT_RANDOM: 0x7ffc56962349
AT_HWCAP2: 0x0
AT_EXECFN: /usr/sbin/php-fpm7.1
AT_PLATFORM: x86_64
我突然想到,子 FPM 池进程可能表现出不同AT_SECURE
的值,但 PHP-FPM 守护程序本身以及任何子进程的输出都是相同的。父级和子级都具有以下值:
# od -t d8 /proc/851/auxv
0000000 33 140722944548864
0000020 16 395049983
0000040 6 4096
0000060 17 100
0000100 3 93903778242624
0000120 4 56
0000140 5 9
0000160 7 140365152313344
0000200 8 0
0000220 9 93903779136352
0000240 11 0
0000260 12 0
0000300 13 0
0000320 14 0
0000340 23 0
0000360 25 140722944193929
0000400 26 0
0000420 31 140722944196579
0000440 15 140722944193945
0000460 0 0
其次,鉴于以下情况,这些原因似乎都不适用:
1)没有迹象表明 PHP-FPM 或其子进程具有不同的真实有效的用户或组 ID(感谢https://unix.stackexchange.com/a/202359的此命令):
# ps -e -o user= -o ruser= | awk '$1 != $2'
systemd+ systemd-timesync
systemd+ systemd-resolve
beansta+ beanstalkd
message+ messagebus
daemon root
systemd+ systemd-network
# ps -e -o group= -o rgroup= | awk '$1 != $2'
systemd+ systemd-timesync
systemd+ systemd-resolve
beansta+ beanstalkd
message+ messagebus
daemon root
systemd+ systemd-network
2) 有问题的二进制文件没有任何功能(以下命令不产生输出):
# getcap /usr/lib/php/20170718/oci8.so
# getcap -r /opt/oracle/instantclient_12_2/
3) 我已经确保 AppArmor 被禁用(它没有应该影响 PHP-FPM 的策略,无论如何):
# systemctl disable apparmor
Synchronizing state of apparmor.service with SysV service script with /lib/systemd/systemd-sysv-install.
Executing: /lib/systemd/systemd-sysv-install disable apparmor
# reboot
# aa-status
apparmor module is loaded.
0 profiles are loaded.
0 profiles are in enforce mode.
0 profiles are in complain mode.
0 processes have profiles defined.
0 processes are in enforce mode.
0 processes are in complain mode.
0 processes are unconfined but have a profile defined.
LD_LIBRARY_PATH
那么,如果不是出于上述任何原因,为什么 PHP-FPM 会忽略?
编辑 5(解决方案):
一位精明的评论者@vinc17 指出,在运行的系统上systemd
,环境变量(例如 )LD_LIBRARY_PATH
不一定会传播到通过systemd
单元启动的进程。
换句话说,PHP-FPM 不是“忽略” LD_LIBRARY_PATH
,而是它没有被传递给进程。并且尝试LD_LIBRARY_PATH
在 PHP-FPM 配置中进行设置是徒劳的,因为对值做任何有用的事情已经太晚了。
根据这个建议,我想到LD_LIBRARY_PATH
在systemd
上下文中进行设置,即在启动 PHP-FPM 守护程序的单元文件中进行设置,在这种情况下 PHP-FPM 会成功加载 OCI8 扩展。
不用说,我们想避免编辑包维护者的文件(以避免在未来升级期间发生冲突),所以我们改为扩展它:
# mkdir /etc/systemd/system/php7.1-fpm.service.d
# touch /etc/systemd/system/php7.1-fpm.service.d/environment.conf
在这个文件中,我们添加以下内容:
[Service]
Environment=LD_LIBRARY_PATH=/opt/oracle/instantclient_12_2
为了使更改生效:
# systemctl daemon-reload
# systemctl restart php7.1-fpm
有关解决多个共同安装的 PHP 版本的更完整示例,请参阅我在https://github.com/oerdnj/deb.sury.org/issues/865#issuecomment-395441936上的帖子。