当前位置: 首页 > 知识库问答 >
问题:

如何在Ubuntu18.04LTS中使用Oracle即时客户端,在PHP-FPM和NGINX中启用OCI8PHP扩展?

盖博简
2023-03-14

我使用最新的PHP软件包,https://launchpad.net/~ondrej/存档/ubuntu/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()环境部分(当通过PHP-FPM NGINX呈现并从浏览器请求时)和PHP变量部分,作为$_SERVER['LD_LIBRARY_PATH']

奇怪的是,即使PHP-FPM的日志设置为debug,我也没有看到CLI中出现的libmql1.so错误的任何痕迹。在PHP-FPM的有效php.ini中,display_startup_errors=On。

我选择在同一台服务器上查看OCI8扩展是否在Apache中工作,如果我将导出LD_LIBRARY_PATH=/opt/oracle/instantclient_12_2添加到/etc/apache2/envvars;在Apache缺席的情况下,它在启动时抱怨:

PHP警告:PHP启动:无法加载动态库“oci8”。第0行未知的so'(trued:/usr/lib/php/20170718/oci8.so(libmql1.so:无法打开共享对象文件:没有这样的文件或目录),/usr/lib/php/20170718/oci8.so.so(/usr/lib/php/20170718/oci8.so.so:无法打开共享对象文件:没有这样的文件或目录))

在Ubuntu 16.04 LTS上,根据我在这里的观察和关于https://stackoverflow.com/a/45242468/1772379,这在Ubuntu 17.10和Ubuntu 18.04 LTS中有所改变。

有没有其他人特别在Ubuntu 18.04 LTS上尝试过这个?

我已经在两个不同的流浪者虚拟机上尝试过了,laravel/homesteadbox 6.0。0和ubuntu/bionic64box v20180509。0.0,两者的行为相同。

任何其他想法都将不胜感激!

编辑1:

我在包维护者的GitHub跟踪器上询问了这个问题,他建议问题源于未能在编译时设置适当的RPATH

我在答复中解释说,我正在设定一个适当的值,但问题仍然没有解决。

然而,我确实注意到一个有趣的细节,那就是Ubuntu 18.04上编译的扩展使用了RUNPATH(而不是RPATH,后者在Ubuntu 16.04中使用)。如果PHP-FPM忽略运行路径,并且只查找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在这个例子中不能正常工作,但是将即时客户端库路径添加到链接器配置似乎是一个更好的方法。

编辑4:

我在上一次编辑中指出,修改ldconfig是一种更好的方法,但后来意识到(根据评论员的建议),这样做可能会导致不必要的库冲突,因为其影响是系统范围的。

事后看来,将运行库链接修改的“附带损害”降至最低是有意义的,方法是通过LD_LIBRARY_PATH将它们限制在执行环境中。因此,我的动机是确定为什么这不能在Ubuntu 18.04 LTS上工作。

我觉得我已经明确地建立了PHP-FPM守护进程忽略Ubuntu上的LD_LIBRARY_PATH(并且至少从Ubuntu 16.04 LTS开始;参见解释注释)。

ld.so(8)manpage声明(与搜索运行库路径的顺序有关):

使用环境变量LD_LIBRARY_PATH(除非可执行文件以安全执行模式运行;见下文)。

到目前为止,我想不出还有什么其他理由可以忽视这条道路。关于安全执行模式,同一文档中说:

 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_SECURE0):

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指出,在运行System d的系统上,环境变量,如LD_LIBRARY_PATH,不一定会传播到通过System dUnit启动的进程。

换句话说,PHP-FPM并没有“忽略”LD_LIBRARY_PATH,而是没有将其传递给流程。并且试图在PHP-FPM配置中设置LD_LIBRARY_PATH也是徒劳的,因为对该值做任何有用的事情都为时已晚。

根据这个建议,我想到在systemd上下文中设置LD\u LIBRARY\u 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 .

共有1个答案

翟嘉祥
2023-03-14

首先,Debian bug 859732是一个完全不同的问题(我甚至可以说是一个相反的问题):对于这个bug,在搜索路径中存在多个库版本(一个在LD_library_path指定的某个目录中,另一个在运行路径指定的某个目录中),但是动态链接器选择了错误的链接。

在您的例子中,问题是在搜索路径的任何地方都找不到请求的库。还请注意,在您的情况下,似乎是PHP试图打开库(通过dlopen?),因为消息以"PHP Warning:"开头。然而,这种机制似乎与通常的动态链接相同。

安装库后,您至少需要以下一项:

  • 如果库已安装在默认搜索的目录中,则没有什么特别之处。因为你有一个错误,这不是你的情况
  • 在运行路径中提供目录,必须在需要库的软件编译时指定该目录。问题是,在Linux下,这不是标准的构建工具所能做到的,在不破坏其他东西的情况下正确地完成它可能会很复杂。然而,在dlopen的上下文中,软件(这里是PHP)可能已经设置了一个可以称之为“插件搜索路径”的路径,您可以在其中放置库
  • LD\u LIBRARY\u PATH中提供目录。这是您尝试的,但您的LD\u LIBRARY\u路径似乎不正确。库通常安装在名为lib(或lib32lib64的子目录中。因此,export-LD\u-LIBRARY\u-PATH=/opt/oracle/instantclient\u 12\u 2似乎是错误的。搜索库的完整路径名oci8。因此,只需将此路径名的目录部分用于LD\u LIBRARY\u路径

注意:strace可能有助于查看哪些目录被认为是搜索库的目录。编辑:lddObjppp-p是查找搜索路径的其他有用工具。

编辑2:选择使用运行路径时要注意的另一点是,在使用RPATH时会发现间接库依赖项,但在使用RUNPATH时不会发现(因此,在后一种情况下,如果所有依赖项依赖于其他库,则它们也需要有一个运行路径,以便可以在不诉诸LD_LIBRARY_path的情况下找到所有库)。这在LD.so(8)手册页的最新版本中有记录:

使用二进制文件(如果存在)的DT_RUNPATH动态部分属性中指定的目录。搜索此类目录只是为了找到dtu NEEDED(直接依赖项)条目所需的对象,而不适用于这些对象的子对象,这些子对象本身必须有自己的dtu运行路径条目。这与DT_RPATH不同,后者用于搜索依赖关系树中的所有子级。

这可能就是为什么,在不使用LD_LIBRARY_PATH的情况下,这适用于16.04(其中使用了RPATH),而不适用于18.04(其中使用了RUNPATH)。

 类似资料:
  • 我已经在Linux服务器(Linux2.6.32-642.4.2.el6.x86_64GNU/Linux)中成功配置了Oracle钱包。 当我尝试使用数据库连接字符串执行以下命令时,它工作得很好。 $/sqlplus/@TESTDB SQL*Plus:发布11.2.0.3.0于2016年10月18日星期二07:12:49生产 版权所有(c)1982、2011,Oracle。保留所有权利。 连接到:

  • 问题内容: 我已经在Ubuntu 14.04(Trusty Tahr)上安装了PHP 7和MySQL 5.5.47。 我已经使用以下方法检查了已安装的扩展程序: 它输出: 我也无法使用phpinfo()看到MySQLi扩展。如何在PHP 7中启用/安装MySQLi扩展? 这就是为什么我不能使用phpMyAdmin的原因。它说:“缺少mysqli扩展名。” 问题答案: 我找到了解决方案。我可以在ph

  • 创建okhttp3客户端,使用sni地址访问web服务器,但握手失败,错误消息为“javax.net.ssl.SSLHandShakeException:握手期间远程主机关闭连接”。 有人知道如何在okhttp3客户端代码中使用sni地址吗?

  • 我试图使用nginx php-fpm与nginx选项'保留' 在tcp端口(9000)或unix套接字( /var/run/php5-fpm.socket)上启动php-fpm时,这些错误是可见的。 这里的目的是尽可能减少Nginx-php-fpm之间新的tcp/socket连接开销,并尽可能重用连接。请注意,我将nginx'keepalive 20'保存为php fpm'pm。最大请求数=0'

  • 我有Windows XP,最近我安装了32位wamp(apache mysql php)。我测试了安装(使用php和apache连接到mysql数据库),一切正常。 现在我需要连接到Oracle数据库,所以我尝试启用一些与Oracle和oci8相关的扩展。 我做到了: 1)我打开php.ini文件,删除了以下行前的分号: 之前: 之后: 2)我重启了apache和所有服务,我得到了一个 “PHP启

  • 问题内容: 是否可以在每次执行循环时回显?例如: 我不想看到循环结束时打印所有内容,而是希望它每次都打印每个结果。 问题答案: 最终解决方案 这就是我发现的: Flush在Apache的mod_gzip或Nginx的gzip下不起作用,因为从逻辑上讲,它正在对内容进行gzip处理,并且这样做必须缓冲内容才能对其进行gzip处理。任何类型的Web服务器gzip压缩都会影响此。简而言之,在服务器端,我