Cartucho de Tarantool: fragmentación de un backend de Lua en tres líneas

Cartucho de Tarantool: fragmentación de un backend de Lua en tres líneas

En Mail.ru Group tenemos Tarantool, un servidor de aplicaciones en Lua que también funciona como base de datos (¿o viceversa?). Es rápido y genial, pero las capacidades de un servidor aún no son ilimitadas. El escalado vertical tampoco es una panacea, por lo que Tarantool tiene herramientas para el escalado horizontal: el módulo vshard [ 1 ]. Le permite fragmentar datos en varios servidores, pero debe modificarlos para configurarlos y adjuntar la lógica empresarial.

Buenas noticias: hemos recopilado algunos peces gordos (p. ej. [ 2 ], [ 3 ]) y creó otro marco que simplificará significativamente la solución a este problema.

Cartucho Tarantool es un nuevo marco para desarrollar sistemas distribuidos complejos. Le permite concentrarse en escribir lógica empresarial en lugar de resolver problemas de infraestructura. Debajo del corte, le diré cómo funciona este marco y cómo escribir servicios distribuidos usándolo.

¿Y cuál es, de hecho, el problema?

Tenemos una tarántula, tenemos vshard, ¿qué más se puede pedir?

En primer lugar, es una cuestión de conveniencia. La configuración de vshard se configura a través de tablas Lua. Para que un sistema distribuido de múltiples procesos Tarantool funcione correctamente, la configuración debe ser la misma en todas partes. Nadie quiere hacer esto manualmente. Por tanto, se utilizan todo tipo de scripts, Ansible y sistemas de implementación.

El propio cartucho gestiona la configuración vshard, lo hace en función de su propia configuración distribuida. Es esencialmente un archivo YAML simple, una copia del cual se almacena en cada instancia de Tarantool. La simplificación es que el propio marco monitorea su configuración y garantiza que sea la misma en todas partes.

En segundo lugar, se trata nuevamente de una cuestión de conveniencia. La configuración vshard no tiene nada que ver con el desarrollo de la lógica empresarial y sólo distrae al programador de su trabajo. Cuando hablamos de la arquitectura de un proyecto, con mayor frecuencia hablamos de componentes individuales y su interacción. Es demasiado pronto para pensar en implementar un clúster en tres centros de datos.

Resolvimos estos problemas una y otra vez, y en algún momento logramos desarrollar un enfoque que simplificó el trabajo con la aplicación durante todo su ciclo de vida: creación, desarrollo, pruebas, CI/CD, mantenimiento.

Cartucho introduce el concepto de un rol para cada proceso de Tarantool. Los roles son un concepto que permite a un desarrollador centrarse en escribir código. Todos los roles disponibles en el proyecto se pueden ejecutar en una instancia de Tarantool, y esto será suficiente para las pruebas.

Características clave del cartucho Tarantool:

  • orquestación de clústeres automatizada;
  • ampliar la funcionalidad de la aplicación utilizando nuevos roles;
  • plantilla de aplicación para desarrollo e implementación;
  • fragmentación automática incorporada;
  • integración con el marco de pruebas Luatest;
  • gestión de clústeres mediante WebUI y API;
  • herramientas de empaquetado y despliegue.

¡Hola Mundo!

No puedo esperar para mostrar el marco en sí, así que dejaremos la historia sobre la arquitectura para más adelante y comenzaremos con algo simple. Si asumimos que Tarantool ya está instalado, entonces todo lo que queda es hacer

$ tarantoolctl rocks install cartridge-cli
$ export PATH=$PWD/.rocks/bin/:$PATH

Estos dos comandos instalarán las utilidades de la línea de comandos y le permitirán crear su primera aplicación a partir de la plantilla:

$ cartridge create --name myapp

Y esto es lo que obtenemos:

myapp/
├── .git/
├── .gitignore
├── app/roles/custom.lua
├── deps.sh
├── init.lua
├── myapp-scm-1.rockspec
├── test
│   ├── helper
│   │   ├── integration.lua
│   │   └── unit.lua
│   ├── helper.lua
│   ├── integration/api_test.lua
│   └── unit/sample_test.lua
└── tmp/

Este es un repositorio de git con un mensaje "¡Hola, mundo!" solicitud. Intentemos ejecutarlo de inmediato, habiendo instalado previamente las dependencias (incluido el marco en sí):

$ tarantoolctl rocks make
$ ./init.lua --http-port 8080

Entonces, tenemos un nodo ejecutándose para la futura aplicación fragmentada. Un profano curioso puede abrir inmediatamente la interfaz web, configurar un grupo de un nodo con el mouse y disfrutar del resultado, pero es demasiado pronto para alegrarse. Hasta ahora, la aplicación no puede hacer nada útil, así que les hablaré sobre la implementación más adelante, pero ahora es el momento de escribir código.

Desarrollo de aplicaciones

Imagínense, estamos diseñando un proyecto que debe recibir datos, guardarlos y generar un informe una vez al día.

Cartucho de Tarantool: fragmentación de un backend de Lua en tres líneas

Comenzamos a dibujar un diagrama y colocamos en él tres componentes: puerta de enlace, almacenamiento y programador. Seguimos trabajando en la arquitectura. Como usamos vshard como almacenamiento, agregamos vshard-router y vshard-storage al esquema. Ni la puerta de enlace ni el programador accederán directamente al almacenamiento; para eso está el enrutador, para eso fue creado.

Cartucho de Tarantool: fragmentación de un backend de Lua en tres líneas

Este diagrama todavía no representa exactamente lo que construiremos en el proyecto porque los componentes parecen abstractos. Todavía tenemos que ver cómo se proyectará esto en el Tarantool real: agrupemos nuestros componentes por proceso.

Cartucho de Tarantool: fragmentación de un backend de Lua en tres líneas

No tiene mucho sentido mantener vshard-router y gateway en instancias separadas. ¿Por qué necesitamos volver a navegar por la red si esto ya es responsabilidad del router? Deben ejecutarse dentro del mismo proceso. Es decir, tanto la puerta de enlace como vshard.router.cfg se inicializan en un proceso y se les permite interactuar localmente.

En la etapa de diseño, era conveniente trabajar con tres componentes, pero yo, como desarrollador, mientras escribo el código, no quiero pensar en lanzar tres instancias de Tarnatool. Necesito ejecutar pruebas y comprobar que escribí la puerta de enlace correctamente. O tal vez quiera mostrarles una característica a mis colegas. ¿Por qué debería tomarme la molestia de implementar tres copias? Así nació el concepto de roles. Un rol es un módulo luash normal cuyo ciclo de vida lo gestiona Cartucho. En este ejemplo hay cuatro: puerta de enlace, enrutador, almacenamiento y programador. Puede que haya más en otro proyecto. Todos los roles se pueden ejecutar en un proceso y esto será suficiente.

Cartucho de Tarantool: fragmentación de un backend de Lua en tres líneas

Y cuando se trata de implementación en puesta en escena o producción, asignaremos a cada proceso de Tarantool su propio conjunto de roles según las capacidades del hardware:

Cartucho de Tarantool: fragmentación de un backend de Lua en tres líneas

Gestión de topología

La información sobre dónde se ejecutan los roles debe almacenarse en algún lugar. Y este “en algún lugar” es la configuración distribuida, que ya mencioné anteriormente. Lo más importante es la topología del clúster. Aquí hay 3 grupos de replicación de 5 procesos de Tarantool:

Cartucho de Tarantool: fragmentación de un backend de Lua en tres líneas

No queremos perder datos, por eso tratamos la información sobre los procesos en ejecución con cuidado. Cartucho realiza un seguimiento de la configuración mediante una confirmación de dos fases. Una vez que queremos actualizar la configuración, primero verifica que todas las instancias estén disponibles y listas para aceptar la nueva configuración. Después de esto, la segunda fase aplica la configuración. Por lo tanto, incluso si una copia no está disponible temporalmente, no sucederá nada malo. La configuración simplemente no se aplicará y verás un error por adelantado.

También en la sección de topología se indica un parámetro tan importante como el líder de cada grupo de replicación. Generalmente esta es la copia que se está grabando. El resto suele ser de sólo lectura, aunque puede haber excepciones. A veces, los desarrolladores valientes no temen los conflictos y pueden escribir datos en varias réplicas en paralelo, pero hay algunas operaciones que, pase lo que pase, no deben realizarse dos veces. Para ello existe la señal de un líder.

Cartucho de Tarantool: fragmentación de un backend de Lua en tres líneas

vida de roles

Para que exista un rol abstracto en dicha arquitectura, el marco debe gestionarlo de alguna manera. Naturalmente, el control se produce sin reiniciar el proceso Tarantool. Hay 4 devoluciones de llamada para gestionar roles. El propio cartucho los llamará según lo que esté escrito en su configuración distribuida, aplicando así la configuración a roles específicos.

function init()
function validate_config()
function apply_config()
function stop()

Cada rol tiene una función. init. Se llama una vez cuando la función está habilitada o cuando se reinicia Tarantool. Allí es conveniente, por ejemplo, inicializar box.space.create, o el programador puede iniciar alguna fibra en segundo plano que realizará el trabajo en ciertos intervalos de tiempo.

Una función init Puede que no sea suficiente. Cartucho permite que los roles aprovechen la configuración distribuida que utiliza para almacenar la topología. Podemos declarar una nueva sección en la misma configuración y almacenar en ella un fragmento de la configuración empresarial. En mi ejemplo, esto podría ser un esquema de datos o una configuración de programación para la función del programador.

Llamadas de grupo validate_config и apply_config cada vez que cambia la configuración distribuida. Cuando se aplica una configuración mediante una confirmación de dos fases, el clúster verifica que cada rol esté listo para aceptar esta nueva configuración y, si es necesario, informa un error al usuario. Cuando todos están de acuerdo en que la configuración es normal, entonces el apply_config.

También los roles tienen un método. stop, que es necesario para limpiar la salida del rol. Si decimos que el programador ya no es necesario en este servidor, puede detener las fibras con las que comenzó. init.

Los roles pueden interactuar entre sí. Estamos acostumbrados a escribir llamadas a funciones en Lua, pero puede suceder que un proceso determinado no tenga el rol que necesitamos. Para facilitar las llamadas a través de la red, utilizamos el módulo auxiliar rpc (llamada a procedimiento remoto), que se basa en el netbox estándar integrado en Tarantool. Esto puede resultar útil si, por ejemplo, su puerta de enlace quiere pedirle directamente al programador que haga el trabajo ahora mismo, en lugar de esperar un día.

Otro punto importante es garantizar la tolerancia a fallos. Cartucho utiliza el protocolo SWIM para controlar la salud [ 4 ]. En resumen, los procesos intercambian “rumores” entre sí a través de UDP: cada proceso informa a sus vecinos las últimas noticias y estos responden. Si de repente no llega la respuesta, Tarantool comienza a sospechar que algo anda mal, y al cabo de un rato recita la muerte y comienza a contar esta noticia a todos los que la rodean.

Cartucho de Tarantool: fragmentación de un backend de Lua en tres líneas

Basado en este protocolo, Cartucho organiza el procesamiento automático de fallas. Cada proceso monitorea su entorno y, si el líder deja de responder repentinamente, la réplica puede asumir su función y Cartucho configura las funciones en ejecución en consecuencia.

Cartucho de Tarantool: fragmentación de un backend de Lua en tres líneas

Debe tener cuidado aquí, porque los cambios frecuentes de un lado a otro pueden provocar conflictos de datos durante la replicación. Por supuesto, no deberías habilitar la conmutación por error automática al azar. Debemos entender claramente lo que está sucediendo y estar seguros de que la replicación no se romperá después de que el líder sea restaurado y se le devuelva la corona.

De todo esto, puede tener la sensación de que los roles son similares a los microservicios. En cierto sentido, son sólo eso, sólo como módulos dentro de los procesos de Tarantool. Pero también hay una serie de diferencias fundamentales. Primero, todos los roles del proyecto deben residir en la misma base de código. Y todos los procesos de Tarantool deben iniciarse desde la misma base de código, para que no haya sorpresas como esas cuando intentamos inicializar el programador, pero simplemente no existe. Además, no se deben permitir diferencias en las versiones del código, porque el comportamiento del sistema en tal situación es muy difícil de predecir y depurar.

A diferencia de Docker, no podemos simplemente tomar una función de "imagen", llevarla a otra máquina y ejecutarla allí. Nuestros roles no están tan aislados como los contenedores Docker. Además, no podemos ejecutar dos roles idénticos en una instancia. Un rol existe o no; en cierto sentido, es un singleton. Y en tercer lugar, los roles deben ser los mismos dentro de todo el grupo de replicación, porque de lo contrario sería absurdo: los datos son los mismos, pero la configuración es diferente.

Herramientas de implementación

Prometí mostrar cómo Cartucho ayuda a implementar aplicaciones. Para hacer la vida más fácil a los demás, el marco incluye paquetes RPM:

$ cartridge pack rpm myapp -- упакует для нас ./myapp-0.1.0-1.rpm
$ sudo yum install ./myapp-0.1.0-1.rpm

El paquete instalado contiene casi todo lo que necesitas: tanto la aplicación como las dependencias instaladas. Tarantool también llegará al servidor como una dependencia del paquete RPM y nuestro servicio estará listo para iniciarse. Esto se hace a través de systemd, pero primero debes escribir una pequeña configuración. Como mínimo, especifique el URI de cada proceso. Tres son suficientes, por ejemplo.

$ sudo tee /etc/tarantool/conf.d/demo.yml <<CONFIG
myapp.router: {"advertise_uri": "localhost:3301", "http_port": 8080}
myapp.storage_A: {"advertise_uri": "localhost:3302", "http_enabled": False}
myapp.storage_B: {"advertise_uri": "localhost:3303", "http_enabled": False}
CONFIG

Hay un matiz interesante aquí. En lugar de especificar solo el puerto del protocolo binario, especificamos la dirección pública completa del proceso, incluido el nombre de host. Esto es necesario para que los nodos del clúster sepan cómo conectarse entre sí. Es una mala idea utilizar 0.0.0.0 como dirección de publicidad_uri; debería ser una dirección IP externa, no un enlace de socket. Sin él, nada funcionará, por lo que Cartucho simplemente no le permitirá iniciar un nodo con el anuncio_uri incorrecto.

Ahora que la configuración está lista, puedes iniciar los procesos. Dado que una unidad systemd normal no permite que se inicie más de un proceso, las aplicaciones en el Cartucho se instalan mediante el llamado. unidades instanciadas que funcionan así:

$ sudo systemctl start myapp@router
$ sudo systemctl start myapp@storage_A
$ sudo systemctl start myapp@storage_B

En la configuración, especificamos el puerto HTTP en el que Cartucho sirve la interfaz web: 8080. Vayamos a él y echemos un vistazo:

Cartucho de Tarantool: fragmentación de un backend de Lua en tres líneas

Vemos que aunque los procesos están en ejecución, aún no están configurados. El cartucho aún no sabe quién debe replicarse con quién y no puede tomar una decisión por sí solo, por lo que está esperando nuestras acciones. Pero no tenemos muchas opciones: la vida de un nuevo clúster comienza con la configuración del primer nodo. Luego agregaremos los demás al clúster, les asignaremos roles y, en este punto, la implementación se puede considerar completada con éxito.

Serviremos un vaso de su bebida favorita y relajaremos después de una larga semana de trabajo. La aplicación se puede utilizar.

Cartucho de Tarantool: fragmentación de un backend de Lua en tres líneas

resultados

¿Cuáles son los resultados? Pruébelo, úselo, deje comentarios, cree tickets en Github.

referencias

[ 1 ] Tarantool » 2.2 » Referencia » Referencia de rocas » Módulo vshard

[ 2 ] Cómo implementamos el núcleo del negocio de inversión de Alfa-Bank basado en Tarantool

[ 3 ] Arquitectura de facturación de nueva generación: transformación con la transición a Tarantool

[ 4 ] SWIM - protocolo de construcción de clusters

[ 5 ] GitHub - tarantool/cartucho-cli

[ 6 ] GitHub - tarantool/cartucho

Fuente: habr.com

Añadir un comentario