如何在带PHP-FPM和NGINX的Ubuntu 18.04 LTS中使用Oracle Instant Client启用OCI8 PHP扩展? [英] How does one enable the OCI8 PHP extension, using the Oracle Instant Client, in Ubuntu 18.04 LTS with PHP-FPM and NGINX?

查看:128
本文介绍了如何在带PHP-FPM和NGINX的Ubuntu 18.04 LTS中使用Oracle Instant Client启用OCI8 PHP扩展?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在使用从 https://launchpad.net获得的最新PHP软件包./〜ondrej/+ archive/ubuntu/php .

当我构建并安装OCI8扩展时,一切似乎都井井有条,但是尽管在PHP-FPM配置中启用了该扩展,但它的存在并未反映在phpinfo()的输出中.

When I build and install the OCI8 extension, everything appears to be in order, but despite enabling the extension in the PHP-FPM configuration, its presence is not reflected in the output from phpinfo().

以下要点详细介绍了我用于配置,构建和安装OCI8 PHP扩展的确切过程:

The following Gist details the exact process that I'm using to configure, build, and install the OCI8 PHP extension:

https://gist.github.com/cbj4074/fa761f60b6f8db431539d76ebfba828e

完全相同的过程和配置在Ubuntu 16.04 LTS上可以很好地工作,因此,似乎在Ubuntu 18.04 LTS上存在一些根本性的区别,无论是所讨论的操作系统还是PHP软件包.

The very same process and configuration work perfectly well on Ubuntu 16.04 LTS, so it seems that there is some fundamental difference on Ubuntu 18.04 LTS, whether with the operating system or the PHP packages in question.

作为一点重要的背景信息(我怀疑与此问题有关),在Ubuntu 18.04 LTS上,该扩展无法立即在CLI环境中加载,并显示以下错误:

As a bit of important (and I suspect relevant to this issue) background information, on Ubuntu 18.04 LTS, the extension fails to load in the CLI environment out-of-the-box, with the error:

PHP警告:PHP启动:无法加载动态库'/usr/lib/php/20160303/oci8.so'-libmql1.so:无法打开共享库文件:在第0行的Unknown中没有这样的文件或目录

PHP Warning: PHP Startup: Unable to load dynamic library '/usr/lib/php/20160303/oci8.so' - libmql1.so: cannot open shared object file: No such file or directory in Unknown on line 0

我这样解决了这个问题:

I resolved the issue like so:

# echo 'LD_LIBRARY_PATH="/opt/oracle/instantclient_12_2"' >> /etc/environment

我认为也许将LD_LIBRARY_PATH添加到PHP-FPM环境配置中可能会解决那里的等效问题:

I thought that perhaps adding the LD_LIBRARY_PATH to the PHP-FPM environment configuration might resolve the equivalent issue there:

# 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'].

This does indeed cause the LD_LIBRARY_PATH value, as specified, to be reflected in both the Environment section of phpinfo() (when rendered via PHP-FPM + NGINX and requested from a browser) and the PHP Variables section, as $_SERVER['LD_LIBRARY_PATH'].

奇怪的是,即使将PHP-FPM的日志记录设置为debug,我也看不到CLI遇到的libmql1.so错误的任何痕迹. OCI8扩展根本无法以静默方式加载. PHP-FPM有效的php.ini中的display_startup_errors = On.

Oddly, even with PHP-FPM's logging set to debug, I don't see any trace of the libmql1.so error that I experience with the CLI. The OCI8 extension simply fails to load, silently. display_startup_errors = On in PHP-FPM's effective php.ini, too.

我选择查看OCI8扩展是否可以在同一服务器上的Apache中工作,并且可以,只要在/etc/apache2/envvars中添加export LD_LIBRARY_PATH=/opt/oracle/instantclient_12_2即可.毫无用处,Apache抱怨启动:

I elected to see if the OCI8 extension works in Apache, on the same server, and it does, provided I add export LD_LIBRARY_PATH=/opt/oracle/instantclient_12_2 to /etc/apache2/envvars; in its absense, Apache complains on startup:

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行的Unknown中

PHP Warning: PHP Startup: Unable to load dynamic library 'oci8.so' (tried: /usr/lib/php/20170718/oci8.so (libmql1.so: cannot open shared object file: No such file or directory), /usr/lib/php/20170718/oci8.so.so (/usr/lib/php/20170718/oci8.so.so: cannot open shared object file: No such file or directory)) in Unknown on line 0

在Ubuntu 16.04 LTS上,根据我在这里的观察以及对 https的评论,与LD_LIBRARY_PATH无关的这项业务都不是必需的. ://stackoverflow.com/a/45242468/1772379 ,在Ubuntu 17.10和Ubuntu 18.04 LTS中进行了更改.

None of this business with the LD_LIBRARY_PATH is necessary on Ubuntu 16.04 LTS, and based on my observations herein and the comments regarding https://stackoverflow.com/a/45242468/1772379 , that changed in Ubuntu 17.10 and Ubuntu 18.04 LTS.

其他人是否曾在Ubuntu 18.04 LTS上尝试过此操作?

Has anybody else tried this, on Ubuntu 18.04 LTS, specifically?

我已经在两个不同的Vagrant VM(laravel/homestead框6.0.0和ubuntu/bionic64框v20180509.0.0)上进行了尝试,并且两者的行为相同.

I've tried this on two different Vagrant VMs, laravel/homestead box 6.0.0, and ubuntu/bionic64 box v20180509.0.0, and the behavior is the same in both.

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

Any other ideas would be most appreciated!

编辑1 :

我在软件包维护者的GitHub跟踪器上询问了此问题他建议问题出在编译时未能设置适当的RPATH.

I asked about this issue on the package maintainer's GitHub tracker and he suggested that the problem stems from failing to set an appropriate RPATH at compile time.

我在答复中解释说,我 am 设置了适当的值,但是问题仍然存在.

I explain in my reply that I am setting an appropriate value, but the issue remains closed.

不过,我确实注意到了一个有趣的细节,那就是Ubuntu 18.04上的编译扩展使用了RUNPATH(而不是Ubuntu 16.04中使用的RPATH).如果PHP-FPM忽略RUNPATH,而仅查找RPATH,它将解释这种现象.

I do notice an interesting detail, however, which is that the compiled extension on Ubuntu 18.04 uses RUNPATH (and not RPATH, which is used in Ubuntu 16.04). If PHP-FPM ignores RUNPATH, and looks only for RPATH, it would explain this behavior.

编辑2 :

这份尚未公开的报告看起来像是介绍了观察到的行为的优秀候选者:

This still-open report looks like an excellent candidate for having introduced the observed behavior:

https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=859732

(通过对使用RPATH而不是RUNPATH的注释发现)

(discovered through comments on use RPATH but not RUNPATH? )

编辑3 :

根据评论者的建议,我在构建扩展程序之前重新检查了更新ld配置的步骤,从而解决了该问题!我之前曾经尝试过,但是在构建尝试之间一定忽略了一些东西:

On a commenter's advice, I reexamined updating the ld configuration before building the extension and that resolved the issue! I had tried this before, but must have overlooked something between build attempts:

# echo /opt/oracle/instantclient_12_2 > /etc/ld.so.conf.d/oracle-instantclient.conf
# ldconfig

我仍然不知道为什么LD_LIBRARY_PATH在这种情况下不起作用,但是将Instant Client库路径添加到链接器配置中似乎是一种更好的方法.

I still don't know why LD_LIBRARY_PATH doesn't work as it should in this instance, but adding the Instant Client library path to the linker configuration seems a better approach besides.

编辑4 :

我在之前的编辑中说过,修改ldconfig是一种更好的方法,但后来(根据评论者的忠告)意识到,这样做可能会导致不良的库冲突,因为这种影响在整个系统范围内.

I stated in my previous edit that modifying the ldconfig constitutes a better approach, but came to realize (on a commenter's good advice) that doing so can cause undesirable library conflicts, because the effects are system-wide.

事后看来,将通过LD_LIBRARY_PATH限制在运行环境中的运行库链接修改所带来的附带损害"降到最低是有意义的.因此,我有动机确定为什么这在Ubuntu 18.04 LTS上不起作用.

In hindsight, it makes sense to minimize the "collateral damage" from runtime library linkage modifications by limiting them to the execution environment via the LD_LIBRARY_PATH. Accordingly, I am motivated to determine why this does not work on Ubuntu 18.04 LTS.

我认为我已经确定地确定PHP-FPM守护程序会在Ubuntu上忽略LD_LIBRARY_PATH(并且至少从Ubuntu 16.04 LTS起具有此功能;请参见注释以获取解释).

I feel that I have established definitively that the PHP-FPM daemon ignores LD_LIBRARY_PATH on Ubuntu (and has since at least Ubuntu 16.04 LTS; see Comments for explanation).

ld.so(8)联机帮助页的状态(与搜索运行时库路径的顺序有关):

The ld.so(8) manpage states (in relation to the order in which runtime library paths are searched):

使用环境变量LD_LIBRARY_PATH(除非可执行文件以安全执行模式运行;请参见下文). [sic]在这种情况下,它会被忽略.

Using the environment variable LD_LIBRARY_PATH (unless the executable is being run in secure-execution mode; see below). [sic] in which case it is ignored.

到目前为止,我还没有想到其他任何会忽略该路径的原因.在secure-execution mode中,同一文档说:

As yet, I cannot think of any other reason for which the path would be ignored. Of secure-execution mode, the same document says:

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

Firstly, Secure-Execution Mode seems not to be in effect, as the PHP executables don't exhibit this flag (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守护程序本身以及任何子进程的输出都是相同的.父母和孩子都具有以下值:

It occurred to me that the child FPM pool processes might exhibit different AT_SECURE values, but the output is identical for the PHP-FPM daemon itself, as well as any child processes. The parent and the children all have the following values:

# 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

第二,鉴于以下原因,这些原因似乎都不适用:

Secondly, none of these reasons seem to apply, given the following:

1)没有迹象表明PHP-FPM或其子进程具有真实有效的用户ID或组ID(由于 https://unix.stackexchange.com/a/202359 (针对此命令):

1) There is no indication that PHP-FPM or its child processes have real and effective user or group IDs that differ (thanks to https://unix.stackexchange.com/a/202359 for this command):

# 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)有问题的二进制文件没有任何功能(以下命令不产生任何输出):

2) The binaries in question do not have any capabilities (the following commands produce no output):

# getcap /usr/lib/php/20170718/oci8.so
# getcap -r /opt/oracle/instantclient_12_2/

3)我确保已禁用AppArmor(无论如何,它没有应影响PHP-FPM的策略):

3) I have ensured that AppArmor is disabled (it doesn't have a policy that should affect PHP-FPM, anyway):

# 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?

So, why does PHP-FPM ignore LD_LIBRARY_PATH, if not for any of the aforementioned reasons?

编辑5(解决方案):

一个精明的注释者@ vinc17指出,在运行systemd的系统上,环境变量(例如LD_LIBRARY_PATH)不一定会传播到通过systemd Unit启动的进程.

An astute commenter, @vinc17 , points-out that on systems running systemd, environment variables, such as LD_LIBRARY_PATH, are not necessarily propagated to processes that are started via a systemd Unit.

换句话说,PHP-FPM不会忽略" LD_LIBRARY_PATH,而是不会将其传达给进程.尝试在PHP-FPM配置中设置LD_LIBRARY_PATH是徒劳的,因为现在对该值执行任何有用的操作为时已晚.

In other words, PHP-FPM isn't "ignoring" LD_LIBRARY_PATH, but rather, it is not being conveyed to the process. And attempts to set LD_LIBRARY_PATH within the PHP-FPM configuration are futile, because it's too late to do anything useful with the value.

根据此建议,我想到在systemd上下文中(即在启动PHP-FPM守护程序的单元文件中)设置LD_LIBRARY_PATH,在这种情况下,将加载PHP-FPM OCI8扩展成功.

On this advice, it occurred to me to set LD_LIBRARY_PATH in the systemd context, namely, in the Unit file(s) that start the PHP-FPM daemon(s), in which case PHP-FPM loads the OCI8 extension successfully.

不用说,我们要避免编辑软件包维护者的文件(以避免将来升级时发生冲突),因此我们将其扩展:

Needless to say, we want to avoid editing the package maintainer's file (to avoid conflicts during future upgrades), so we extend it instead:

# mkdir /etc/systemd/system/php7.1-fpm.service.d
# touch /etc/systemd/system/php7.1-fpm.service.d/environment.conf

在此文件中,我们添加以下内容:

To this file we add the following:

[Service]
Environment=LD_LIBRARY_PATH=/opt/oracle/instantclient_12_2

并使更改生效:

# systemctl daemon-reload
# systemctl restart php7.1-fpm

有关更完整的示例(该示例解决了多个共同安装的PHP版本),请参见

For a more complete example, which addresses multiple co-installed PHP versions, please see my post at https://github.com/oerdnj/deb.sury.org/issues/865#issuecomment-395441936 .

推荐答案

首先,

First, Debian bug 859732 is a completely different issue (I would even say an opposite issue): for this bug, several versions of the library are present in the search path (one in some directory specified by LD_LIBRARY_PATH and a different one in some directory specified by the run path), but the wrong one is chosen by the dynamic linker.

在您的情况下,问题在于在搜索路径中的任何位置都找不到所请求的库.另请注意,在您的情况下,似乎是PHP试图打开库(通过 ?),因为该消息以"PHP警告:"开头.但是,机制似乎与通常的动态链接相同.

In your case, the problem is that the requested library is not found anywhere in the search path. Note also that in your case, it is PHP that seems to try to open the library (via dlopen?), since the message starts with "PHP Warning:". However, it seems that the mechanisms are the same as with usual dynamic linking.

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

After installing the library, what you need is at least one of:

  • 如果库已安装在默认搜索的目录中,则没什么特别的.由于出现错误,因此情况并非如此.
  • 在运行路径中提供目录,必须在需要该库的软件的编译时指定该路径.问题在于,在Linux下,构建工具没有按标准进行此操作,并且正确地做到这一点而不破坏其他功能可能很复杂.但是,在dlopen的情况下,该软件(此处为PHP)可能已经设置了可以称为插件搜索路径"的位置,您可以在其中放置您的库.
  • LD_LIBRARY_PATH中提供目录.这是您尝试过的方法,但是您的LD_LIBRARY_PATH似乎不正确.库通常安装在名为lib(在特定情况下为lib32lib64)的子目录中.因此,export LD_LIBRARY_PATH=/opt/oracle/instantclient_12_2似乎是错误的.搜索库oci8.so的完整路径名,然后仅将该路径名的目录部分作为LD_LIBRARY_PATH.
  • Nothing special if the library has been installed in a directory that is searched by default. Since you get an error, this is not your case.
  • Providing the directory in a run path, which must be specified at compile time of the software that will need the library. The problem is that under Linux, this is not done in standard by the build tools, and it may be complex to do it right without breaking other things. However, in the context of dlopen, the software (here, PHP) may have set up what one can call a "plugin search path", where you can put your libraries.
  • Providing the directory in LD_LIBRARY_PATH. This is what you tried, but your LD_LIBRARY_PATH seems incorrect. Libraries are usually installed in subdirectories named lib (or lib32 or lib64 in specific cases). So, export LD_LIBRARY_PATH=/opt/oracle/instantclient_12_2 seems wrong. Search for the full pathname of the library oci8.so, and just take the directory part of this pathname for LD_LIBRARY_PATH.

注意:strace对于查看要搜索库的目录可能有用. 编辑:lddobjdump -p是其他有用的工具,用于查找搜索路径的状态.

Note: strace may be useful to see what directories are considered to search for the libraries. ldd and objdump -p are other useful tools to find what's going on with the search paths.

选择使用运行路径时要注意的另一点是,当使用RPATH时会发现间接库依赖关系,而当使用RUNPATH时会发现间接库依赖关系(因此,在在后一种情况下,如果所有依赖项都依赖于其他库,则 也需要具有运行路径,以便无需求助于LD_LIBRARY_PATH就可以找到所有库. ld.so(8)手册页的最新版本中对此进行了记录:

EDIT 2: Another point to note when choosing to use a run path is that indirect library dependencies are found when RPATH is used, but not when RUNPATH is used (so, in this latter case, all dependencies also need to have a run path if they depend on other libraries so that all libraries can be found without resorting to LD_LIBRARY_PATH). This is documented in recent versions of the ld.so(8) man page:

使用二进制文件的DT_RUNPATH动态部分属性中指定的目录(如果存在).仅搜索此类目录以找到DT_NEEDED(直接依赖项)条目所需的那些对象,并且不适用于那些对象本身必须具有自己的DT_RUNPATH条目的子对象.这与DT_RPATH不同,DT_RPATH用于在依赖关系树中搜索所有子代.

Using the directories specified in the DT_RUNPATH dynamic section attribute of the binary if present. Such directories are searched only to find those objects required by DT_NEEDED (direct dependencies) entries and do not apply to those objects' children, which must themselves have their own DT_RUNPATH entries. This is unlike DT_RPATH, which is applied to searches for all children in the dependency tree.

这可能就是为什么不使用LD_LIBRARY_PATH而不是使用16.04(使用RPATH的地方)而不使用18.04(使用RUNPATH的地方)的原因.

This is probably why, without using LD_LIBRARY_PATH, this was working with 16.04 (where RPATH is used) but not with 18.04 (where RUNPATH is used).

这篇关于如何在带PHP-FPM和NGINX的Ubuntu 18.04 LTS中使用Oracle Instant Client启用OCI8 PHP扩展?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆