Índice de contenidos
- Introducción
- Para qué usar Terragrunt
- Estructura de un repositorio de Terragrunt
- Repositorio único vs múltiples repositorios
- Conclusiones
- Referencias
Introducción
En un tutorial previo habíamos mencionado Cómo definir nuestra infraestructura en Terraform y esta es la continuación de cómo utilizaríamos esos módulos en el ámbito de un repositorio de Terragrunt.
El contenido está basado en mi experiencia trabajando con Terragrunt y los criterios aprendidos para definir una estructura que sea usable y escalable en función de la magnitud del proyecto.
Se da por hecho que ya se tiene cierto conocimiento sobre Terragrunt y Terraform, de forma que haremos referencia a términos usados en el uso de ambas herramientas.
Nos centraremos en responder a las siguientes preguntas:
- ¿Para qué deberíamos usar Terraform?
- ¿Qué estructura debería tener un repositorio de Terragrunt?
- ¿Deberíamos crear un repositorio único o varios repositorios?
Para qué usar Terragrunt
Terragrunt es una herramienta auxiliar a Terraform que nos permite gestionar algunas carencias que tiene Terraform de forma sencilla, como:
- Gestión de estados.
- Gestión de dependencias entre módulos de forma explícita.
- …
Las convenciones que mejor me han funcionado son las siguientes:
- Siempre Terraform + Terragrunt: Cuando se decide Terraform como herramienta para definir la infraestructura, siempre debería ir acompañada de Terragrunt debido a la cantidad de beneficios que nos aporta y veremos en las secciones posteriores.
-
Terragrunt es una representación de tu infraestructura: El repositorio de Terragrunt debería reflejar todo lo que está desplegado, y qué configuración específica tiene. Si quieres ver exactamente qué componentes son, vas a verlos al módulo de Terraform.
Idealmente, deberías ser capaz de saber qué tiene tu infraestructura, así como las dependencias que tienen los elementos de forma explícita, y no porque te encuentres un
data
perdido dentro de un módulo de Terraform recuperando un valor que tienes en otro módulo en un lugar diferente. - Terragrunt SOLO tiene información contextual: A diferencia de los módulos de Terraform, aquí si que tenemos la configuración específica de esos recursos, tanto la que dependa del entorno como de la instancia concreta que estemos desplegando del módulo de Terraform.
- Terragrunt es el encargado de gestionar los estados de Terraform: Los estados y la generación del
backend
es siempre gestionada a nivel de Terragrunt, y se importará a cada módulo antes de cada ejecución de forma que podamos segregar al máximo los estados reduciendo el riesgo de cambios.
Mientras más pequeños sean los estados, mejor para todos. -
Terragrunt es el encargado de gestionar los providers de Terraform: De la misma forma que no gestionamos el
backend
, los providers también se definen a este nivel, ya que son clara información contextual.Ejemplo: En Terraform tienes dos recursos en el mismo módulo que pertenecen a cuentas diferentes de Amazon en las que se despliegan. El módulo de Terraform no debería tener más referencia de estas cuentas que a nivel de definir que necesita dos providers. Es en Terragrunt, donde tenemos información del contexto donde le mandamos ambos providers de forma que decidamos realmente dónde se quieren desplegar esos dos recursos (Puede que para un entorno de test queramos que convivan en el mismo lugar)
Estructura de un repositorio de Terragrunt
Los principios de una buena estructuras mencionados en el tutorial anterior aplican también en este caso. Buscamos una estructura que sea:
- Consistente: Necesitamos saber dónde está cada recurso y que estos siempre estén en el mismo lugar. Del mismo modo, necesitamos poder definir variables a diferentes niveles que nos faciliten las configuraciones comunes y las promociones de código
- Intuitiva: Si no sabemos lo que es buscamos, la estructura de directorios nos ayuda a encontrar lo que buscamos.
- Simple: Buscamos la estructura más simple que se adapte a la flexibilidad de nuestra infraestructura. Aquí quiero añadir que muchas veces una estructura puede ser simple y también GRANDE, debido a que tienes muchos elementos en tu infraestructura con muchos ciclos de vida diferentes. Y está bien que sea así, ya que esta estructura es un reflejo de tu infraestructura.
- Sencilla de usar
- Documentada
- Replicable
A diferencia de la estructura de Terraform, necesitamos hacernos dos preguntas muy importantes que nos ayudarán a definir la cardinalidad de nuestro repositorio.
- ¿Dónde vamos a desplegar la infraestructura?
- ¿Qué elementos lógicos nos ayudan a diferenciar de forma única una instancia de un módulo de Terraform de otra?
El dónde vamos a desplegar nuestra infraestructura nos ayuda a definir una cardinalidad fija en función del proveedor que vamos a utilizar. Yo he trabajado sobretodo con AWS, y en este caso, Amazon divide sus recursos en los siguientes niveles:
- Cuenta: La cuenta de Amazon donde tienes desplegado un recurso.
- Región: La región en la que está desplegado.
Por otra parte, hay otras divisiones lógicas por las cuales nos interesa identificar un recurso, y que varian en función del proyecto.
Vamos a definir algunas de ellas como ejemplos que pueden servir:
- Proyecto: Es posible que tu infraestructura de soporte a más de una aplicación, y que por tanto esté dividida a nivel de proyecto.
- País: Es posible que elementos de tu infraestructura estén divididos por país, o que por motivos de disaster recovery necesites tener tu aplicación en varios países.
- Entorno: Esta es una de las más habituales, el tener entornos para pruebas por los que vas promocionando hasta llegar a producción.
- Instancia de módulo: Es posible que quieras desplegar un módulo de terraform con diferentes configuraciones (un módulo de VPC para las VPCs de diferentes entornos por ejemplo)
- …
Una vez tienes las cardinalidades por las que puedes identificar cualquier elemento de tu infraestructura, les das un orden en la estructura de directorios en función de qué quieras agrupar.
En mi experiencia, lo mejor es tratar a la infraestructura desde el punto de vista más estable posible, es decir, que las divisiones lógicas deberían ser estables.
Por ejemplo, si mañana mi aplicación cambia de equipo, no quiero que cambie su estructura de directorios.
En base a esto, podríamos tener una estructura como:
.
.
└── PROYECTO
├── accounts.hcl
├── backend.tftpl
├── countries.hcl
├── environments.hcl
├── external-dependencies.hcl
├── project.hcl
├── providers.tftpl
├── regions.hcl
├── PAÍS
│ ├── country.hcl
│ └── CUENTA
│ ├── account.hcl
│ └── ENTORNO
│ ├── environment.hcl
│ └── REGION
│ ├── region.hcl
│ └── MÓDULO
│ ├── stack.hcl
│ ├── INSTANCIA
│ ├── instance.hcl
│ └── main
│ └── terragrunt.hcl
└── terragrunt.hcl
- Vemos como la organización de las cardinalidades tratan de ir de lo más genérico a lo más concreto.
- Siempre que estás a una altura específica, vas a encontrarte el mismo tipo de ámbito.
- Todos los ficheros que tienen nombre de una cardinalidad, son variables específicas para ese entorno, cuyos valores se aplican a todos los directorios hijos.
En este tutorial no se pretende detallar en cómo funciona cada uno de estos directorios en detalle, pero al final voy a dejar un enlace a un repositorio que explica cómo funciona la herencia de estas variables y cómo se trabajaría con un repositorio así en el README del repositorio plantilla.
Me parece más interesante que tengamos el ejemplo de qué preguntas tenemos que hacernos en función de lo grande que sea el proyecto, o lo que creamos que puede escalar para definir una estructura que no nos de problemas en el futuro, del mismo modo que este mismo criterio se puede aplicar para definir una estructura en Azure o en Google Cloud.
También en estos proyectos es adecuado tener una serie de convenciones de consistencia con ficheros como:
.editorconfig
.gitignore
Pipfile
README.md
Y uso de herramientas como:
Makefiles
Pre-commit
Todo esto fue explicado en el tutorial anterior.
Repositorio único vs múltiples repositorios
Prácticamente no hay mucha discusión en cuanto a esto, ya que lo mejor que podemos hacer es tener toda la infraestructura definida en un único repositorio ya que esto nos permite hacer uso completo de las dependencias entre todos los elementos de nuestra infraestructura, evitándonos usar datasources
de Terraform para obtener información contextual, que son el mal :).
El único criterio por el cual tendríamos repositorios separados debería ser por si hay algún requisito de seguridad que no nos permita tener todo junto. Como siempre, habría que tratar de reducir esto al mínimo ya que esto nos ayuda a hacer mejor uso de las dependencias de Terragrunt.
Conclusiones
Espero que este tutorial te ayude a saber qué estructura debería tener tu repositorio, o a tener una perspectiva diferente de cómo está montado en otros sitios.
He de decir que el ejemplo de esta estructura ha sido aplicado en algunos proyectos muy grandes y ha funcionado bastante bien.
Como siempre, aquí tienes la plantilla que utilizo para los repositorios de Terragrunt