【发布时间】:2018-10-28 01:11:07
【问题描述】:
我正在使用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 值(如指定的那样)反映在phpinfo() 的Environment 部分(通过PHP-FPM + NGINX 呈现并从浏览器请求时)和PHP Variables部分,如$_SERVER['LD_LIBRARY_PATH']。
奇怪的是,即使 PHP-FPM 的日志记录设置为 debug,我也看不到任何使用 CLI 时遇到的 libmql1.so 错误的痕迹。 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 的 cmet,在 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:
I asked about this issue on the package maintainer's GitHub tracker,他认为问题源于未能在编译时设置适当的RPATH。
我在回复中解释说我正在设置了一个适当的值,但问题仍然关闭。
不过,我确实注意到了一个有趣的细节,那就是 Ubuntu 18.04 上的编译扩展使用 RUNPATH(而不是 Ubuntu 16.04 中使用的 RPATH)。如果 PHP-FPM 忽略 RUNPATH,而只查找 RPATH,它将解释这种行为。
编辑 2:
这份仍然开放的报告看起来是介绍观察到的行为的绝佳候选者:
https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=859732
(通过 use RPATH but not RUNPATH?上的cmets发现)
编辑 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 守护程序在 Ubuntu 上忽略 LD_LIBRARY_PATH(并且至少从 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 是 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.
那么,如果不是出于上述任何原因,为什么 PHP-FPM 会忽略 LD_LIBRARY_PATH?
编辑 5(解决方案):
一位精明的评论者@vinc17 指出,在运行systemd 的系统上,环境变量(例如LD_LIBRARY_PATH)不一定会传播到通过systemd 单元启动的进程。
换句话说,PHP-FPM 并没有“忽略”LD_LIBRARY_PATH,而是没有将其传递给进程。并且尝试在 PHP-FPM 配置中设置 LD_LIBRARY_PATH 是徒劳的,因为对值做任何有用的事情已经太晚了。
根据这个建议,我想到在 systemd 上下文中设置 LD_LIBRARY_PATH,即在启动 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 的帖子。
【问题讨论】:
-
非常好的分析系统,虽然我不完全理解你的解决方案(Edit5)。那现在只有php-fpm的解决方案吗?您的 php cli 是否适用于 oci8?我遇到了 apache 和 php cli 的问题,所以我使用 Edit3 的解决方案。
-
@Gunni 谢谢!编辑 5 中描述的解决方案仅在从 systemd 中启动 PHP-FPM 守护程序时才相关。此外,Edit 5 不是 only 解决方案;编辑 3 中的那个会“工作”,但它有缺点(请参阅对答案的评论)。对于 PHP CLI,只需
echo 'LD_LIBRARY_PATH="/opt/oracle/instantclient_12_2"' >> /etc/environment。至于 Apache 的 mod_php,虽然我还没有测试过,但我怀疑如果它是从 systemd 内部启动的,那么 Edit 3 或 Edit 5 解决方案将是必要的(我怀疑stackoverflow.com/a/45242468/1772379 的“旧”方法会起作用)。 -
对我来说,“旧”解决方案确实适用于 Ubuntu 18.04 中的 modapache。无论如何,我选择第三种方法,因为它是我的解决方案,它具有最少的文件数来编辑以“让它工作”(tm)无处不在(apache,cli)。我阅读了可能的冲突,但对我来说这些不适用。
标签: php ubuntu oci8 fpm instantclient