AWS & GNU/Linux: optimizando aplicaciones web

GNU/Linux: optimizando servidores
GNU/Linux: optimizando servidores

Aquí estoy de nuevo dejando mi granito de arena en todo esto de la informática. Hoy traigo un howto para la optimización de servidores web. Simplemente, son unas propuestas de configuraciones totalmente estándar y disponibles en las documentaciones oficiales. Pero que mejoran mucho el rendimiento de aplicaciones web, hechas en lenguajes como HTML, CSS, Javascript, PHP, Java, Python, SQL, DQL..

Este post es para optimizar aplicaciones web del tipo WordPress, Prestashop, Magento, Drupal, Symfony.. etcétera. Es una visión general de cómo funcionan las cosas a bajo nivel. Digo a bajo nivel, porque están muy de moda las arquitecturas server-less, o los servidores auto-administrados, que también necesitan su optimización. Pero aquí nos remangaremos la camisa, y nos embarraremos hasta el cuello mientras que vamos investigando, aprendiendo y aplicando, para cada proyecto en concreto. Este es un post para los que nos gusta trabajar los servidores, para los que nos gusta ponernos manos a la obra, para experimentar nosotros mismos con los proyectos, rascando cada milisegundo para conseguir que todo vaya lo más rápido posible..

Al grano, una arquitectura del sistema de ejemplo

Para elaborar este post, he tratado de diseñar una arquitectura de servicios web más o menos general. Esto nos puede crear una visión, a grandes rasgos, de cómo conectar los servicios que más se suelen usar. Pero hay muchas más arquitecturas posibles, muchas.. Es decir, esto sólo es un ‘por ejemplo’ de arquitectura, que podría ser el de la siguiente imagen:

Arquitectura de servicios para una web.

En este ejemplo intervienen muchos servicios, pero para abordar otros sistemas, simplemente cambiarán los servicios. En otros sistemas quizá tengamos servicios de colas de mensajes, servicios de mailing masivo, notificaciones push, colas de procesamiento y almacenamiento para ficheros de resultado, quizá Java corriendo en servidor, etcétera.. pero las ideas generales serán las mismas.

¡No hay una única optimización que sea la mejor para todos los proyectos!

No puedes coger una arquitectura construida que te funcione bien, y simplemente aplicarla a todos los proyectos web. Pueden parecerse en parte, pero habrá casos en los que unas configuraciones de los servicios no serán adecuadas para unos proyectos, pero para otros sí, ¡incluso aunque uses el mismo CMS!

Y por otro lado, aunque la arquitectura general sea a base de micro-servicios, o aunque tengas partes server-less, aunque no lo veas.. o aunque lo tengas auto-administrado.. por debajo.. tendrás toda esta serie de servicios, que deberán de estar bien orquestados según cada proyecto.

Los registros, los logs, la información vale su peso en oro

Lo primero por lo que empezar es por tener localizados todos los registros. Hay que revisar cada servicio, activando los registros y configurando los niveles de los eventos que se van a grabar en dichos registros.

Hay servicios que te dejarán configurar niveles de desarrollo, informativos, sólo errores, producción, etc. Dependiendo de dicho nivel de registro, que cada servicio te guardará más o menos información en los registros. En producción no conviene guardar demasiada información, incluso un nivel de desarrollo puede llegar a guardar tanta información para depurar que bloquee el uso en producción.

Lo siguiente será saber donde guarda registro cada servicio o parte del sistema:

  • CloudFront y ELB te guarda registros en S3.
  • Varnish, Apache, ModPagespeed, PHP o MySql te guardan registro en el directorio /var/log/ en Ubuntu Server.
  • Y así sucesivamente..
GoAccess sample data
GoAccess estudiando las peticiones únicas al servidor..

Luego necesitaremos herramientas de análisis de los registros:

Sugiero probar las dos primeras. La primera porque viene en los repositorios GNU/Linux y se puede usar desde línea de comandos, no necesita de casi configuraciones y para hacerte una buena idea de qué es lo que está pasando está muy bien. La segunda la recomiendo porque es un conjunto de herramientas, con su versión Open Source, que te proporcionan una forma muy robusta de consultar los registros. Hay muchas más soluciones, la mayoría de pago..

La parte frontal, el frontend, balanceando la carga, tolerancia a fallos, CDNs, etc..

El autoescalado y tolerancia a fallos es el punto de entrada a la optimización. Hay que duplicar servidores para tolerar fallos, auto-recuperarlos si es posible, o si quieres dejarlos rotos para ver qué ha pasado puedes lanzar nuevos que los sustituyan. Esto lo puedes hacer con CloudFormation, OpsWorks, con contenedores Docker o grupos de auto-escalado. Hay otros servicios en AWS como Beanstalk, o los servicios de aplicaciones server-less, pero en aplicaciones complejas probablemente querremos ir a lo tradicional para asegurarnos de que podremos hacer cualquier cosa.

La tolerancia a fallos va de la mano del auto-escalado. En el auto-escalado se mide el rendimiento de los servidores y servicios, aumentando o disminuyendo su número según la necesidad. Esto también lo puedes hacer en AWS con Opsworks, CloudFormation, contenedores..

Por otro lado conviene cachear los assets (imágenes, CSSs y Javascripts), tanto en un CDN como en un sistema como Varnish o ModPagespeed.

De los servicios citados en Amazon Web Services podemos comenzar por:

  • CloudFront: es una red de distribución de contenidos (CDN), que permite distribuir las visitas entre servidores, balanceadores, o incluso directamente apuntar a servicios de almacenamiento de ficheros.
  • Elastic Load Balancer (ELB): los balanceadores de carga en si mismos.
  • OpsWorks, CloudFormation, AWS Auto Scaling: donde trabajaremos las nubes en sí mismas. Estas secciones son las principales para proporcionar auto-escalado y tolerancia a fallos.
  • CloudWatch: es el panel de control donde podemos añadir todas las métricas y ver qué está pasando a grandes rasgos en las diferentes partes del sistema.

También a nivel interno de un servidor, puedes gestionar estas funciones con Varnish. Además de que internamente los servicios funcionan así, duplicando procesos, paralelizándose para atender más peticiones.

La programación cliente es muy importante

Realmente da completamente igual lo que tengas en la parte de programación de servidor, si no optimizamos la programación cliente. Es decir, hay las siguientes 3 etapas en la ejecución de las webs:

  • El navegador del vistante hace la consulta de la página al servidor.
  • En el servidor se ejecuta toda la programación de servidor, mediante PHP, Java, Python.. con el CMS que tengas, con Laravel o Symfony, etc.. y se devuelve la respuesta al navegador.
  • En el navegador se ejecuta toda la parte de programación cliente y se visualiza la página. Todo en programación cliente se construye utilizando HTML, CSS y Javascript.

Así, sucesivamente, ocurren estas tres etapas con el siguiente click o tecla pulsada. Toda la programación cliente es muy importante. De nada sirve optimizar algunas capas de software sin optimizar otras. Si no prestamos también atención a esta capa que se ejecuta en el navegador, entonces tendremos problemas.

Para identificar problemas rápidamente en la capa cliente tenemos herramientas como las siguientes:

Luego con lupa miramos los informes, y habrá que investigar sobre cada tecnología utilizada en cada proyecto a ver qué podemos ir optimizando.

La programación de la aplicación servidor también influye mucho en el rendimiento

Tal y como se menciona en el apartado anterior, tanto lo que ocurre en la parte cliente como en la parte de servidor influye en el rendimiento. En la parte de servidor tendremos el procesamiento de lenguajes de servidor como PHP, Java, Python, ASP, y un largo muy largo etcétera.

Esperando la respuesta del servidor ante una visita a una página web..

Estas capas de software en servidor no queda más que estudiarlas con lupa. Hay mirar las latencias entre que el navegador envia el mensaje al servidor, y el servidor comienza a devolver la respuesta. ¡Ojo! No el pintado en el navegador.

Si nos fijamos en la imagen anterior, tenemos 323 milisegundos en los que estamos esperando la respuesta del servidor ante la visita a la home de https://jnjsite.com/. En ese tiempo de espera ocurre todo el procesamiento de servidor: se recibe la petición del navegador, se consulta a la base de datos o caché si procede, se construye usando el lenguaje de servidor que tengamos, etc.. Si esos 323 milisegundos se pueden reducir, mejor que mejor. Aquí no queda otra que abrir el proyecto y depurar el código fuente.

La magia de Mod Pagespeed

Mod Pagespeed es un módulo en incubación para Apache y Nginx para optimizar los assets, la parte de front-end. Es decir, con éste módulo instalado en el servidor web podemos optimizar al vuelo las respuestas que va dando el servidor: imágenes, CSS, ficheros Javascript, códigos Javascript y CSS, también se puede optimizar el HTML al vuelo, se puede incluso modificar el HTML antes de devolverlo de forma que cargue las imágenes lentamente, etcétera..

Página del proyecto: https://developers.google.com/speed/pagespeed/module

Si te animas con este módulo te puedo decir que lo he probado en proyectos con puntuaciones de Google oscilando los 10-20 puntos sobre 100 y conseguir subirlos a 50-60 puntos o más sobre 100. Ayuda bastante, aunque insisto, una cosa sin la otra no funcionará muy bien.

¡Cuidado con éste módulo ya que consume mucha memoria por proceso! Según lo combines con Apache en modo MPM Prefork, Worker o Event te consumirá más o menos memoria y CPU. Pero una vez lo consigas equilibrar para tu proyecto y hardware, tendrás muy buenos resultados 😉

Optimizando Apache

Ahora entramos en la parte de back. A partir de aquí decir que los servicios suelen ser parecidos, aunque cada uno con su documentación de su padre y de su madre. Habrá que empaparse y optimizarlo para cada proyecto. No hay configuración mágica, lo siento 🤨

Establecer las configuraciones límite según el hardware es principal. Luego tendremos que elegir entre lo más sencillo, antiguo y estable, a lo más nuevo, complicado y optimizado. Las formas de trabajo:

  • Prefork: en esta configuración tendremos que todo se ejecuta en el mismo proceso, con varios hilos de ejecución. No está muy optimizado, pero es estable. Fácilmente alcanzarás los límites con muchas visitas y pasarás al siguiente.
  • Worker: esta configuración es la evolución de la anterior, en la que se crean varios procesos que pueden servir las peticiones. Más optimizada, menos simple, pero la siguiente es la mejor creo yo.
  • Event: está configuración te permite servir un alto número de peticiones concurrentes. Es la forma más optimizada, pero también puedes alcanzar los límites de hardware antes, ya que permite configuraciones que se comerán la memoria si no se ajusta.

La configuración más evolucionada será MPM Event, optimiza más los recursos, pero unas configuraciones sin ajustar harán un efecto negativo. Conviene empaparse de la documentación, dejo aquí un ejemplo de configuraciones de MPM Event para el que quiera probar:

<IfModule mpm_event_module>
    StartServers           10
    MinSpareThreads        20
    MaxSpareThreads        60
    ThreadLimit            1024
    ThreadsPerChild        70
    MaxRequestWorkers      120
    MaxConnectionsPerChild 8192
    ServerLimit            5
</IfModule>

Para estudiarnos qué significa cada variable, y cómo funcionan en conjunto me remito a la documentación oficial: https://httpd.apache.org/docs/trunk/es/mod/event.html Según cada proyecto, habrá que encontrar las configuraciones idóneas de este haciéndole unas buenas pruebas de estrés.

En el caso de PHP, como servicio

PHP en sus últimas versiones está muy optimizado, conviene poner siempre la última versión. Esto es evidente, pero no es tan evidente el configurar PHP como servicio independiente. Es decir, conviene instalar PHP FPM que es un sí mismo un servicio completo que corre independiente del servidor web. Sus siglas FPM significan FastCGI Process Manager, que significa manejador de procesos de FastCGI. Es un servidor, configurado como servicio del sistema, dedicado exclusivamente a ejecutar ficheros PHP.

Una configuración de pruebas para el pool nos puede indicar qué cosas podemos configurarle:

pm = dynamic
pm.max_children = 120
pm.start_servers = 25
pm.min_spare_servers = 20
pm.max_spare_servers = 50
pm.process_idle_timeout = 4s
pm.max_requests = 500

Tu servidor frontal conectará a éste servicio extra para decirle: “Ejecútame tal fichero y dame la respuesta.”. Este servicio puedes optimizarlo de igual manera a como puedes optimizar Apache, Nginx, Redis, Memcache, Elasticsearch, etcétera.. Podrás configurarle el número de procesos, hilos, memoria, etcétera.. Me remito a la documentación oficial para más información: https://www.php.net/manual/es/install.fpm.php

De nuevo, según cada proyecto conviene jugar con las configuraciones mientras estresamos el servidor a ver qué tal responde.

Varnish

Este servicio es un caso especial, se trata de un acelerador de aplicaciones web. Pertenece a lo llamado caché de front. Tiene nombre de ente del averno 😅 y es que la primera vez da mucha guerra para configurarlo, o por lo menos a mi me la dió. Aunque cuando le coges el truquillo siempre es la misma historia, y la aceleración que le da a los proyectos es brutal.

Tiene su propio lenguaje de programación para configurarlo, una variante de C. Para algunos proyectos como Magento lo puedes hacer funcionar sin demasiado estudio, gracias a módulos o a configuraciones estándar que lo hacen directamente compatible. Pero si quieres beneficiarte de Varnish, tendrás que hacerte a la idea de que conviene trabajar la plantilla para hacer tu proyecto 100% compatible, y sacarle el máximo provecho.

Resumiendo, es un servicio cachea contenidos y los devuelve extremadamente rápido. Además, puede balancear la carga frontal entre varios servidores. Documentación oficial: https://varnish-cache.org/

Varnish y Mod Pagespeed, sí que se puede combinar

Sí, se puede configurar para hacerlos funcionar juntos aunque no encontrarás mucha información por Internet sobre esto. Necesitarás hacer varias configuraciones en forma de proxys inversos para compatibilizar entre sí los servicios. Por ejemplo, en la arquitectura de ejemplo:

Arquitectura de servidores, optimizando servidores
Una arquitectura de servicios para optimizar la entrega de contenidos web.

Para hacerlos funcionar juntos es la misma idea que la de poner SSL con Varnish. Varnish no es compatible con SSL, si lo quieres tendrás que ponerle delante un Apache o Nginx haciendo de proxy. Y este Apache o Nginx que tienes delante podrás ponerle SSL. La misma idea nos sirve para poner Mod Pagespeed, te puedo decir que funciona 😉

Redis o Memcached

Llegamos entonces a las cachés de back que también están muy de moda. Por ejemplo para Prestashop recomiendan Memcached, mientras que para Magento recomiendan Redis. Te puedo decir que agilizan también mucho el rendimiento.

Varnish, un CDN o Pagespeed son cachés de front porque cachean los assets y contenido que enviamos al front, después de haberlos procesado. Redis o Memcached se llaman cachés de back porque cachean los accesos a la base de datos. Se ponen detrás de la aplicación, del software que procesa las peticiones. Alamacenarán claves y valores en memoria RAM, de forma que reducirán drásticamente el acceso a la base de datos, y servirán estos datos mucho más rápido que con una base de datos tradicional. Esto es así porque almacenan todo en memoria RAM, mientras que una base de datos tradicional almacena la mayor parte en el disco.

Unos enlaces, por aquí Redis: https://redis.io/ y por aquí Memcached: https://memcached.org/

Mariadb, Mysql.. la base de datos, la cocina

Finalmente, todo el contenido dinámico, o la mayor parte, suele estar en una base de datos. No olvidemos optimizar y vigilar el rendimiento a este nivel. Quizá, por ejemplo, las búsquedas en una web, pueden convertirse en consultas a la BD, que puedan llegar a tumbar el sistema casi por completo si no se controlan o se optimizan. Activar los registros de consultas lentas es importante, hacer pruebas de estrés, memorias asignadas a cada servicio, número de procesos máximos, etc..

Necesitamos optimizar también estos servicios de bases de datos. Sólo puedo decir que funcionan igual que los servicios anteriores: tendremos unos servicios que podrán configurarse para recibir concurrentemente peticiones. Conviene revisar sus documentaciones, paralelizar procesos, hacer clusters, etc.. Algunas bases de datos de mucho reconocimiento que te puedo recomendar son las siguientes:

Ya me estoy extendiendo demasiado con este post. Otro día más 🙄 ¡Un saludo!

Dejar un comentario

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *