Históricamente, la industria de TI se ha dividido en dos campos condicionales: los que están "a favor" y los que están "en contra". Y el tema de disputas puede ser absolutamente arbitrario. ¿Cuál es el mejor sistema operativo: Win o Linux? ¿Smartphone Android o iOS? ¿Almacenar todo en la nube o subirlo a fríos almacenamientos RAID y meter los discos en la caja fuerte? ¿Tienen los desarrolladores de PHP derecho a llamarse programadores? Estas disputas son, a veces, de naturaleza exclusivamente existencial y no tienen ninguna base tangible, más allá del interés deportivo.
Así resultó que con la aparición de los contenedores y toda esta querida cocina con Docker y el condicional k8s, comenzaron también las disputas "a favor" y "en contra" del uso de nuevas capacidades en varias áreas del backend. (Aclaramos de antemano que aunque ese razonamiento suele referirse a Kubernetes como orquestador, la elección de esta herramienta en particular no tiene importancia. Puedes sustituirla por cualquier otra que te parezca más conveniente y familiar.)
Y, a primera vista, podría parecer un simple debate entre dos lados de una misma moneda. Tan sin sentido e implacable como la eterna confrontación Win vs Linux, donde personas razonables se encuentran en algún punto intermedio. Pero en el caso de la contenedorización, no todo es tan simple. Normalmente, en tales disputas no hay un lado correcto, pero en el caso de "utilizar" o "no utilizar" contenedores para almacenar bases de datos, todo se complica. Porque en cierto sentido, tanto los defensores como los detractores de este enfoque tienen razón.
Lado Luminoso
Se puede resumir la argumentación del Lado Luminoso en una frase: “¡Hola, 2k19 afuera!” Suena a populismo, ciertamente, pero si se analiza la situación en detalle, hay sus ventajas. Eso es lo que vamos a desglosar ahora.
Supongamos que tienes un gran proyecto web. Podría haberse construido inicialmente con un enfoque de microservicios, o quizás llegó a ello de manera evolutiva en algún momento—esto no es muy importante, en realidad. Has distribuido nuestro proyecto en microservicios individuales, configurado la orquestación, la equilibración de carga y la escalabilidad. Y ahora, con la conciencia tranquila, disfrutas de un mojito en la hamaca durante los efectos de Habr, en vez de levantar servidores caídos. Pero en todas las acciones debes ser consistente. Muy a menudo, solo se contenedoriza directamente la aplicación en sí: el código. ¿Y qué más tenemos además del código?
Correcto, los datos. El corazón de cualquier proyecto son sus datos: puede ser una base de datos típica como MySQL, Postgre, MongoDB, así como almacenes utilizados para búsqueda (ElasticSearch), almacenes key-value para caché—por ejemplo, redis, etc. Ahora no vamos a hablar de implementaciones defectuosas del backend, donde la base de datos falla debido a consultas mal escritas, sino que hablaremos sobre cómo asegurar la tolerancia a fallos de esa base de datos ante la carga del cliente. Es que cuando contenedorizamos nuestra aplicación y le permitimos escalar libremente para manejar cualquier cantidad de solicitudes entrantes, esto naturalmente aumenta también la carga en la base de datos.
De hecho, el canal de acceso a la base de datos y el servidor donde esta se ejecuta se convierten en un cuello de botella en nuestro maravilloso backend contenedorizado. Con esto, el principal objetivo de la virtualización de contenedores—ser una estructura flexible y dinámica—permite organizar la distribución de la carga máxima a través de toda la infraestructura que tenemos disponible de manera más eficiente. Es decir, si no contenedorizamos y no distribuimos por clúster todos los elementos del sistema que tenemos, estamos cometiendo un error muy grave.
Es mucho más lógico agrupar no solo la aplicación en sí, sino también los servicios responsables del almacenamiento de datos. Al agrupar y desplegar servidores web independientes y distribuidos entre sí en k8s, ya resolvemos el problema de la sincronización de datos, como por ejemplo los comentarios en publicaciones, si tomamos como ejemplo un medio de comunicación o una plataforma de blogs. En cualquier caso, se establece una representación interna de la base de datos, aunque sea virtual, como un ExternalService. La cuestión es que la propia base de datos aún no está agrupada; los servidores web desplegados en el clúster obtienen información sobre cambios desde nuestra base de datos de producción estática, que opera de forma separada.
¿Sientes un truco? Usamos k8s o Swarm para distribuir la carga y evitar la caída de lo principal, de un servidor web, pero no hacemos esto para la base de datos. Pero si la base de datos cae, no tiene sentido en nuestra infraestructura agrupada; ¿de qué sirve tener páginas web vacías que devuelven un error de acceso a la base de datos?
Por eso es importante agrupar no solo los servidores web, como se suele hacer, sino también la infraestructura de la base de datos. Solo así podemos garantizar una estructura que funcione plenamente en una sola unidad, pero que sea independiente entre sí. De este modo, incluso si 'colapsa' la mitad de nuestro backend bajo carga, el resto sobrevivirá, y el sistema de sincronización de bases de datos dentro del clúster, junto con la posibilidad de escalado infinito y despliegue de nuevos clústeres, ayudará a alcanzar rápidamente la capacidad necesaria, siempre que haya bastidores en el centro de datos.
Además, el modelo de base de datos distribuido en clústeres permite llevar dicha base de datos a donde se necesite; si hablamos de un servicio global, no tiene mucho sentido tener un clúster web en algún lugar de San Francisco y enviar paquetes para acceder a la base de datos en la región de Moscú y de vuelta.
La contenedorización de la base de datos también permite construir todos los elementos del sistema en el mismo nivel de abstracción. Lo que, a su vez, hace posible gestionar este sistema directamente desde el código, por parte de los desarrolladores, sin necesidad de involucrar activamente a los administradores. A los desarrolladores se les ocurrió que necesitaban una base de datos separada para un nuevo subproyecto: ¡fácil! Escribieron un archivo yaml, lo cargaron en el clúster y listo.
Y por supuesto, la operación interna se simplifica enormemente. Diga, ¿cuántas veces ha cerrado los ojos en momentos en que un nuevo miembro del equipo se ha introducido en la base de datos en producción? Esa que, de hecho, es la única que está en uso en este momento. Claro, todos somos adultos aquí, y seguramente tenemos un respaldo reciente, y más allá, detrás de los pepinos de la abuela y los esquís viejos, hay otro respaldo, posiblemente incluso en almacenamiento frío, porque una vez su oficina se incendió. Pero aun así, cada nueva incorporación al equipo que tiene acceso a la infraestructura de producción y, por supuesto, a la base de datos en vivo, es un verdadero dolor de estómago para todos. ¿Quién sabe cómo es, el principiante? Da miedo, ¿no cree?
La contenedorización y, de hecho, la topología física distribuida de la base de datos de su proyecto ayudan a evitar momentos de ansiedad como esos. ¿No confía en el nuevo? ¡Está bien! Le levantaremos un clúster propio para trabajar y lo desconectaremos de los demás clústeres de base de datos: la sincronización será solo por un push manual y la rotación simultánea de dos llaves (una para el líder de equipo y otra para el administrador). Y todos estarán felices.
Y ahora es momento de cambiar de opinión sobre la clústerización de bases de datos.
El Lado Oscuro
Al reflexionar por qué no se debe contenedizar una base de datos y continuar operándola en una central única, servidor, no vamos a caer en la retórica de los ortodoxos y afirmaciones como "nuestros ancestros operaban bases de datos en hardware, ¡y nosotros también lo haremos!" En lugar de eso, intentemos crear una situación en la que la contenedorización realmente traiga beneficios tangibles.
Aceptémoslo, los proyectos que realmente necesitan una base de datos en un contenedor se pueden contar con los dedos de una mano de un fresador no muy competente. En su mayoría, incluso el uso de k8s o Docker Swarm puede ser excesivo; a menudo se utilizan estas herramientas debido a la moda general de vender la tecnología y las directrices de los superiores para llevar todo a la nube y los contenedores. Bueno, porque ahora está de moda y todos lo están haciendo.
En al menos la mitad de los casos, usar Kubernetes o simplemente Docker en un proyecto es redundante. La cuestión es que no todos los equipos o las empresas de outsourcing contratadas para gestionar la infraestructura del cliente se dan cuenta de esto. Lo peor es cuando se imponen contenedores, ya que esto representa un cierto coste para el cliente.
En general, existe la opinión de que Docker/Kube-mafia simplemente acapara a los clientes que externalizan estas cuestiones de infraestructura. Para trabajar con clústeres, se necesitan ingenieros que sean capaces de hacerlo y que comprendan la arquitectura de la solución implementada. Ya hemos descrito nuestro caso con la publicación de Republic: allí entrenamos al equipo del cliente para trabajar en el contexto de Kubernetes, y todos quedaron satisfechos. Y fue bastante razonable. Sin embargo, a menudo los 'implementadores' de k8s toman la infraestructura del cliente como rehén: ahora solo ellos entienden cómo funciona todo, mientras que en el lado del cliente no hay especialistas.
Ahora imagina que de esta manera no solo estamos delegando la parte del servidor web al outsourcing, sino también el mantenimiento de la base de datos. Decimos que la base de datos es el corazón, y perder el corazón es fatal para cualquier organismo vivo. En resumen, las perspectivas no son las mejores. Así que, en lugar del 'hype' de Kubernetes, muchos proyectos deberían simplemente no ser tacaños y elegir un plan decente en AWS, que resolverá todos los problemas de carga en su sitio/proyecto. Pero AWS ya no está de moda, y las apariencias son más caras que el dinero; lamentablemente, también en el entorno de TI.
Está bien. Quizás la agrupación sea realmente necesaria para el proyecto, pero si con las aplicaciones sin estado todo está claro, ¿cómo entonces organizar un suministro adecuado de conectividad en una base de datos clústerizada?
Si hablamos de una solución de ingeniería sin costuras, que es lo que representa la transición a k8s, nuestra principal preocupación es la replicación de datos en una base de datos clústerizada. Algunas bases de datos son bastante permisivas respecto a la distribución de datos entre sus diferentes instancias. Sin embargo, muchas otras no son tan amables. A menudo, el argumento principal para elegir una base de datos para nuestro proyecto no radica en su capacidad de replicarse con el mínimo de recursos y costos de ingeniería. Especialmente si el proyecto no se planificó desde el principio como microservicio, sino que simplemente evolucionó en esa dirección.
No es necesario hablar de la velocidad de los discos de red: son lentos. Es decir, no tenemos la posibilidad real de, en caso necesario, levantar una instancia de base de datos en un lugar donde haya, por ejemplo, más potencia de procesamiento o más memoria RAM disponible. Rápidamente nos toparemos con el rendimiento del subsistema de disco virtualizado. Por lo tanto, la base de datos debe estar atada a su propio conjunto personal de máquinas que se encuentren en estrecha proximidad. O bien, se debe encontrar una manera de garantizar una sincronización de datos lo suficientemente rápida hacia las reservas previstas.
Continuando con el tema de los sistemas de archivos virtuales: los volúmenes de Docker, desafortunadamente, no están exentos de problemas. En general, en cuestiones de almacenamiento de datos a largo plazo y de manera confiable, preferiríamos ceñirnos a esquemas técnicos lo más simples posible. Añadir un nuevo nivel de abstracción desde el sistema de archivos del contenedor al sistema de archivos del host parental ya es en sí mismo un riesgo. Pero cuando, además, surgen dificultades en el funcionamiento del sistema de contenedores para transmitir datos entre estas capas, la situación empeora aún más. Actualmente, la mayoría de los problemas conocidos que incomodan a la humanidad parecen haber sido erradicados. Pero ya comprendemos que cuanto más complejo es el mecanismo, más fácil es que se rompa.
A la luz de todas estas "aventuras", es mucho más rentable y sencillo mantener la base de datos en un solo lugar, y aunque necesites la containerización de la aplicación, que funcione por sí sola y reciba una conexión simultánea a la base de datos a través de un gateway, que se leerá y escribirá una sola vez y en un solo lugar. Este enfoque reduce la probabilidad de errores y desincronizaciones al mínimo.
¿A dónde queremos llegar? A que la containerización de base de datos es pertinente donde existe una necesidad real. No se puede meter la base de datos de una aplicación completa y hacerla funcionar como si tuvieras una veintena de microservicios; eso no funciona. Y esto necesita estar claro.
En lugar de la salida
Si esperas una conclusión clara sobre si "virtualizar o no la base de datos", te decepcionaremos: no la habrá aquí. Porque al crear cualquier solución de infraestructura, hay que guiarse no por la moda y el progreso, sino, sobre todo, por el sentido común.
Existen proyectos para los cuales los principios y herramientas que vienen con Kubernetes encajan perfectamente, y en tales proyectos se establece la paz, al menos en el ámbito del backend. Y hay proyectos que no necesitan containerización, sino una infraestructura de servidor adecuada, porque no pueden escalar bajo un modelo de clúster de microservicios; de lo contrario, colapsarán.
Fuente: habr.com
