Ir al contenido
EuroVPSEuroVPS

Pruebe una actualización de PHP antes de conmutar, con el rollback preparado

Pasar una aplicación a una nueva rama de PHP con seguridad: instalar en paralelo, hacer lint y escaneo del código, trazar lo que el tráfico carga realmente, y después cambiar un socket.

Por EuroVPS Engineering

5 min de lectura
Pruebe una actualización de PHP antes de conmutar, con el rollback preparado

La mayoría de las actualizaciones de PHP fallan de la misma forma: la versión nueva se instala encima de la antigua, la página de inicio carga, todo el mundo se relaja, y el primer administrador que abre una pantalla poco usada a la mañana siguiente recibe una página en blanco. Este es el método que usamos para encontrar esos problemas en el banco de pruebas, antes del cutover, con una vuelta atrás que lleva segundos.

Por qué las actualizaciones siguen rompiendo cosas

Cada rama de PHP elimina funciones, endurece el manejo de tipos y convierte avisos antiguos en errores fatales. Una aplicación que ha crecido durante años lleva rutas de código que nadie ha ejercitado en mucho tiempo: una exportación, un módulo de pago heredado, un informe de administración. Una prueba rápida de las páginas públicas nunca llegará a ellas.

Las aplicaciones comerciales añaden una segunda capa. Los ficheros protegidos por un encoder solo se ejecutan si el loader para la nueva rama de PHP admite el formato con el que fueron codificados, y eso es una propiedad de cada fichero, no de la página comercial de la aplicación.

Paso uno: instale en paralelo, nunca encima

Instale el PHP de destino junto al actual. En RHEL y sus derivados, las software collections de Remi lo hacen sencillo: cada rama vive en su propio prefijo, con su propio servicio FPM y su propio socket. Cree un pool para el sitio en la versión nueva, idéntico al pool antiguo salvo por el binario y la ruta del socket.

Para los visitantes todavía no ha cambiado nada. Las dos versiones están en marcha, y el servidor web sigue apuntando al socket antiguo.

Paso dos: haga lint de cada fichero con el nuevo binario

La prueba más barata es pedir al nuevo PHP que analice cada fichero:

NEW_PHP=/path/to/new/php
find /var/www/app -name '*.php' -print0 \
  | xargs -0 -n1 -P8 "$NEW_PHP" -l 2>&1 \
  | grep -v '^No syntax errors'

Esto detecta la sintaxis que la nueva rama ya no acepta. No detecta funciones eliminadas ni cambios de comportamiento, porque esos solo fallan en tiempo de ejecución. Para eso, ejecute un escaneo estático de compatibilidad, como PHP_CodeSniffer con el estándar PHPCompatibility, fijado a su rama de destino:

phpcs -p --standard=PHPCompatibility --runtime-set testVersion <target> \
      --extensions=php /var/www/app

Espere ruido de las librerías de terceros y del código muerto. La lista es un mapa, no un veredicto.

Paso tres: averigüe qué ficheros carga realmente el tráfico

Una aplicación puede contener decenas de miles de ficheros PHP, de los que solo una fracción llega a ejecutarse. Quiere dedicar el esfuerzo de compatibilidad a esa fracción, y quiere estar seguro de que incluye las rutas de usuarios identificados y de administración.

OPcache ya lo sabe. Tras un día laborable normal en la versión actual, incluyendo personas que inician sesión y administradores haciendo su trabajo, pregúntele qué scripts ha compilado:

<?php
// protect this file: admin network only, and delete it afterwards
$s = opcache_get_status(true);
foreach (array_keys($s['scripts']) as $f) { echo $f, "\n"; }

Guarde la lista, compárela con los resultados del lint y del escaneo, y los problemas que de verdad van a aparecer suben a lo alto. Los ficheros que aparecen en el escaneo pero nunca en la traza son candidatos a eliminarse, no a arreglarse.

Paso cuatro: lea las cabeceras de los ficheros codificados

Para el software comercial codificado, compruebe cada fichero en lugar de fiarse de un cartel de compatibilidad. Los ficheros codificados llevan una cabecera en texto plano que nombra el encoder y el formato:

grep -rl --include='*.php' 'ionCube Loader' /var/www/app \
  | while read -r f; do head -c 400 "$f" | strings | head -3; done | sort | uniq -c

Agrupe los ficheros por formato de encoder, y después confirme que el loader de su rama de PHP de destino ejecuta ese formato. Un módulo codificado hace años para una rama antigua es exactamente el fichero que se negará a cargar en la nueva, y es mejor saberlo por una cabecera que por un cliente.

Paso cinco: ensaye en el pool nuevo

Apunte un hostname de prueba, o su propia IP mediante una regla de cabecera, al socket nuevo mientras producción sigue en el antiguo. Recorra las rutas de la traza de OPcache, con especial atención a las que solo alcanzan los usuarios identificados y los administradores, y observe el log de errores del pool nuevo mientras lo hace:

tail -F /var/log/php-fpm/app-new-error.log | grep -E 'Fatal|Uncaught|Deprecated'

Los avisos de deprecación no son fallos hoy, pero son la lista para la próxima actualización. Anótelos.

Paso seis: cambie una línea, mantenga el pool antiguo en marcha

El cutover es un cambio en el servidor web: el socket FastCGI.

# fastcgi_pass unix:/run/php-fpm/app-old.sock;
fastcgi_pass unix:/run/php-fpm/app-new.sock;
nginx -t && systemctl reload nginx

El rollback es la misma edición a la inversa, aplicada en segundos, sin cambios de paquetes y sin reiniciar nada salvo la configuración del servidor web. Acuerde la hora con el propietario del sitio, vigile las tasas de error y los tiempos de respuesta durante la primera hora, y mantenga el pool antiguo en marcha durante un periodo de rodaje lo bastante largo para cubrir las tareas semanales y mensuales. Solo entonces retírelo.

En una pila de varios nodos, hágalo un nodo web cada vez detrás del balanceador, de modo que incluso una mala sorpresa alcance a una fracción del tráfico durante unos minutos.

Paso siete: mantenga cron y la CLI al mismo paso

El servidor web no es lo único que ejecuta PHP. Las tareas programadas, los workers de colas y las herramientas de línea de comandos suelen llamar a una ruta de binario fija. Encuéntrelos:

grep -rE 'php[0-9]*( |$)|/bin/php' /etc/cron* /var/spool/cron /etc/systemd/system 2>/dev/null

Páselos al nuevo binario en la misma ventana de cambio, o deliberadamente después, pero nunca por accidente.

Su lista de comprobación

  • El nuevo PHP instalado junto al antiguo, con su propio pool y su propio socket.
  • Cada fichero con lint del nuevo binario, y un escaneo de compatibilidad para la rama de destino.
  • Una traza de OPcache de lo que carga el tráfico real, incluidas las rutas de usuarios identificados y de administración.
  • Los ficheros codificados agrupados por formato y contrastados con el nuevo loader.
  • Un ensayo en el pool nuevo con su log de errores abierto.
  • Un cutover de una línea, el pool antiguo conservado hasta que termina el rodaje, y cron movido a propósito.

Si tiene una actualización que lleva tiempo posponiendo, con gusto la planificamos con usted.

cta: Hable con un ingeniero

Related

  • /what-we-do/data-web/php-upgrades-security-patching/
  • /what-we-do/data-web/performance-audits/
  • /what-we-do/data-web/high-availability-web-tiers/

Publicado el 15 de septiembre de 2026.

Todas las notas de campo