Как включить расширение PHP OCI8, используя мгновенный клиент Oracle, в Ubuntu 18.04 LTS с PHP-FPM и NGINX?


Я использую последние пакеты PHP, доступные с https://launchpad.net /~ондрей/+архив/ubuntu/php .

Когда я собираю и устанавливаю расширение OCI8, кажется, что все в порядке, но, несмотря на включение расширения в конфигурации PHP-FPM, его присутствие не отражается на выводе из phpinfo().

Следующая суть подробно описывает точный процесс, который я использую для настройки, сборки и установки PHP OCI8 добавочный номер:

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

Тот же самый процесс и конфигурация отлично работают в Ubuntu 16.04 LTS, поэтому кажется, что в Ubuntu 18.04 LTS есть некоторые фундаментальные различия, будь то с операционной системой или с рассматриваемыми пакетами PHP.

В качестве небольшой важной (и я подозреваю, имеющей отношение к этой проблеме) справочной информации, в Ubuntu 18.04 LTS расширение не загружается в интерфейс командной строки среда "из коробки", с ошибкой:

Предупреждение 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, с которой я сталкиваюсь в командной строке. Расширение 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

Ничто из этого дела с LD_LIBRARY_PATH не является необходимо в Ubuntu 16.04 LTS и основано на моих наблюдениях здесь и комментариях относительно https://stackoverflow.com/a/45242468/1772379 , который изменился в Ubuntu 17.10 и Ubuntu 18.04 LTS.

Кто-нибудь еще пробовал это, в частности, в Ubuntu 18.04 LTS?

Я пробовал это на двух разных виртуальных машинах Vagrant, laravel/homestead поле 6.0.0 и ubuntu/bionic64 поле 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, но не ПУТЬ ВЫПОЛНЕНИЯ? )

РЕДАКТИРОВАТЬ 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 игнорирует LD_LIBRARY_PATH в Ubuntu (и имеет, по крайней мере, Ubuntu 16.04 LTS; см. Комментарии для объяснения).

Состояния справочной страницы ld.so(8) (в зависимости от порядка поиска путей к библиотекам среды выполнения):

С использованием переменной среды LD_LIBRARY_PATH (если исполняемый файл не запускается в режиме безопасного выполнения; см. Ниже). [sic] в этом случае он игнорируется.

Пока я не могу придумать никакой другой причины для который путь будет проигнорирован. Из secure-exection 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 или его дочерние процессы имеют реальные и эффективные идентификаторы пользователей или групп, которые отличаются (благодаря 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, скорее, это не будучи переданным процессу. И попытки установить 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 .

Author: Ben Johnson, 2018-05-17

1 answers

Во-первых, Ошибка Debian 859732 - это совершенно другая проблема (я бы даже сказал, противоположная проблема): для этой ошибки в пути поиска присутствует несколько версий библиотеки (одна в некотором каталоге, указанном LD_LIBRARY_PATH, и другая в некотором каталоге, указанном путем запуска), но динамический компоновщик выбирает неправильную.

В вашем случае проблема заключается в том, что запрошенная библиотека нигде не найдена в пути поиска. Обратите также внимание, что в вашем случае, это PHP, который, кажется, пытается открыть библиотеку (через dlopen?), так как сообщение начинается с "Предупреждение PHP:". Однако, похоже, что механизмы такие же, как и при обычной динамической компоновке.

После установки библиотеки вам понадобится хотя бы один из:

  • Ничего особенного, если библиотека была установлена в каталоге, поиск в котором выполняется по умолчанию. Поскольку вы получаете сообщение об ошибке, это не ваш случай.
  • Предоставление каталога в пути выполнения, который должно быть указано во время компиляции программного обеспечения, которому потребуется библиотека. Проблема в том, что в Linux это не делается стандартными средствами сборки, и может быть сложно сделать это правильно, не нарушая другие вещи. Однако в контексте dlopen программное обеспечение (здесь PHP), возможно, настроило то, что можно назвать "путем поиска плагинов", где вы можете разместить свои библиотеки.
  • Предоставление каталога в LD_LIBRARY_PATH. Это то, что вы пытались, но ваш LD_LIBRARY_PATH кажется неправильным. Библиотеки являются обычно устанавливается в подкаталоги с именем lib (или lib32 или lib64 в особых случаях). Итак, export LD_LIBRARY_PATH=/opt/oracle/instantclient_12_2 кажется неправильным. Найдите полный путь к библиотеке oci8.so и просто возьмите часть каталога этого пути для LD_LIBRARY_PATH.

Примечание: strace может быть полезно посмотреть, какие каталоги считаются подходящими для поиска библиотек. РЕДАКТИРОВАТЬ: ldd и objdump -p являются другими полезными инструментами для поиска того, что происходит с путями поиска.

РЕДАКТИРОВАТЬ 2: Еще один момент, который следует отметить при выборе пути выполнения следует учитывать, что косвенные зависимости библиотек обнаруживаются при использовании RPATH, но не при использовании RUNPATH (поэтому в последнем случае все зависимости также должны иметь путь выполнения, если они зависят от других библиотек, чтобы все библиотеки можно было найти, не прибегая к LD_LIBRARY_PATH). Это задокументировано в последних версиях ld.so (8) справочная страница:

С использованием каталогов, указанных в атрибуте динамического раздела DT_RUNPATH двоичного файла, если он присутствует. Поиск в таких каталогах выполняется только для поиска объектов, требуемых записями DT_NEEDED (прямые зависимости), и не применяется к дочерним объектам этих объектов, которые сами должны иметь свои собственные записи DT_RUNPATH. Это отличается от DT_RPATH, который применяется для поиска всех дочерних элементов в дереве зависимостей.

Вероятно, поэтому без использования LD_LIBRARY_PATH это работало с 16.04 (где используется RPATH), но не с 18.04 (где используется RUNPATH).

 1
Author: vinc17, 2018-06-05 23:55:04