web-dev-qa-db-fra.com

Comment peut-on activer l'OCI8 PHP extension, en utilisant Oracle Instant Client, dans Ubuntu 18.04 LTS avec PHP-FPM et NGINX?

J'utilise les derniers packages PHP disponibles à partir de https://launchpad.net/~ondrej/+archive/ubuntu/php .

Lorsque je construis et installe l'extension OCI8, tout semble être en ordre, mais malgré l'activation de l'extension dans la configuration PHP-FPM, sa présence n'est pas reflétée dans la sortie de phpinfo().

Le Gist suivant détaille le processus exact que j'utilise pour configurer, compiler et installer l'extension OCI8 PHP:

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

Le même processus et la même configuration fonctionnent parfaitement sous Ubuntu 16.04 LTS. Il semble donc y avoir une différence fondamentale entre Ubuntu 18.04 LTS, qu’il s’agisse du système d’exploitation ou des packages PHP en question.

En tant qu'information de base importante (et je soupçonne qu'elle est pertinente à ce problème), sur Ubuntu 18.04 LTS, l'extension ne parvient pas à se charger dans l'environnement CLI immédiatement, avec l'erreur suivante:

Avertissement PHP: PHP Démarrage: impossible de charger la bibliothèque dynamique '/usr/lib/php/20160303/oci8.so' - libmql1.so: impossible d'ouvrir le fichier d'objet partagé: aucun fichier ou répertoire de ce type dans Inconnu sur ligne 0

J'ai résolu le problème comme suit:

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

J'ai pensé que l'ajout du LD_LIBRARY_PATH à la configuration de l'environnement PHP-FPM pourrait peut-être résoudre le problème équivalent ici:

# echo "env['LD_LIBRARY_PATH'] = /opt/Oracle/instantclient_12_2" >> /etc/php/7.2/fpm/pool.d/www.conf
# systemctl restart php7.2-fpm

Cela provoque effectivement la valeur LD_LIBRARY_PATH, telle que spécifiée, dans la section Environment de phpinfo() (lorsqu’elle est rendue via PHP-FPM + NGINX et demandée à partir d’un navigateur) et dans la section PHP Variables, en tant que $_SERVER['LD_LIBRARY_PATH'].

Bizarrement, même si la journalisation de PHP-FPM est définie sur debug, je ne vois aucune trace de l'erreur libmql1.so que je rencontre avec l'interface de ligne de commande. L'extension OCI8 ne parvient tout simplement pas à se charger, en silence. display_startup_errors = On dans le php.ini effectif de PHP-FPM.

J'ai choisi de voir si l'extension OCI8 fonctionne dans Apache, sur le même serveur, et c'est le cas, à condition que j'ajoute export LD_LIBRARY_PATH=/opt/Oracle/instantclient_12_2 à /etc/Apache2/envvars; en son absence, Apache se plaint au démarrage:

Avertissement PHP: PHP Démarrage: impossible de charger la bibliothèque dynamique 'oci8.so' (tentative: /usr/lib/php/20170718/oci8.so (libmql1.so: impossible d'ouvrir le fichier objet partagé: aucun fichier ou répertoire), /usr/lib/php/20170718/oci8.so.so (/usr/lib/php/20170718/oci8.so.so: impossible d'ouvrir le fichier objet partagé: aucun fichier ou répertoire de ce type)) dans Unknown à la ligne 0

Aucune de ces affaires avec le LD_LIBRARY_PATH n'est nécessaire sur Ubuntu 16.04 LTS, et sur la base de mes observations ici et des commentaires concernant https://stackoverflow.com/a/45242468/1772379 , ce qui a changé dans Ubuntu 17.10 et Ubuntu 18.04 C'EST.

Quelqu'un d'autre a-t-il déjà essayé cela, sur Ubuntu 18.04 LTS, en particulier?

J'ai essayé ceci sur deux machines virtuelles Vagrant différentes, laravel/Homestead box 6.0.0 et ubuntu/bionic64 box v20180509.0.0, et le comportement est le même dans les deux cas.

Toute autre idée serait la plus appréciée!

EDIT 1 :

J'ai posé des questions sur ce problème dans l'outil de suivi GitHub du mainteneur du paquet et il a suggéré que le problème venait de la non-définition d'une RPATH appropriée au moment de la compilation.

J'explique dans ma réponse que je am définissant une valeur appropriée, mais le problème reste clos.

Je remarque toutefois un détail intéressant, à savoir que l'extension compilée sous Ubuntu 18.04 utilise RUNPATH (et non RPATH, utilisée dans Ubuntu 16.04). Si PHP-FPM ignore RUNPATH et ne recherche que RPATH, cela expliquerait ce comportement.

EDIT 2 :

Ce rapport encore ouvert semble être un excellent candidat pour avoir introduit le comportement observé:

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

(découvert grâce aux commentaires sur utilise RPATH mais pas RUNPATH? )

EDIT 3 :

Sur les conseils d'un commentateur, j'ai réexaminé la mise à jour de la configuration ld avant de construire l'extension, ce qui a résolu le problème! J'avais déjà essayé cela auparavant, mais j'ai dû oublier quelque chose entre les tentatives de construction:

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

Je ne sais toujours pas pourquoi LD_LIBRARY_PATH ne fonctionne pas comme il se doit dans ce cas, mais l'ajout du chemin de la bibliothèque Instant Client à la configuration de l'éditeur de liens semble également une meilleure approche.

EDIT 4 :

Dans ma précédente édition, j’avais déclaré que la modification de la variable ldconfig constituait une meilleure approche, mais j’ai réalisé (sur le bon conseil du commentateur) que cela pouvait entraîner des conflits indésirables entre bibliothèques, car les effets s’appliquaient à l’ensemble du système.

En rétrospective, il est logique de minimiser les "dommages collatéraux" résultant des modifications de liaison des bibliothèques d’exécution en les limitant à l’environnement d’exécution via le LD_LIBRARY_PATH. En conséquence, je suis motivé pour déterminer pourquoi cela ne fonctionne pas sur Ubuntu 18.04 LTS.

Je sens que j'ai définitivement établi que le démon PHP-FPM ignore LD_LIBRARY_PATH sur Ubuntu (et depuis au moins Ubuntu 16.04 LTS; voir Commentaires pour des explications).

La page de manuel ld.so(8) indique (en fonction de l'ordre dans lequel les chemins d'accès aux bibliothèques d'exécution sont recherchés):

Utilisation de la variable d'environnement LD_LIBRARY_PATH (sauf si le fichier exécutable est exécuté en mode d'exécution sécurisée; voir ci-dessous). dans ce cas, il est ignoré.

Pour le moment, je ne peux penser à aucune autre raison pour laquelle le chemin serait ignoré. De secure-execution mode, le même document dit:

 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.

Tout d’abord, le mode d’exécution sécurisée semble ne pas être en vigueur, car les exécutables PHP ne présentent pas cet indicateur (AT_SECURE est 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

Il m'est apparu que les processus du pool FPM enfant pouvaient présenter des valeurs AT_SECURE différentes, mais la sortie était identique pour le démon PHP-FPM lui-même, ainsi que pour tous les processus enfants. Le parent et les enfants ont tous les valeurs suivantes:

# 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

Deuxièmement, aucune de ces raisons ne semble s'appliquer, compte tenu de ce qui suit:

1) Rien n'indique que PHP-FPM ou ses processus enfants aient des identifiants d'utilisateur ou de groupe réels et effectifs différents (grâce à https://unix.stackexchange.com/a/202359 pour cette commande):

# 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) Les fichiers binaires en question n’ont aucune capacité (les commandes suivantes ne produisent aucune sortie):

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

3) Je me suis assuré qu'AppArmor est désactivé (il n'a pas de stratégie qui devrait affecter PHP-FPM, de toute façon):

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

Alors, pourquoi PHP-FPM ignore-t-il LD_LIBRARY_PATH, si ce n’est pour l’une des raisons susmentionnées?

EDIT 5 (Solution) :

Un commentateur astucieux, @ vinc17, souligne que sur les systèmes exécutant systemd, les variables d'environnement, telles que LD_LIBRARY_PATH, ne sont pas nécessairement propagées aux processus démarrés via une unité systemd.

En d'autres termes, PHP-FPM n'est pas "ignorant" LD_LIBRARY_PATH, mais plutôt, il n'est pas transmis au processus. Et les tentatives visant à définir LD_LIBRARY_PATH dans la configuration de PHP-FPM sont vaines, car il est trop tard pour faire quoi que ce soit d’utile avec la valeur.

Sur ce conseil, je me suis mis à définir LD_LIBRARY_PATH dans le contexte systemd, à savoir dans le ou les fichiers Unit qui démarrent le ou les démons PHP-FPM, auquel cas PHP-FPM charge correctement l'extension OCI8.

Il va sans dire que nous souhaitons éviter de modifier le fichier du mainteneur du paquet (afin d'éviter les conflits lors de futures mises à niveau), nous l'étendons donc à la place:

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

A ce fichier, nous ajoutons les éléments suivants:

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

Et pour que le changement soit effectif:

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

Pour un exemple plus complet, qui concerne plusieurs versions co-installées PHP, veuillez consulter mon message à l’adresse https://github.com/oerdnj/deb.sury.org/issues/865#issuecomment- 395441936 .

5
Ben Johnson

Tout d'abord, le bogue Debian 859732 est un problème complètement différent (je dirais même un problème opposé): pour ce bogue, plusieurs versions de la bibliothèque sont présentes dans le chemin de recherche (une dans un répertoire spécifié par LD_LIBRARY_PATH un dans un répertoire spécifié par le chemin d’exécution), mais le mauvais est choisi par l’éditeur de liens dynamique.

Dans votre cas, le problème est que la bibliothèque demandée n’est trouvée nulle part dans le chemin de recherche. Notez également que dans votre cas, c’est PHP qui semble essayer d’ouvrir la bibliothèque (via dlopen ?), Car le message commence par "PHP Warning:". Cependant, il semble que les mécanismes soient les mêmes qu'avec les liens dynamiques habituels.

Après avoir installé la bibliothèque, vous avez besoin d'au moins l'un des éléments suivants:

  • Rien de spécial si la bibliothèque a été installée dans un répertoire qui est recherché par défaut. Puisque vous obtenez une erreur, ce n'est pas votre cas.
  • Fournir le répertoire dans un chemin d’exécution, qui doit être spécifié lors de la compilation du logiciel qui aura besoin de la bibliothèque. Le problème est que sous Linux, cela n’est pas fait en standard par les outils de compilation, et il peut être complexe de le faire correctement sans casser d’autres choses. Cependant, dans le contexte de dlopen, le logiciel (ici, PHP) peut avoir mis en place ce que l’on peut appeler un "chemin de recherche de plug-in", dans lequel vous pouvez placer vos bibliothèques.
  • Fournir le répertoire en LD_LIBRARY_PATH. C’est ce que vous avez essayé, mais votre LD_LIBRARY_PATH semble incorrect. Les bibliothèques sont généralement installées dans des sous-répertoires nommés lib (ou lib32 ou lib64 dans des cas spécifiques). Donc, export LD_LIBRARY_PATH=/opt/Oracle/instantclient_12_2 semble faux. Recherchez le chemin complet de la bibliothèque oci8.so et prenez simplement la partie répertoire de ce chemin pour LD_LIBRARY_PATH.

Remarque: strace peut être utile pour connaître les répertoires pris en compte pour la recherche des bibliothèques. EDIT: ldd et objdump -p sont d’autres outils utiles pour trouver ce qui se passe dans les chemins de recherche.

EDIT 2: Un autre point à noter lorsque vous choisissez d'utiliser un chemin d'exécution est que des dépendances de bibliothèque indirectes sont trouvées lorsque RPATH est utilisé, mais pas lorsque RUNPATH est utilisé (donc, dans ce dernier cas, toutes les dépendances aussi devez avoir un chemin d’exécution s’ils dépendent d’autres bibliothèques afin que toutes les bibliothèques puissent être trouvées sans recourir à LD_LIBRARY_PATH). Ceci est documenté dans les versions récentes de la page de manuel ld.so (8):

Utilisation des répertoires spécifiés dans l'attribut DT_RUNPATH dynamic section du fichier binaire, le cas échéant. Ces répertoires ne sont recherchés que pour rechercher les objets requis par les entrées DT_NEEDED (dépendances directes) et ne s'appliquent pas aux enfants de ces objets, qui doivent eux-mêmes avoir leurs propres entrées DT_RUNPATH. Ceci diffère de DT_RPATH, qui est appliqué aux recherches pour tous les enfants de l'arbre de dépendance.

C'est probablement pourquoi, sans utiliser LD_LIBRARY_PATH, cela fonctionnait avec 16.04 (où RPATH est utilisé) mais pas avec 18.04 (où RUNPATH est utilisé).

1
vinc17