h1

Despliegue Continuo (Continuous Delivery)

06/23/2011

Wizdoc [Icon By Buuf]  Tips & Tricks.

La semana pasada realizamos nuestro primer release a producción exitoso de la aplicación sobre la que tenemos responsabilidad. Esto es importante porque hace poco más de un mes hicimos un intento de liberación que resultó infructuoso, aunque las razones del fallo anterior se debieron más a cuestiones de proceso aunadas a la inexperiencia del equipo, que a problemas de la aplicación misma. Siendo así las cosas, realizamos un pequeño postmortem donde detectamos y corregimos algunos detalles del delivery que ya durante nuestro segundo intento, funcionaron sin problemas. Básicamente hicimos un pequeño control de daños de:

Gestión de la Configuración. Verificar que todos los componentes necesarios para ejecutar la aplicación estén correctamente identificados, almacenados y versionados. En este caso, nos faltó verificar la existencia – o más bien, inexistencia – de una serie de colas JMS que generaron errores durante la ejecución de la aplicación, que en aquél momento devinieron en nuestra decisión de echarnos para atrás.

Plan de rollback. Qué hacer si el release no es exitoso. Durante nuestro primer intento, se nos fueron las cabras al iniciar la migración de la aplicación sin realizar un respaldo previo de la base de datos; el cambio de versión implica correr varios scripts de base de datos, así que cuando decidimos regresarnos a la versión anterior, sudamos la gota gorda porque tuvimos que hacer un rollback en caliente de algunos stored procedures e índices de tablas que habían cambiado.

Plan de comunicación. Quién debe estar presente durante la liberación a producción; quién debe tomar las decisiones sobre qué. En este caso, el administrador de bases de datos empezó a trabajar sin esperar al resto del equipo, por lo que en conjunto con los dos puntos anteriores, nos metimos en problemas. De haber estado todos desde un principio, alguien pudo haber tenido la feliz ocurrencia de preguntar: “¿Por cierto, ya realizaste un respaldo de la base de datos?”

Sin embargo, nosotros ya estamos hablando de “mejora continua” del proceso de despliegue, cuando la mayoría de las empresas ni siquiera tienen un plan de liberación o cada que realizan una salida a producción todos entran en pánico porque el equipo tiene que pasar hasta 36 horas seguidas depurando defectos que se escaparon durante el desarrollo o buscando entre los archivos de configuración porque el balanceo de cargas no funciona. Es así como incluyo a continuación qué es el Despliegue Continuo o Continuous Delivery y cómo es posible alcanzarlo:

Despliegue Continuo es un concepto salido de las mejores prácticas de administración de la configuración, pruebas automatizadas, integración continua, gestión de datos y gestión de versiones. El objetivo es automatizar al máximo el despliegue de un sistema en un ambiente productivo, minimizando los riesgos asociados a dicho proceso. En pocas palabras, generar “liberaciones por botonazo“. Para lograrlo es necesario contar con una infraestructura y mejores prácticas que de ser correctamente implementadas, permiten alcanzar el objetivo propuesto.

Delivery Infrastructure

Infraestructura mínima necesaria para realizar despliegues continuos. El corazón del proceso reside en el uso extensivo de un servidor de integración continua (Hacer click en imagen o aqui para ver en tamaño original)

La infraestructura consiste de hardware y herramientas o frameworks que agilizarán el ciclo de vida de desarrollo de software (construcción-pruebas-liberación): desarrollo es el ambiente local donde trabajarán los programadores y se generarán la codificación y configuración de la aplicación, así como los documentos, manuales y demás artefactos producidos durante la construcción. Es muy común que este ambiente contenga a la aplicación funcionando de manera local. Una vez que se haya construido una historia de usuario o caso de uso, el código, configuraciones y documentos asociados serán subidos a los repositorios de código y configuración para su correcta administración de versiones. Éstos pueden residir en un solo repositorio, pero deben localizarse en una máquina independiente de las demás, de preferencia en una locación segura. Por otro lado, en muchos desarrollos existen librerías comunes a varios proyectos; en otros casos puede tenerse un “proyecto base” que tenga baja variabilidad y sólo cambie muy de vez en cuando. Para no compilar nuevamente todo cada vez que se realiza un despliegue es necesario contar con un repositorio de dependencias que facilite su gestión.

Como parte de las buenas prácticas de desarrollo, es necesario contar con un servidor de integración continua, que de forma periódica descarga la última versión de la aplicación y ejecuta automáticamente una serie de pruebas unitarias para conocer la “salud general” del código encontrado en el repositorio de versiones. Si estas pruebas unitarias se ejecutan correctamente, se puede utilizar el mismo servidor de integración para generar un despliegue que será depositado en el ambiente de pruebas: un ambiente lo más parecido a producción donde el equipo de aseguramiento de calidad así como el usuario o gerente de producto podrán verificar la funcionalidad de la aplicación. Más tarde es necesario realizar pruebas de capacidad o performance. Esto se logra en un ambiente de desempeño, que es una copia fiel del ambiente de producción con la capacidad de ser estresado por el equipo de aseguramiento de calidad. La principal diferencia entre éste y el ambiente de pruebas es la escalabilidad: el ambiente de desempeño ya tiene configuradas funcionalidades como balanceo de carga o migración de sesiones de usuario. Finalmente, previa autorización, se sube la aplicación y su configuración a un ambiente de producción. Para desplegar la nueva versión o regresar a una anterior, debe existir un proceso automatizado o botonazo que pueda ser ejecutado desde el servidor de integración continua.

Ahora bien, este flujo debe tomar en cuenta las siguientes prácticas durante su ejecución, a manera de detalle:

Desarrollo de software – Los IDEs y frameworks de construcción (Apache Ant, Apache Maven) deben permitir un despliegue local mediante el uso de un botonazo. Por supuesto, todo proceso de despliegue debe estar configurado a través de un script, gestionado desde el sistema de control de versiones y ejecutado como parte del flujo del despliegue.

  • Los scripts de construcción deben ser independientes del IDE. Al ser implementados de esta manera, pueden ser ejecutados automáticamente por el sistema de integración continua.

  • Crear un script que genere una versión ligera de la base de datos. Cada desarrollador usará este script para poblar su base de datos local y agilizar la ejecución de pruebas.

  • Como corolario del punto anterior, es necesario asegurarse que la aplicación sea compatible con versiones anteriores de la base de datos para que ambas puedan ser desplegadas de forma independiente. En caso de existir diferencias, deberán generarse los scripts para migrar de versión hacia adelante o hacia atrás, según se requiera.

  • Las diferencias entre los ambientes son capturadas como información de configuración. Todos los valores de estas variables son externalizados como propiedades a tomar durante la generación del build o para ser tomadas por la aplicación en tiempo de ejecución.

  • El mismo script usado para realizar un despliegue debería poder utilizarse para liberar la aplicación a cualquier ambiente que sea necesario. Las variables de configuración deberían ser modificables, pero administradas (ver punto anterior). Esto permitirá que cualquier miembro del equipo pueda realizar de igual manera un despliegue en su máquina local o en alguno de los demás ambientes.

Repositorio de versiones – Todo el código fuente, incluyendo ejecutables así como configuraciones de aplicación y de ambiente debe ser almacenado y gestionado desde un repositorio de control de versiones. Algunos tips durante su gestión:

  • Evaluar y utilizar software de gestión de la configuración generado por terceros que pueda ser fácilmente configurado, desplegado y automatizado (Apache Subversion).

  • En todo proyecto deben mantenerse al mínimo las ramas o branches.

  • Los cambios que se realicen sobre el código deben subirse de manera periódica; cada miembro del equipo debe realizar un check in de forma regular al menos una vez por día, sin embargo es inaceptable subir código roto o que no haya sido probado.

  • Las ramas deberían tener un tiempo de vida corto – no más de algunos días y jamás mayores a una iteración.

Repositorio de dependencias – Siempre es conveniente centralizar la administración (Apache Maven) de todas las librerías de las que depende un desarrollo con el afán de disminuir el esfuerzo requerido para llevar su versionamiento o actualización, problemas en el classpath, repetición en el uso de dichas librerías o dependencias transitivas entre diferentes proyectos.

  • Un beneficio adicional de la administración de dependencias es que una vez compilado un código, es posible administrar el resultado de su compilación como si fueran librerías. Esto permite disminuir la carga de trabajo al incluir un repositorio de binarios para hacer uso de éstos cuando se realiza un despliegue. Es decir, en vez de compilar todo el código, sólo se compilan las partes que fueron modificadas, mientras el resto es tomado del repositorio de dependencias.

Integración Continua – La parte medular de un despliegue continuo es la adopción de un servidor de integración continua que generará un build y validará mediante la ejecución de pruebas automatizadas la salud del código cada vez que se realiza un commit en el repositorio de versiones. Algunas opciones que deberían estar presentes en la configuración del sistema de integración continua incluyen:

  • Retroalimentación continua, en forma de correos o notificaciones automáticas a todos los miembros del equipo cuando un build es o no exitoso.

  • Corregir errores en el build o su ejecución en cuanto ocurran. Nadie realizará un check in en el proyecto hasta que el error haya sido corregido.

  • Considerar un build no exitoso cuando el código viole las “reglas del juego”. Por ejemplo, problemas en la arquitectura, pruebas que tardan demasiado en ejecutarse o violación de los estándares de codificación.

Pruebas – Es un error muy común confiar la validación de la funcionalidad del código a una “fase de pruebas”: esto es heredado del desarrollo basado en el modelo de cascada, que es propenso a muchos problemas. La mejor opción es basar la construcción en el desarrollo guiado por pruebas (Test Driven Development – TDD) donde primero se escribe la prueba y se le hace fallar; luego se escribe el código necesario para hacerla exitosa y finalmente se le refactoriza de acuerdo a los estándares de programación adoptados. En el contexto del despliegue continuo, estas pruebas deben escribirse de tal manera que puedan ejecutarse automáticamente con el fin de validar que un código funciona correctamente; asimismo disminuye el tiempo necesario para realizar otro tipo de pruebas, como las de regresión o desempeño.

  • Todas las operaciones realizadas deben ser contenidas en transacciones. Para pruebas relacionadas a la base de datos, una buena idea es incluir la transacción completa y después realizar un rollback para verificar su ejecución.

  • Pueden usarse stubs u objetos simulados para reproducir la interacción con sistemas externos, reduciendo la complejidad del despliegue en un ambiente local.

  • Las pruebas podrían hacerse en paralelo, ejecutando subconjuntos de estas en diferentes máquinas y disminuyendo su tiempo de ejecución.

  • Si es necesario realizar cambios sobre la base de datos, se deben generar scripts que realicen esta acción, para no realizarlos de manera manual o tener que usar operaciones de importación/exportación.

  • Usar scripts para aplicar cambios incrementales en cada ambiente, ya sea al esquema de la base de datos o a la información que contiene.

Desempeño – Incontables aplicaciones son liberadas a producción sin haber verificado su desempeño. Un ambiente lo más parecido al productivo permite detectar cuellos de botella así como los montos máximos de usuarios, transacciones o throughput de los que es capaz de atender sin degradación en la calidad del servicio.

  • Es muy importante automatizar en la medida de lo posible el aprovisionamiento del ambiente de desempeño, para no tener que “encontrar el hilo negro” al aprovisionar un nuevo ambiente. Esto es especialmente importante si se quiere buscar mayor escalabilidad en el sistema (por ejemplo, agregar un servidor aplicativo) sin tener que llamar a todo al equipo para su aprovisionamiento.

  • Si el ambiente de desempeño es en efecto una copia del productivo, es especialmente útil porque en éste pueden realizarse ensayos de despliegue, encontrando defectos en el proceso de liberación antes de que se presenten durante una ventana de mantenimiento productiva.

Producción – El ambiente donde se ejecutará la aplicación y al que sólo el personal de operaciones debe tener acceso.

  • De ser posible, realizar “despliegues de advertencia”, donde se libera el software a un pequeño subconjunto de clientes (por ejemplo, el 10%) previo a una liberación masiva. Esto es más sencillo si existen instancias de la aplicación para cada región o país.

  • Hacer uso de un “despliegue azul-verde” (Blue-Green Deployment): Primero se realiza un despliegue completo en un ambiente no productivo (ambiente azul) mientras producción (ambiente verde) sigue corriendo. Una vez que ha sido comprobada la funcionalidad de la aplicación a través de algunas “pruebas de humo” (smoke tests), se realiza un switch donde se deja al ambiente azul en producción mientras se desconecta el ambiente verde.

Blue-Green Deployment

Para no tener que apagar producción durante el despliegue, puede usarse el blue-green deployment.

  • Los despliegues deben realizarse cuando afecten a la menor cantidad de usuarios posible. Por ejemplo, mediante ventanas de mantenimiento nocturnas.

  • Ante cualquier eventualidad, debe existir un plan de rollback que permita regresar a la versión anterior de la aplicación de la manera más transparente posible, incluyendo configuración y base de datos.

  • Implementar un monitoreo constante de los componentes de la plataforma (red, memoria, CPU, disco, colas de mensajes, bitácoras) y en caso de detectar una anomalía, iniciar el proceso de rollback.

  • Una vez que haya sido finalizada la liberación, exitosa o no, es indispensable realizar una retrospectiva o postmortem para discutir cómo se puede mejorar el proceso de despliegue en la próxima iteración.

Conclusiones

Es bastante complicado liberar a producción. Sin embargo, siguiendo estos tips se pueden disminuir los tiempos y el riesgo asociados durante el “paso de la muerte”. Por experiencia propia veo que este flujo sí sirve, sin embargo algunas partes del mismo pueden representar un gasto adicional que muchos clientes no están dispuestos a pagar, como un ambiente de desempeño idéntico al de producción o tener por separado un servidor de integración continua. Sin embargo, queda en nosotros hacerle ver al cliente que no es un gasto sino una inversión, ya que al generar deliveries continuos se evitan muchos problemas, tanto internos – como alta rotación de personal debido a ingenieros que están hartos de salir hasta las 4:00 AM cada que hay un release – hasta externos – como retrasos en una salida a producción – que pueden significar una potencial pérdida de dinero.

Por lo pronto, nosotros ya estamos preparando el siguiente release de nuestra aplicación, que saldrá en una semana. Aunque el equipo de trabajo sigue estando bastante verde, ya hay más confianza en que podemos resolver las broncas que lleguen a salir y si todo resulta bien, habrá que hacer una pequeña celebración porque después de todo, el equipo original ya no está y nosotros tenemos que cargar con el muertito, sacando la chamba a como dé lugar.

One comment

  1. Que sufrimiento!, bueno pero lo bueno es que de los errores se aprende bastante, aunque ese “errorcito” de no respaladar la BD se me hace muy inocente por parte del DBA; me invitan a la celebracion, ok, ok… me invitan a apoyarlos moralmente a la liberacion.

    Interesante tus comentarios sobre estos devenires de los “Delivery”, las aventuras y ganancias de aprendizaje en el proceso.

    Es verdad tu comentario sobre esas salidas a las 4:00am… la vida del desarrolador deberia ser menos estresante pero la vida es asi, pobres chicos a veces se sienten muy traumatizados.

    Saludos y gracias por tu blog. Siempre con temas muy interesantes.

    PD. Perdon por la falta de acentos.



Responder

Introduce tus datos o haz clic en un icono para iniciar sesión:

Logo de WordPress.com

Estás comentando usando tu cuenta de WordPress.com. Cerrar sesión / Cambiar )

Imagen de Twitter

Estás comentando usando tu cuenta de Twitter. Cerrar sesión / Cambiar )

Foto de Facebook

Estás comentando usando tu cuenta de Facebook. Cerrar sesión / Cambiar )

Google+ photo

Estás comentando usando tu cuenta de Google+. Cerrar sesión / Cambiar )

Conectando a %s

A %d blogueros les gusta esto: