Как включить расширение 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 .
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
).