Cómo ejecutar varias versiones de PHP en mismo NginX

Trabajando con un cliente me encontré ante este escenario:

En un mismo servidor (Una instancia de AWS) había una aplicación desarrollada usando CodeIgniter y otra basada en Symfony.

Al momento de comenzar mi intervención ambas aplicaciones compartían un mismo servidor web (NginX) y el binario de php para ejecutar algunos scripts de línea de comandos.

El problema era que la versión de CodeIgniter en la que se había realizado el desarrollo (2.2.6) no podía actualizarse a la más reciente (4.1.5) sin re-escribir casi por completo la aplicación.

Mientras tanto, la aplicación Symfony fue siguiendo la evolución del framework bastante de cerca.

Para continuar el mantenimiento de la aplicación Symfony, y aprovechar nuevas caracterísitcas de rendimiento y seguridad, se hacía conveniente actualizar la versión de PHP y, difícilmente este cambio sería soportado por la aplicación vieja.

La mejor solución sería darle a cada aplicación su propio entorno de ejecución de modo de que ninguna se vuelva un cuello de botella para la otra.

La decisión fue mantener ambas versiones del lenguaje disponibles y que cada sitio utilizara la que le correspondiera (Al menos hasta definir qué hacer con la aplicación CodeIgniter).

Pensé en pasar ambas a Docker y no descarto hacerlo más adelante pero, por el momento, me pareció demasiado esfuerzo para poco beneficio.

Para tener dos versiones diferentes de PHP ejecutando en un mismo servidor es necesario:

  1. Instalar la nueva versión
  2. Modificar la configuración del WebServer
  3. Modificar la configuración de los cronjobs

Cómo instalar una nueva versión de PHP

El servidor en cuestión es un Ubuntu, de modo que instalar una nueva versión de PHP (8.0 en mi caso) requiere estos comandos:

sudo add-apt-repository ppa:ondrej/php
sudo apt install php8.0

Con esto queda instalado el binario de php en su versión 8, luego hay que tener en cuenta instalar las extensiones que sean necesarias.

Una vez finalizado el proceso, con este comando:

update-alternatives --display php

Vemos que se ha instalado la nueva versión:

php - manual mode
  link best version is /usr/bin/php8.0
  link currently points to /usr/bin/php7.3
  link php is /usr/bin/php
  slave php.1.gz is /usr/share/man/man1/php.1.gz
/usr/bin/php7.3 - priority 73
  slave php.1.gz: /usr/share/man/man1/php7.3.1.gz
/usr/bin/php8.0 - priority 80
  slave php.1.gz: /usr/share/man/man1/php8.0.1.gz

Notá como el comando php es un link simbólico que está alojado en /usr/bin/php y apunta a /usr/bin/php7.3. Es por eso que php -v muestra:

PHP 7.3.19-1+ubuntu18.04.1+deb.sury.org+1 (cli) (built: Jun 12 2020 07:48:30) ( NTS )
Copyright (c) 1997-2018 The PHP Group
Zend Engine v3.3.19, Copyright (c) 1998-2018 Zend Technologies
    with Zend OPcache v7.3.19-1+ubuntu18.04.1+deb.sury.org+1, Copyright (c) 1999-2018, by Zend Technologies

Es decir, si queremos asegurarnos de estar ejecutando la versión 8.0 el comando que nos da más certeza es /usr/bin/php8.0 -v:

PHP 8.0.13 (cli) (built: Nov 22 2021 09:50:24) ( NTS )
Copyright (c) The PHP Group
Zend Engine v4.0.13, Copyright (c) Zend Technologies
    with Zend OPcache v8.0.13, Copyright (c), by Zend Technologies 

Perfecto. Tengamos esto en cuenta para dentro de un rato cuando veamos lo que pasa con los cronjobs.

Cómo especificar la versión de PHP para cada sitio

Para este punto viene muy bien el hecho de estar usando NginX como servidor web. A diferencia de Apache que puede tener PHP incorporado, NginX se limita a servir contenido estático y delega la ejecución de PHP en otro proceso (php-fpm).

Si miramos el archivo de configuración de cada sitio veremos algo como:

server {
    ...
    location ~ ^/index\.php(/|$) {
        fastcgi_split_path_info ^(.+\.php)(/.*)$;
        fastcgi_pass unix:/var/run/php/php7.3-fpm.sock;
        include fastcgi_params;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;

        fastcgi_intercept_errors off;
        fastcgi_buffer_size 16k;
        fastcgi_buffers 4 16k;
    }

    ...
}

En particular la línea que nos interesa en este caso es:

fastcgi_pass unix:/var/run/php/php7.3-fpm.sock;

Aquí se está definiendo el canal de comunicación entre el servidor web y el proceso que atenderá las peticiones php.

De modo que lo que debemos cambiar es el 7.3 por 8.0 en el archivo que corresponde a la aplicación más nueva.

Claro que, para que esto funcione debemos contar con php8.0-fpm.

Para instalarlo puede usarse el comando:

sudo apt install php8.0-fpm

Y con eso ya contaremos con el proceso FastCGI que necesitamos.

Por supuesto que, antes de aplicar estos cambios en producción es prudente realizar pruebas en un ambiente controlado. Asumiendo que ya se realizaron dichas pruebas y se corrigieron eventuales inconvenientes, con los cambios realizados la aplicación web está lista.

Resta ver qué hacer con las tareas programadas y/o scripts de línea de comandos que se utilicen.

En este caso lo que hay son algunas tareas programadas vía cron.

Cómo especificar la versión de PHP para cada cronjob

Usando el comando crontab -l obtenemos algo como:

# Edit this file to introduce tasks to be run by cron.
# 
# Each task to run has to be defined through a single line
# indicating with different fields when the task will be run
# and what command to run for the task
# 
# To define the time you can provide concrete values for
# minute (m), hour (h), day of month (dom), month (mon),
# and day of week (dow) or use '*' in these fields (for 'any').# 
# Notice that tasks will be started based on the cron's system
# daemon's notion of time and timezones.
# 
# Output of the crontab jobs (including errors) is sent through
# email to the user the crontab file belongs to (unless redirected).
# 
# For example, you can run a backup of all your user accounts
# at 5 a.m every week with:
# 0 5 * * 1 tar -zcf /var/backups/home.tgz /home/
# 
# For more information see the manual pages of crontab(5) and cron(8)
# 
# m h  dom mon dow   command

MAILTO=mauro.chojrin@leewayweb.com
0 6 1 2,5,8,11 * /usr/bin/php /home/ubuntu/followapp/bin/console app:agents:send-transaction-summary

Esto significa que todos los 1 de los meses de Febrero, Mayo, Agosto y Noviembre a las 6:00 AM se ejecuta el script PHP /home/ubuntu/followapp/bin/console pasándole como argumentos app:agents:send-transaction-summary.

El script es el que Symfony dispone para la ejecución de comandos diseñados utilizando el framework.

El argumento especifica cuál de todos los comandos disponibles es el que se desea ejecutar (En este caso se trata de uno diseñado a efectos de enviar a los vendedores un resumen de las transacciones realizadas durante el trimestre anterior).

Y la parte de la línea que dice /usr/bin/php es la que especifica cuál será el binario que deberá invocarse cuando sea el momento indicado. En este caso se trata del binario de PHP.

Claro que, en este servidor donde hay más de una versión de PHP, esta definición puede resultar ambigua (A los ojos humanos claro, para la computadora está muy claro que /usr/bin/php es un alias de /usr/bin/php7.3).

Lo conveniente para evitar confusiones es hacer explícita la versión del intérprete que se requiere, dejando la línea del crontab de esta forma:

0 6 1 2,5,8,11 * /usr/bin/php /home/ubuntu/followapp/bin/console app:agents:send-transaction-summary

Y ahora sí, con este último cambio está todo listo para tener ambas versiones conviviendo en armonía.

Unas últimas notas

Como habrás podido apreciar, no es realmente complejo lograr esta configuración, sin embargo, siendo que el servidor ya se encuentra virtualizado, lo más conveniente sería tener una instancia separada para cada aplicación.

La desventaja de este enfoque es que, además de los costos propios de la infraestructura, la administración puede hacerse un poco más compleja.

Lo que debe evaluarse para decidir es el riesgo de que una aplicación falle y, al competir por los recursos con la otra, deje fuera de línea un sitio que, en principio, podría continuar operativo.

mchojrin

Por mchojrin

Ayudo a desarrolladores PHP a acceder mercados y clientes más sofisticados y exigentes

¿Te quedó alguna duda? Publica aca tu pregunta

Este sitio usa Akismet para reducir el spam. Aprende cómo se procesan los datos de tus comentarios.