Escalando tu aplicación web: pasos básicos

Publicado: 2022-09-13

No es suficiente crear aplicaciones para su negocio, necesita optimizarlas. Una forma efectiva es escalar. En este artículo, aprenderá sobre optimización de código, optimización de arquitectura y cómo crear aplicaciones web escalables en general.

optimizando

Gearheart sugiere hacerse las siguientes preguntas:

  • ¿Las consultas a la base de datos son óptimas (análisis EXPLAIN, uso de índices)?
  • ¿Los datos se almacenan correctamente (SQL vs NoSQL)?
  • ¿Se utiliza el almacenamiento en caché?
  • ¿No hay solicitudes innecesarias al FS oa la base de datos?
  • ¿Son óptimos los algoritmos de procesamiento de datos?
  • ¿La configuración del entorno es óptima: Apache/Nginx, MySQL/PostgreSQL, PHP/Python?

Cada una de estas cuestiones podría tratarse en un artículo separado, por lo que una consideración detallada de las mismas en el marco de este artículo es claramente excesiva. Es importante comprender que antes de comenzar a escalar una aplicación, es muy conveniente optimizar su trabajo tanto como sea posible; de ​​hecho, es posible que no se requiera escalar en absoluto.

Escalada

Suponga que ya optimizó su aplicación, pero aún no puede manejar la carga. En este caso, la solución obvia es distribuir la aplicación en varios hosts para aumentar el rendimiento general de la aplicación aumentando los recursos disponibles. Este enfoque se llama oficialmente "escalar" la aplicación. Más precisamente, la escalabilidad es la capacidad de un sistema para aumentar su rendimiento aumentando la cantidad de recursos disponibles para él.

Hay dos tipos de escalabilidad: vertical y horizontal. La escalabilidad vertical implica aumentar el rendimiento de la aplicación mediante la adición de recursos (CPU, memoria, disco) dentro de un nodo (host). El escalado horizontal es típico de las aplicaciones distribuidas e implica aumentar el rendimiento de la aplicación al agregar otro nodo.

Está claro que la forma más fácil es una simple actualización de hardware (procesador, memoria, disco), es decir, escalamiento vertical. Además, este enfoque no requiere ninguna modificación en la aplicación. Sin embargo, el escalado vertical alcanza rápidamente su límite, después de lo cual el desarrollador y el administrador no tienen más remedio que cambiar al escalado horizontal de la aplicación.

Arquitectura de la aplicación

La mayoría de las aplicaciones web están distribuidas a priori, porque su arquitectura se puede dividir en al menos tres capas: servidor web, lógica comercial (aplicación), datos (base de datos, estática).

Cada una de estas capas se puede escalar. Entonces, si su sistema tiene una aplicación y una base de datos que residen en el mismo host, el primer paso definitivamente debería ser separarlos en diferentes hosts.

el cuello de botella

Procediendo al escalado del sistema, lo primero que se debe hacer es determinar cuál de las capas es el “cuello de botella”, es decir, más lento que el resto del sistema. Para empezar, puede usar utilidades triviales como top (htop) para evaluar el consumo de CPU/memoria y df, iostat para evaluar el consumo de disco. Sin embargo, es deseable proporcionar un host separado con una emulación de carga de batalla (usando AB o JMeter), en el que puede perfilar la aplicación usando utilidades como xdebug, oprofile, etc. Puede usar utilidades como pgFouine para identificar consultas de base de datos limitadas (por supuesto, es mejor hacerlo en función de los registros del servidor de batalla).

Por lo general, depende de la arquitectura de la aplicación, pero en general, los candidatos más probables para un cuello de botella son la base de datos y el código. Si su aplicación maneja una gran cantidad de datos de usuario, es probable que el cuello de botella sea el almacenamiento estático.

Escalado de base de datos

Como se mencionó anteriormente, el cuello de botella en las aplicaciones modernas suele ser la base de datos. Los problemas con él generalmente se dividen en dos clases: rendimiento y la necesidad de almacenar una gran cantidad de datos.

Puede reducir la carga en la base de datos dividiéndola en varios hosts. Existe una aguda dificultad de sincronización entre ellos, que se puede solucionar implementando el esquema maestro/esclavo con replicación síncrona o asíncrona. Para PostgreSQL, puede usar Slony-I para la replicación síncrona y PgPool-II o WAL (9.0) para la replicación asíncrona. Para resolver el problema de dividir las solicitudes de lectura y escritura, así como equilibrar la carga entre los esclavos, puede configurar una capa especial de acceso a la base de datos (PgPool-II).

La preocupación de almacenar grandes cantidades de datos en el caso de las bases de datos relacionales se puede resolver mediante el particionamiento ("particionamiento" en PostgreSQL) o mediante la implementación de la base de datos en una base de datos distribuida como Hadoop DFS.

Puede leer sobre ambas soluciones en el excelente libro sobre la configuración de PostgreSQL.

1. Sin embargo, para almacenar grandes cantidades de datos, la mejor solución es la fragmentación, que es una ventaja inherente de la mayoría de las bases de datos NoSQL (p. ej., MongoDB).

2. Además, las bases de datos NoSQL en general funcionan más rápido que sus hermanas SQL debido a la falta de sobrecarga para analizar/optimizar la consulta, verificar la integridad de la estructura de datos, etc. El tema de comparar las bases de datos relacionales y NoSQL también es bastante extenso y merece un artículo aparte.

3. Por separado, vale la pena señalar la experiencia de Facebook, que usa MySQL sin selecciones JOIN. Esta estrategia les permite escalar la base de datos mucho más fácilmente, al tiempo que transfieren la carga de la base de datos al código, que, como se describirá a continuación, escala más fácilmente que la base de datos.

Escalado de código

  • Las complejidades de escalar el código dependen de cuántos recursos compartidos necesitan sus hosts para ejecutar su aplicación. ¿Serán solo sesiones o necesitará compartir cachés y archivos? De cualquier manera, lo primero que debe hacer es ejecutar copias de la aplicación en varios hosts con el mismo entorno.
  • A continuación, debe configurar el equilibrio de carga/solicitud entre estos hosts. Puedes hacerlo tanto en TCP (HAProxy), HTTP (nginx) o DNS.
  • El siguiente paso, mencionó Gearheart, es hacer que los archivos estáticos, el caché y las sesiones de aplicaciones web estén disponibles en cada host. Para las sesiones, puede usar un servidor que funcione a través de la red (por ejemplo, Memcached). Como servidor de caché, tiene sentido usar el mismo Memcached, pero en un host diferente, por supuesto.
  • Los archivos estáticos se pueden montar desde algún almacenamiento de archivos compartidos a través de NFS/CIFS o utilizando FS distribuido (HDFS, GlusterFS, Ceph).

También es posible almacenar archivos en una base de datos (por ejemplo, Mongo GridFS), resolviendo así el problema de disponibilidad y escalabilidad (teniendo en cuenta que para la base de datos NoSQL el problema de escalabilidad se resuelve mediante sharding).

Por separado, vale la pena señalar el problema de la implementación en múltiples hosts. ¿Cómo asegurarse de que el usuario al hacer clic en "Actualizar" no vea diferentes versiones de la aplicación? La solución más simple, en mi opinión, sería excluir del balanceador de carga de configuración (servidor web) los hosts que no están actualizados y encenderlos secuencialmente a medida que se realizan las actualizaciones. También puede vincular a los usuarios a hosts específicos mediante cookies o IP. Si la actualización requiere cambios significativos en la base de datos, la forma más fácil es cerrar el proyecto temporalmente.