Saltar al contenido principal

Infraestructura como código

La infraestructura como código (IaC) consiste en definir nuestros servidores, redes y bases de datos en ficheros de texto versionados, en lugar de crearlos pinchando en una consola web.

El cambio que produce es más profundo de lo que parece: la infraestructura pasa a tener las mismas propiedades que el código. Historial, revisión por pares, capacidad de volver atrás y posibilidad de recrearla desde cero.

Lo que sustituye​

El modelo manual tiene un problema que no se ve hasta que es tarde:

1. Documentar la infraestructura en un Excel
2. Crear los servidores a mano en la consola
3. Configurarlos entrando por SSH
4. Rezar para poder replicarlo
5. Descubrir que staging y producción no se parecen

El paso 5 es el que duele. Cuando staging y producción divergen, staging deja de servir para lo único que sirve: predecir qué pasará en producción.

Con IaC, ambos entornos salen de la misma definición.

Qué nos da​

  • Repetible. El mismo código produce la misma infraestructura, siempre.
  • Versionado. git log sobre nuestra infraestructura, y git revert cuando hace falta.
  • Revisable. Los cambios pasan por un pull request antes de tocar producción.
  • Documentada por definición. El código es la documentación, y no se queda obsoleto.
  • Recuperable. Perder una región deja de ser una catástrofe y pasa a ser un apply.

Las herramientas​

Terraform / OpenTofuPulumiCloudFormation
LenguajeHCL (declarativo)Python, TypeScript, GoYAML / JSON
NubesTodasTodasSolo AWS
EstadoFichero propioFichero propioGestionado por AWS
EcosistemaEnormeMedioSolo AWS

Terraform es el estándar de facto y con lo que trabajaremos aquí. OpenTofu es su bifurcación open source tras el cambio de licencia de HashiCorp en 2023, y es compatible. Pulumi interesa si preferimos un lenguaje de programación real con bucles y condicionales de verdad. CloudFormation solo tiene sentido si vivimos exclusivamente en AWS.

Lo básico de Terraform​

provider "aws" {
region = "eu-west-1"
}

resource "aws_vpc" "principal" {
cidr_block = "10.0.0.0/16"
enable_dns_hostnames = true

tags = {
Name = "vpc-principal"
Environment = var.entorno
}
}

El flujo de trabajo son tres comandos, y el del medio es el importante:

terraform init # descarga los proveedores
terraform plan # ¿QUÉ va a cambiar?
terraform apply # aplícalo

plan es la razón por la que Terraform funciona. Nos enseña exactamente qué va a crear, modificar o destruir antes de tocar nada. Lo leemos siempre, y especialmente buscamos la palabra destroy — algunos cambios aparentemente inocentes obligan a recrear un recurso, y "recrear" una base de datos significa perderla.

El fichero de estado​

Terraform guarda en un fichero .tfstate qué recursos ha creado y cómo se corresponden con nuestro código. Es lo que le permite saber que el aws_vpc.principal de nuestro fichero es esa VPC concreta que existe en AWS.

Es la pieza más delicada de todo el sistema:

  • Nunca en Git. Contiene valores en claro, contraseñas incluidas.
  • Siempre remoto y compartido, o cada miembro del equipo tendrá una versión distinta de la realidad.
  • Con bloqueo, para que dos apply simultáneos no lo corrompan.
terraform {
backend "s3" {
bucket = "mi-empresa-terraform-state"
key = "produccion/terraform.tfstate"
region = "eu-west-1"
encrypt = true
use_lockfile = true
}
}

Y una regla que ahorra disgustos: un estado por entorno. Si dev y producción comparten estado, un error en dev puede destruir producción.

Módulos​

Un módulo es infraestructura empaquetada y parametrizada, igual que una función:

# modules/vpc/main.tf
resource "aws_vpc" "this" {
cidr_block = var.cidr_block
tags = merge(var.tags, { Name = "${var.prefijo}-vpc" })
}

output "vpc_id" {
value = aws_vpc.this.id
}
# environments/produccion/main.tf
module "vpc" {
source = "../../modules/vpc"

prefijo = "produccion"
cidr_block = "10.0.0.0/16"
}

Un aviso por experiencia ajena: no creemos un módulo hasta que tengamos el mismo patrón dos o tres veces. Abstraer demasiado pronto produce módulos con veinte variables que nadie entiende, y que acaban siendo más difíciles de usar que copiar y pegar.

Antes de escribir el nuestro, miremos el Terraform Registry: los módulos de VPC y EKS de la comunidad están mucho más pulidos que lo que vamos a escribir la primera vez.

Organizar el proyecto​

infrastructure/
├── modules/ # piezas reutilizables
│ ├── vpc/
│ └── rds/
└── environments/ # una carpeta y un estado por entorno
├── dev/
├── staging/
└── produccion/

La estructura debe ser idéntica entre entornos; lo único que cambia son los valores de las variables. En cuanto producción tiene recursos que staging no tiene, hemos perdido la capacidad de probar antes de desplegar.

IaC en el pipeline​

name: Terraform
on: [pull_request]

jobs:
plan:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: hashicorp/setup-terraform@v3

- run: terraform fmt -check -recursive
- run: terraform init
- run: terraform validate
- run: terraform plan -no-color

El patrón que usa casi todo el mundo:

  • En el pull request — plan, y publicar el resultado como comentario para que se revise
  • Al hacer merge a main — apply, con aprobación manual si es producción

Nunca un apply automático a producción sin que un humano haya leído el plan. Es la diferencia entre un error corregido en dos minutos y un incidente.

Además del plan, aquí encaja el análisis de seguridad de la infraestructura: buckets públicos, puertos abiertos, recursos sin cifrar. Lo tenemos con laboratorio en el capítulo de seguridad en IaC del curso de DevSecOps.

Errores habituales​

  • Cambios manuales en la consola. Rompen la correspondencia con el estado y el siguiente apply los deshace o falla.
  • Un único estado gigante. Un plan tarda diez minutos y cualquier error afecta a todo. Divide por entorno y por dominio.
  • Secretos en los ficheros .tf. Acaban en Git y en el estado. Usemos el gestor de secretos de nuestra nube.
  • No fijar la versión del proveedor. Un apply que funcionaba hace un mes deja de funcionar sin que nadie haya tocado nada.

Resumen​

  • IaC da a nuestra infraestructura las propiedades del código: historial, revisión y reversión
  • Terraform es el estándar; OpenTofu es su bifurcación libre y compatible
  • plan antes de apply, siempre, y buscamos la palabra destroy
  • El estado va remoto, cifrado, con bloqueo y uno por entorno
  • No abstraigamos en módulos hasta repetir el patrón dos o tres veces
  • En el pull request se hace plan; el apply a producción lo aprueba una persona
  • Los cambios manuales en la consola rompen todo el modelo

Recursos adicionales​


💡 Recordemos: si nuestra infraestructura no se puede recrear desde cero con un comando, no la tenemos como código. Tenemos documentación con suerte.

En el próximo capítulo vemos qué hacer con lo que hay dentro de esas máquinas.