Cómo Dark despliega código en 50 ms

Cómo Dark despliega código en 50 ms

Cuanto más rápido es el proceso de desarrollo, más rápido se desarrolla la empresa tecnológica.

Desafortunadamente, las aplicaciones modernas trabajan en nuestra contra: nuestros sistemas deben actualizarse en tiempo real sin interrumpir a nadie ni provocar tiempos de inactividad. Implementar en estos sistemas se convierte en una tarea complicada y requiere pipelines de entrega continua incluso en equipos pequeños.

Estos pipelines suelen tener un uso limitado, funcionan lentamente y no son fiables. Los desarrolladores deben crearlos manualmente primero y luego gestionarlos, y las empresas a menudo contratan equipos enteros de DevOps para esto.

La velocidad de estos pipelines afecta la velocidad de desarrollo. En los mejores equipos, la implementación toma de 5 a 10 minutos, pero normalmente todo tarda mucho más, y una sola implementación puede requerir varias horas.

En Dark, esto toma 50 ms. Cincuenta. Milisegundos.. Dark — es una solución integral con un lenguaje de programación, editor e infraestructura, creado específicamente para entrega continua, y todos los aspectos de Dark, incluido el propio lenguaje, están diseñados para un despliegue instantáneo y seguro.

¿Por qué los pipelines de entrega continua son tan lentos?

Supongamos que tenemos una aplicación web en Python y ya hemos creado un grandioso y moderno pipeline de entrega continua. Para un desarrollador que trabaja en este proyecto todos los días, implementar un pequeño cambio se verá aproximadamente así:

Realizando cambios.

  • Creando una nueva rama en git.
  • Realizando cambios tras el interruptor de funciones.
  • Pruebas unitarias para verificar los cambios con y sin el interruptor de funciones.

Pull request.

  • Confirmar cambios.
  • Enviar cambios al repositorio remoto en GitHub.
  • Pull request.
  • La construcción de CI se ejecuta automáticamente en segundo plano.
  • Revisión del código.
  • Algunas revisiones más, si es necesario.
  • Fusionando cambios con el maestro de git.

CI se ejecuta en el maestro.

  • Instalando dependencias del frontend a través de npm.
  • Construyendo y optimizando recursos HTML+CSS+JS.
  • Ejecutando pruebas unitarias y funcionales en el frontend.
  • Instalando dependencias de Python desde PyPI.
  • Ejecutando pruebas unitarias y funcionales en el backend.
  • Pruebas de integración en ambos extremos.
  • Enviando recursos del frontend al CDN.
  • Construyendo el contenedor para la aplicación Python.
  • Envío del contenedor al registro
  • Actualización del manifiesto de Kubernetes

Reemplazo del código antiguo por el nuevo

  • Kubernetes inicia varias instancias del nuevo contenedor
  • Kubernetes espera a que las instancias estén operativas
  • Kubernetes añade instancias al equilibrador de carga HTTP
  • Kubernetes espera a que las antiguas instancias dejen de ser utilizadas
  • Kubernetes detiene las antiguas instancias
  • Kubernetes repite estas operaciones hasta que todas las nuevas instancias reemplacen a las antiguas

Activación de un nuevo interruptor de funciones

  • El nuevo código se activa solo para sí mismo, asegurándose de que todo esté correcto
  • El nuevo código se activa para el 10% de los usuarios, se supervisan métricas operativas y comerciales
  • El nuevo código se activa para el 50% de los usuarios, se supervisan métricas operativas y comerciales
  • El nuevo código se activa para el 100% de los usuarios, se supervisan métricas operativas y comerciales
  • Finalmente, repites todo el proceso para eliminar el código antiguo y el interruptor

El proceso depende de las herramientas, el lenguaje y el uso de arquitecturas orientadas a servicios, pero en general se ve así. No mencioné los despliegues con migración de bases de datos, porque eso requiere una planificación cuidadosa, pero a continuación, explicaré cómo lo maneja Dark.

Aquí hay muchos componentes y muchos de ellos pueden fácilmente causar retrasos, fallos, competencia temporal o colapsar el sistema en funcionamiento.

Y dado que estos pipelines casi siempre están diseñados para un caso particular, es difícil confiar en ellos. Muchos tienen días en los que no se puede desplegar el código porque hay problemas en el Dockerfile, uno de los decenas de servicios ha fallado o el especialista necesario está de vacaciones.

Peor aún, muchos de estos pasos no hacen nada útil en absoluto. Fueron necesarios en el pasado, cuando desplegábamos código directamente para los usuarios, pero ahora tenemos interruptores para el nuevo código y estos procesos se han separado. Como resultado, el paso en el que se despliega el código (el antiguo es reemplazado por el nuevo) se ha convertido en un riesgo innecesario.

Por supuesto, este es un pipeline muy bien pensado. El equipo que lo creó no escatimó tiempo ni dinero en un despliegue rápido. Por lo general, los pipelines de despliegue son mucho más lentos y menos fiables.

Implementación de entrega continua en Dark

La entrega continua es tan importante para Dark que desde el principio nos propusimos un tiempo de menos de un segundo. Revisamos todos los pasos del pipeline para eliminar lo innecesario y perfeccionamos el resto. Así es como eliminamos pasos.

Jessie Frazelle (Jessie Frazelle) creó la nueva palabra deployless (sin despliegue) en la conferencia Future of Software Development en Reikiavik.

Decidimos de inmediato que Dark se basaría en la концепция «deployless» (gracias a Jessie Frazelle por el neologismo). Deployless significa que cualquier código se despliega instantáneamente y está listo para usar en producción. Por supuesto, no dejaremos pasar código defectuoso o incompleto (los principios de seguridad los describiré a continuación).

Durante la demostración de Dark, a menudo nos preguntaban cómo logramos acelerar el despliegue de esa manera. Una pregunta extraña. La gente debe pensar que hemos inventado alguna supertecnología que compara código, lo compila, lo empaqueta en un contenedor, lanza una máquina virtual, inicia un contenedor en frío y todo eso, en 50 ms. Es poco probable que eso sea posible. Pero hemos creado un motor de despliegue especial que no necesita hacer todo eso.

Dark lanza intérpretes en la nube. Supongamos que estás escribiendo código en una función o manejador HTTP o de eventos. Enviamos un diff al árbol de sintaxis abstracta (la implementación del código que utiliza internamente nuestro editor y servidores) a nuestros servidores, y luego ejecutamos ese código cuando llegan las solicitudes. Así que el despliegue parece simplemente una modesta entrada en una base de datos: instantáneo y elemental. El despliegue es tan rápido porque incluye lo mínimo.

En el futuro, planeamos convertir a Dark en un compilador de infraestructura que creará y ejecutará la infraestructura ideal para alto rendimiento y fiabilidad de aplicaciones. El despliegue instantáneo, por supuesto, no irá a ninguna parte.

Despliegue seguro

Editor estructurado

El código en Dark se escribe en el editor de Dark. El editor estructurado no permite errores de sintaxis. De hecho, en Dark no hay ni siquiera un analizador. Mientras introduzcas texto, trabajamos directamente con el árbol de sintaxis abstracta (AST), como Paredit, Sketch-n-Sketch, Tofu, Prune y MPS.

Cualquier código incompleto en Dark tiene una semántica de ejecución válida, similar a typed holes en Hazel. Por ejemplo, si cambias la llamada a una función, guardamos la antigua hasta que la nueva sea adecuada.

Cada programa en Dark tiene su propio propósito, por lo que el código incompleto no interfiere con el funcionamiento del código completo.

Modos de edición

Escribes código en Dark en dos situaciones. Primero: escribes nuevo código y eres el único usuario. Por ejemplo, está en REPL, y otros usuarios nunca tendrán acceso a él, o es una nueva ruta HTTP a la que no te refieres en ninguna parte. Aquí puedes trabajar sin ninguna medida de precaución, y ahora estás trabajando así en el entorno de desarrollo.

La segunda situación: el código ya está en uso. Si hay tráfico pasando a través del código (funciones, manejadores de eventos, bases de datos, etc.), se debe tener cuidado. Para ello, bloqueamos todo el código en uso y requerimos utilizar herramientas más estructuradas para su edición. A continuación hablaré sobre las herramientas estructurales: los interruptores de funciones para manejadores HTTP y eventos, una potente plataforma de migración para bases de datos y un nuevo método de gestión de versiones para funciones y tipos.

Interruptores de funciones

Una de las maneras de eliminar la complejidad innecesaria en Dark es resolver varios problemas con una solución. Los interruptores de funciones realizan muchas tareas diferentes: reemplazo del entorno de desarrollo local, ramas de git, despliegue de código y, por supuesto, el tradicional lanzamiento lento y controlado de nuevo código.

La creación y despliegue de un interruptor de función se realiza en nuestro editor en una sola operación. Crea un espacio vacío para el nuevo código y proporciona controles de acceso al código antiguo y nuevo, así como botones y comandos para cambiar gradualmente al nuevo código o excluirlo.

Los interruptores de funciones están integrados en el lenguaje Dark, y incluso los interruptores incompletos cumplen su función: si la condición en el interruptor no se cumple, se ejecutará el antiguo código bloqueado.

Entorno de desarrollo

Los interruptores de funciones reemplazan el entorno de desarrollo local. Hoy en día, es difícil para los equipos asegurarse de que todos utilicen las mismas versiones de herramientas y bibliotecas (herramientas de formateo de código, linters, gestores de paquetes, compiladores, preprocesadores, herramientas de prueba, etc.). Con Dark, no es necesario instalar dependencias localmente, gestionar una instalación local de Docker o tomar otras medidas para garantizar al menos una semblanza de igualdad entre el entorno de desarrollo y la producción. Dado que tal igualdad sigue siendo imposible, ni siquiera vamos a pretender que estamos tratando de lograrla.

En lugar de crear un entorno local clonado, los interruptores en Dark crean una nueva caja de arena en producción que reemplaza el entorno de desarrollo. En el futuro, también planeamos crear una caja de arena para otras partes de la aplicación (por ejemplo, clones instantáneos de bases de datos), aunque por ahora esto no parece tan importante.

Ramas y despliegues

Actualmente, hay varias formas de introducir nuevo código en los sistemas: ramas de git, etapa de despliegue e interruptores de funciones. Resuelven un mismo problema en diferentes partes del flujo de trabajo: git — en las etapas previas al despliegue, el despliegue — en el momento de la transición del código viejo al nuevo, y los interruptores de funciones — para un lanzamiento controlado del nuevo código.

La forma más efectiva son los interruptores de funciones (además de ser la más fácil de entender y usar). Con ellos se puede prescindir por completo de los otros dos métodos. Es especialmente útil eliminar el despliegue — si ya estamos usando interruptores de funciones para activar el código, el paso de trasladar servidores al nuevo código solo crea riesgos innecesarios.

Git es difícil de usar, especialmente para los principiantes, y eso lo limita enormemente, pero por otro lado tiene ramas convenientes. Hemos suavizado muchas de las desventajas de git. Dark se edita en tiempo real y proporciona la posibilidad de colaboración al estilo de Google Docs, de modo que no es necesario enviar código y se puede realizar el rebase y la fusión con menos frecuencia.

Los interruptores de funciones son la base de un despliegue seguro. Junto con los despliegues instantáneos, permiten probar rápidamente conceptos en pequeños fragmentos con bajo riesgo, en lugar de aplicar un gran cambio que podría colapsar el sistema.

Versionado

Para cambiar funciones y tipos, usamos el versionado. Si deseas cambiar una función, Dark crea una nueva versión de esa función. Luego puedes llamar a esa versión con un interruptor en el manejador HTTP o de eventos. (Si es una función profundamente anidada en el árbol de llamadas, se creará una nueva versión de cada función en el camino. Puede parecer excesivo, pero las funciones no interfieren entre sí si no las utilizas, así que ni siquiera lo notarás.)

Por las mismas razones, versionamos los tipos. Hablamos en detalle sobre nuestro sistema de tipos en la publicación anterior..

Gracias al versionado de funciones y tipos, puedes realizar cambios en la aplicación de forma gradual. Puedes verificar que cada manejador individual trabaja con la nueva versión, sin la necesidad de implementar todos los cambios en la aplicación de inmediato (aunque tenemos herramientas para hacerlo rápidamente si lo deseas).

Esto es mucho más seguro que el despliegue completo de todo a la vez, como se hace actualmente.

Nuevas versiones de paquetes y la biblioteca estándar

Cuando actualizas un paquete en Dark, no reemplazamos inmediatamente el uso de cada función o tipo en toda la base de código. Eso no es seguro. El código sigue utilizando la misma versión que estaba usando, mientras que actualizas el uso de funciones y tipos a la nueva versión para cada caso individual mediante interruptores.

Cómo Dark despliega código en 50 ms
Captura de pantalla de parte del proceso automático en Dark, mostrando dos versiones de la función Dict::get. Dict::get_v0 devolvía el tipo Any (del que estamos renunciando), mientras que Dict::get_v1 devuelve el tipo Option.

A menudo proporcionamos una nueva función en la biblioteca estándar y eliminamos las versiones antiguas. Los usuarios con versiones antiguas en su código mantendrán acceso a ellas, pero los nuevos usuarios no podrán obtenerlas. Planeamos proporcionar herramientas para migrar a los usuarios de versiones antiguas a nuevas en un solo paso, nuevamente utilizando interruptores de funciones.

Dark también ofrece una oportunidad única: dado que ejecutamos tu código de trabajo, podemos probar nuevas versiones comparando la salida de las nuevas y viejas solicitudes para informarte sobre los cambios. Como resultado, la actualización de paquetes, que a menudo se realiza a ciegas (o requiere pruebas exhaustivas por motivos de seguridad), representa mucho menos riesgo y puede ocurrir automáticamente.

Nuevas versiones de Dark

La transición de Python 2 a Python 3 se extendió por una década y sigue siendo un problema. Dado que estamos creando Dark para la entrega continua, debemos tener en cuenta estos cambios en el lenguaje.

Cuando hacemos pequeños cambios en el lenguaje, creamos una nueva versión de Dark. El código antiguo permanece en la versión anterior de Dark, y el código nuevo se utiliza en la nueva versión. Se pueden utilizar interruptores o versiones de funciones para actualizar a la nueva versión de Dark.

Esto es especialmente útil, considerando que Dark ha surgido recientemente. Muchos cambios en el lenguaje o en la biblioteca pueden resultar desafortunados. La versionado gradual del lenguaje nos permite realizar actualizaciones menores, lo que significa que podemos tomarnos nuestro tiempo y posponer muchas decisiones sobre el lenguaje hasta que tengamos más usuarios, lo que significa más información.

Migraciones de bases de datos

Para migrar bases de datos de manera segura, existe una fórmula estándar:

  • Reescribir el código para soportar los nuevos y antiguos formatos
  • Transformar todos los datos al nuevo formato
  • Eliminar el acceso antiguo a los datos

Como resultado, la migración de bases de datos se alarga y requiere muchos recursos. Y acumulamos esquemas obsoletos, ya que incluso tareas simples, como corregir el nombre de una tabla o columna, no justifican el esfuerzo.

Dark tiene una plataforma de migración de bases de datos eficaz que (esperamos) simplificará el proceso tanto que dejarás de tenerle miedo. Todos los almacenes de datos en Dark (almacenes de pares de "clave-valor" o tablas hash persistentes) tienen un tipo. Para transferir un almacén de datos, simplemente le asignas un nuevo tipo y una función de reversión y avance para transformar los valores entre los dos tipos.

El acceso a los almacenes de datos en Dark se realiza a través de nombres de variables versionados. Por ejemplo, el almacén de datos Users inicialmente se llamará Users-v0. Cuando se crea una nueva versión con un tipo diferente, el nombre cambia a Users-v1. Si los datos se guardan a través de Users-v0 y se accede a ellos mediante Users-v1, se aplica la función de retroceso. Si los datos se guardan a través de Users-v1 y se accede a ellos mediante Users-v0, se aplica la función de restauración.

Cómo Dark despliega código en 50 ms
Pantalla de migración de base de datos con los nombres de los campos de la antigua base de datos, expresiones de retroceso y restauración e instrucciones para habilitar la migración.

Utilice los interruptores de funciones para dirigir las llamadas a Users-v0 en la versión Users-v1. Esto se puede hacer un controlador HTTP a la vez, para reducir los riesgos, y los interruptores también funcionan para usuarios individuales, para que pueda verificar que todo esté funcionando como se esperaba. Cuando no queden usuarios en Users-v0, Dark convertirá todos los datos restantes en segundo plano del antiguo formato al nuevo. No se dará cuenta de esto.

Pruebas

Dark es un lenguaje de programación funcional con tipado estático y valores inmutables, por lo que su superficie de prueba es significativamente menor en comparación con lenguajes orientados a objetos con tipado dinámico. Pero aún así, es necesario hacer pruebas.
En Dark, el editor ejecuta automáticamente pruebas modulares en segundo plano para el código editado y, por defecto, ejecuta estas pruebas para todos los interruptores de funciones. En el futuro, queremos usar tipos estáticos para ejecutar automáticamente fuzzing del código, para encontrar errores.

Además, Dark ejecuta su infraestructura en producción, lo que abre nuevas posibilidades. Guardamos automáticamente las solicitudes HTTP en la infraestructura de Dark (por ahora, guardamos todas las solicitudes, pero luego queremos pasar a la selección). Probamos nuevo código contra ellas y realizamos pruebas modulares, y si lo desea, puede convertir fácilmente solicitudes interesantes en pruebas modulares.

De lo que nos deshicimos

Como no tenemos despliegue, pero sí interruptores de funciones, alrededor del 60% del pipeline de despliegue queda fuera. No necesitamos ramas de git o pull requests, construcción de recursos backend y contenedores, envío de recursos y contenedores a registros o pasos de despliegue en Kubernetes.

Cómo Dark despliega código en 50 ms
Comparación del pipeline estándar de entrega continua (izquierda) y la entrega continua de Dark (derecha). En Dark, la entrega consta de 6 pasos y un ciclo, mientras que la versión tradicional incluye 35 pasos y 3 ciclos.

En Dark, hay solo 6 pasos y 1 ciclo en el despliegue (pasos que se repiten varias veces), mientras que el pipeline moderno de entrega continua consta de 35 pasos y 3 ciclos. En Dark, las pruebas se ejecutan automáticamente, y ni siquiera lo ves; las dependencias se instalan automáticamente; ya no es necesario trabajar con git o Github; no es necesario compilar, probar y enviar contenedores Docker; el despliegue en Kubernetes ya no es necesario.

Incluso los pasos restantes en Dark se han simplificado. Dado que los interruptores de funciones se pueden controlar con una sola acción, no es necesario recorrer todo el pipeline de despliegue nuevamente para eliminar el código antiguo.

Hemos simplificado la entrega de código tanto como hemos podido, reduciendo el tiempo y los riesgos de la entrega continua. Además, hemos simplificado enormemente la actualización de paquetes, las migraciones de bases de datos, las pruebas, la gestión de versiones, la instalación de dependencias, la paridad entre el entorno de desarrollo y producción, y las actualizaciones rápidas y seguras de versiones de lenguajes.

Estoy respondiendo preguntas sobre esto en HackerNews.

Para saber más sobre el dispositivo Dark, lee el artículo sobre Dark, síguenos en Twitter (o en mí) o regístrate para la versión beta y recibe notificaciones sobre las próximas publicaciones. Si vas a StrangeLoop en septiembre, ven a nuestro lanzamiento.

Fuente: habr.com

Compra un hosting fiable para sitios web con protección contra DDoS, servidores VPS VDS 🔥 Compra un hosting fiable para sitios web con protección contra DDoS, servidores VPS VDS | ProHoster