Cómo Terraform y Jenkins transforman la forma de trabajar

Hoy en día, la gran mayoría de los productos software vive en la nube. Ya es raro ver una aplicación instalada en un servidor físico dentro de una oficina; lo habitual es que se sostenga sobre infraestructura remota, porque para la mayoría de las empresas resulta mucho más rentable alquilar capacidad de cómputo que mantener sus propios servidores.

Pero contratar ese servicio no significa acceder únicamente a un espacio donde alojar y ejecutar código. Proveedores como Microsoft (Azure) o Amazon (AWS) ofrecen todo un catálogo de piezas ya listas para usar: bases de datos, sistemas de mensajería, almacenamiento, redes y muchos otros componentes. El equipo de desarrollo puede elegir y configurar los que mejor encajen con cada proyecto, aprovechando recursos ya creados, probados y mantenidos por el propio proveedor en lugar de construirlos desde cero.

En WeGen lo vivimos de cerca. Nuestros productos se apoyan en una serie de servicios en la nube que sostienen su funcionamiento, y todos esos servicios deben evolucionar y adaptarse al mismo ritmo que incorporamos nuevas funcionalidades. Precisamente en ese avance en paralelo el desarrollo del software y el de la infraestructura es donde empezamos a encontrar los primeros problemas.

El primer frente: la infraestructura que diverge sin avisar

Cuando cada pieza de infraestructura se crea y configura manualmente, los entornos comienzan siendo idénticos pero terminan divergiendo con el tiempo. Nos pasaba con frecuencia: cualquier miembro del equipo podía aplicar un pequeño arreglo directamente sobre un entorno que, aunque resolvía el problema en el momento, quedaba sin probar y sin registrar en ninguna parte. Cada cambio manual se convertía así en un riesgo silencioso, casi siempre olvidado, que solo salía a la luz cuando algo inesperado fallaba en producción.

El segundo frente: el código que nunca llega a producción

Cada día, el equipo integra nuevos cambios y los prueba en entornos internos. Poco a poco, el proyecto crece y con él el número de versiones que conviven en paralelo. Cuanto más grande se vuelve, más difícil resulta llevar la cuenta de qué versión está desplegada en cada sitio. El resultado es que algunos cambios probados con éxito en un entorno acaban sin llegar nunca a producción, simplemente porque nadie se dio cuenta de que aún no se habían desplegado.

Estos dos problemas apuntan a dos soluciones distintas pero complementarias: Terraform y Jenkins.

Terraform: la infraestructura como código

Terraform permite describir toda la infraestructura como código. Se declara qué recursos deben existir y con qué configuración, y esta tecnología se encarga de crearlos, modificarlos o eliminarlos para que el estado real de la nube coincida siempre con lo definido en el repositorio. La infraestructura deja de ser un conjunto de decisiones manuales dispersas y se convierte en algo versionado, revisable y reproducible, donde nada existe sin dejar rastro.

En nuestro caso, Terraform nos permite levantar y mantener todos los servicios en la nube sobre los que se apoyan los productos. Cada uno queda descrito en código, con su configuración explícita, de modo que incorporar un servicio nuevo es tan sencillo como añadir su bloque de definición y aplicarlo. Requiere algo más de tiempo por adelantado y cierta disciplina para mantenerlo, pero el proyecto gana en coherencia: cualquier entorno se reconstruye igual, los cambios quedan documentados y nada existe «por fuera».

Jenkins: el despliegue que se ejecuta solo

Jenkins es un orquestador de integración y despliegue continuo (CI/CD) que permite definir un pipeline, es decir, una secuencia de pasos automatizados que se ejecuta siempre igual, sin importar quién la inicie. En lugar de que cada persona despliegue a su manera, describimos una única vez los pasos necesarios para compilar, probar y desplegar los cambios de código, y Jenkins se encarga de ejecutarlos en cualquier entorno.

El proceso sigue siempre la misma secuencia: extrae el código del repositorio y compila cada servicio, genera una imagen de contenedor por cada uno y las publica en el registro correspondiente, y finalmente redespliega esos servicios en el clúster donde el producto realmente se ejecuta. El despliegue deja de depender de la memoria o las costumbres de cada persona y pasa a ser un proceso reproducible por todos, en continua revisión y mejora. En todo momento sabemos qué versión está desplegada, quién la lanzó y con qué resultado.

La combinación que lo cambia todo

El verdadero salto llega al combinar ambas tecnologías. Si dentro del propio pipeline de Jenkins incluimos también la infraestructura definida en Terraform, podemos gestionar por código operaciones que de otro modo serían manuales y costosas: modificar una base de datos con más seguridad, añadir o ajustar rutas en una API con más agilidad o crear y mantener infraestructura con la misma trazabilidad con la que ya tratamos el código de la aplicación.

El cambio es contundente. Dedicamos menos tiempo a apagar fuegos y más a construir producto.

Lo que nos llevamos aprendido

Después de implantar estas herramientas, hay tres lecciones que destacaría:

1) la disciplina inicial lo es todo. Codificar la infraestructura requiere un esfuerzo real al principio; la tentación de hacer un cambio rápido «a mano» siempre está ahí. Pero cada excepción es una deuda que se paga más tarde, normalmente en el peor momento posible.

2)  la visibilidad lo cambia todo. Saber en todo momento qué está desplegado, quién lo cambió y por qué no es solo comodidad: es lo que permite detectar un problema antes de que llegue a producción y lo que hace que incorporar a alguien nuevo al equipo sea mucho menos traumático.

3) el pipeline es un producto en sí mismo. No basta con definirlo una vez y olvidarlo; hay que cuidarlo, mejorarlo y revisarlo igual que se cuida el código de la aplicación. Cuando el equipo lo trata así, el retorno es enorme.