La importancia de la rama estable

Infraestructura como código

En cualquier proyecto de Infraestructura como código, la meta preseguida es contar con código capaz de reflejar y replicar fielmente la infraestructura en producción de una organización.

Esto no es solo por posibilitar una rápida vuelta a producción ante eventuales catástrofes, sino principalmente porque la mayor utilidad que se le da al código es en el día a día. Es parte fundamental del proceso iterativo-incremental en la mejora de los servicios brindados, y muchas veces la mejor documentación con la que se cuenta.

A través de buenas prácticas como la idempotencia y la segmentación que brindan las herramientas manejadas (Por ej. tags y roles en Ansible), es posible realizar tareas cotidianas como instalar algún paquete adicional o modificar un parámetro puntual en un archivo de configuración, con la certeza de que no se generarán conflictos ni se duplicará lo hecho anteriormente. Y por si fuera poco, con la ventaja de tener un downtime nulo o del orden de segundos, sin tipear manualmente en un terminal del servidor.

Otra ventaja, es poder contar (cifrado mediante) con las contraseñas/credenciales utilizadas en sistemas y servicios dentro el mismo código. Estando siempre disponibles para los participantes del proyecto, de forma segura y en un ambiente controlado, eliminando vectores potencialmente peligrosos como la compartición de credenciales de accesos por múltiples vías.

Al rever todo lo comentado hasta aquí, focalizándolo en equipos de desarrollo/operaciones que trabajan en forma distribuida y concurrente, podrás deducir que resulta vital acordar procedimientos para mantener el código "sincronizado" a la infraestructura y viceversa.

Si un servidor en producción fue configurado parcialmente "a mano" desde una terminal, no podemos garantizar que cuando alguien más ejecute el código se eviten regresiones o inconsistencias, mas aún si los cambios manuales no fueron debidamente documentados. Del mismo modo, si sobre un servidor se mejoraron configuraciones a través de código, pero el nuevo código no fue puesto a disposición del equipo, cuando otro miembro ejecute el proyecto sobre el mismo servidor, se generará una regresión (se establecerá la configuración con valores antiguos).

Ante estos escenarios, el problema será perceptible por el mal funcionamiento de los servicios, pero difícilmente rastreable y corregible por no tener una forma directa de determinar cual era la configuración correcta.

Lo prometido

Y llegamos, finalmente, a la razón que motiva esta entrada de la wiki. Dejando de lado la introducción agnóstica, centrémonos en nuestro stack: Ansible y Git.

Nuestro proyecto está desarrollado en Ansible y trabajamos todo lo relativo a él empleando Git como gestor de versiones. Yendo un paso mas allá, hosteamos el repositorio en nuestra instancia de GitLab y sacamos todo el provecho posible de las herramientas que éste brinda en torno al trabajo colaborativo, gestión de incidencias, discusiones y documentación.

Adoptando este flujo de trabajo, separamos por ramas git (partiendo de una incidencia) los avances en el desarrollo de una nueva característica o correcciones a una ya implementada. Y así, aislados, permanecen estos avances hasta que maduran lo suficiente como para ser reflejados en producción, integrándolos con el resto del código estable y operativo.

El código estable y operativo es, en esencia, la rama master del repositorio. Pero por sobre ello, master debe ser el fiel reflejo en código de nuestra infraestructura en producción, como se mencionaba en la introducción.

Mientras que en los servidores arenero o stage rara vez se trabaja concurrentemente, los servidores en producción son administrados por todos los miembros del equipo (desarrolladores y/o operadores). master representa ese lugar de común acuerdo en el que se asumirá que se encuentra el código con el que los servicios fueron montados, aprovisionados y mejorados. Es desde donde todos los miembros partirán a trabajar mejoras y desde donde correrán playbooks contra servidores en producción.

Resulta entonces de vital importancia que cualquier cambio aplicado a un servidor en producción sea reflejado como código Ansible (siempre que sea posible, de lo contrario debidamente documentado y notificado) en rama master y subido al repositorio remoto tan pronto sea posible. Del mismo modo, es estrictamente necesario actualizarse desde la rama master remota antes de ejecutar un playbook contra servidores en producción. Disponerte a ejecutar un playbook usando el inventario de hosts en producción (el archivo hosts_prod) es un buen elemento para recordarte seguir estas prácticas, pero no el único.

Ejemplos puntules de problemas que se pueden generar al realizar cambios y no ponerlos a disposición del resto del equipo son: despublicación de registros en el resolvedor de nombres o eliminar del firewall del cluster reglas que permiten determinados accesos. En ambos casos, son solo pocas líneas de código en un solo archivo, pero que determinan la disponibilidad o no de uno o varios servicios. El razonamiento y los riesgos son idénticos al ejecutar playbooks sin previamente asegurarse que se cuenta con la versón mas reciente del proyecto.

Los ejemplos mencionados en el párrafo anterior no son elegidos al azar. Se trata de casos particulares, excepciones a la regla de trabajar siempre en ramas originadas por incidencias. Debido a que la incidendencia que nos ocupa puede tomar más tiempo en resolverse y llevarse a la rama estable, en estos casos es (más que recomendable) necesario que los cambios se hagan sobre la rama master actualizada, y posteriormente aplicados, comprobados y subidos al repositorio.

Es necesario aclarar explícitamente, que del mismo modo que el código "terminado" y aplicado en los servidores se debe subir rápidamente a master, el razonamiento inverso también aplica. No deben subirse a master avances parciales, código sin probar o incompleto, para ello están destinadas las demás ramas del repositorio.

Modularidad

Siguiendo las buenas prácticas Ansible, los roles se han ido desacoplando del proyecto config y pasados a repositorios individuales en GitHub para ser publicados y compartidos con la comunidad en Ansible Galaxy.

Por más que se encuentran en otro repositorio, son lógicamente parte del mismo proyecto, y por ende lo comentado en este artículo sigue aplicando. En GitHub también tenemos ramas secundarias e incidencias para tratar mejoras y correcciones propios de la implimentación del role, y consideramos a master como la versión estable y funcional de dicho role.

Adicionalmente, utilizamos versionado semántico para marcar cambios menores, adición de características y/o reestructuraciones que eliminan la retrocompatibilidad con las vars mantenidas hasta el momento. En este último caso es importante reflejar los cambios en config y llevar ambos a sus respectivas ramas master simultáneamente.