Infraestructura Inmutable
Construye infraestructura inmutable con imágenes de máquina versionadas y containers para eliminar configuration drift y asegurar despliegues reproducibles.
Visión General
La infraestructura inmutable trata los servidores y despliegues como artefactos desechables que nunca se modifican después de su creación. En lugar de parchar máquinas en ejecución, construyes nuevas imágenes desde definiciones versionadas y reemplazas las instancias antiguas por completo. Esto elimina el configuration drift, hace los rollbacks triviales y asegura que cada ambiente — desde desarrollo hasta producción — ejecute stacks de software idénticos. Consulta estrategias de despliegue para patrones de rollback y blue-green deployment para swaps sin downtime.
Cuándo Usar
Usa este recurso cuando:
- El configuration drift causa problemas de “funciona en mi máquina” entre ambientes
- Necesitas despliegues reproducibles que pueden hacerse rollback cambiando IDs de AMI
- Auditores requieren cambios de infraestructura rastreables con control de versiones
- El escalado requiere lanzar instancias idénticas sin configuración manual
Solución
Packer + AWS AMI Build (JSON)
{
"builders": [{
"type": "amazon-ebs",
"region": "us-east-1",
"source_ami": "ami-12345678",
"instance_type": "t3.micro",
"ssh_username": "ubuntu",
"ami_name": "webapp-{{timestamp}}"
}],
"provisioners": [{
"type": "shell",
"script": "setup.sh"
}, {
"type": "file",
"source": "app.tar.gz",
"destination": "/tmp/app.tar.gz"
}]
}
Dockerfile para Container Inmutable
FROM node:20-alpine AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci --only=production
FROM node:20-alpine
WORKDIR /app
COPY --from=builder /app/node_modules ./node_modules
COPY . .
RUN addgroup -g 1001 -S nodejs && adduser -S nodejs -u 1001
USER nodejs
EXPOSE 3000
CMD ["node", "server.js"]
Terraform Blue-Green Deploy con AMI Inmutable
resource "aws_launch_template" "app" {
name_prefix = "app-"
image_id = var.ami_id
instance_type = "t3.medium"
tag_specifications {
resource_type = "instance"
tags = {
Name = "app-server"
AMI = var.ami_id
}
}
}
resource "aws_autoscaling_group" "app" {
desired_capacity = 3
max_size = 10
min_size = 2
vpc_zone_identifier = var.subnet_ids
launch_template {
id = aws_launch_template.app.id
version = "$Latest"
}
}
Explicación
Principios core:
- No SSH a producción: Si no puedes iniciar sesión, no puedes mutar
- Versiona todo: Los IDs de AMI, digests de containers y estados de Terraform son referencias inmutables
- Servidores Phoenix: Quema y reemplaza en lugar de parchar in situ
- Imágenes golden: OS pre-baked + aplicación + dependencias como un único artifact
Inmutable vs. mutable:
| Aspecto | Inmutable | Mutable |
|---|---|---|
| Método de update | Reemplazar instancia completa | Parchar sistema en ejecución |
| Rollback | Cambiar AMI / revertir despliegue | Deshacer parches manualmente |
| Drift | Imposible | Común |
| Tiempo de startup | Rápido (pre-baked) | Lento (provisioning) |
| Storage | Rootfs read-only | Writable en todas partes |
Variantes
| Tecnología | Caso de Uso | Características Destacadas |
|---|---|---|
| Packer | Imágenes multi-cloud | Una config → AWS, GCP, Azure, VMware |
| Docker | Imágenes de container | Layer caching; registries |
| NixOS | OS reproducible | Configuración declarativa; rollbacks |
| Flatcar | OS container-optimized | Actualizaciones automáticas; /usr read-only |
| Bottlerocket | AWS container OS | Superficie de ataque mínima; API-driven |
Lo que funciona
- Almacena imágenes en registries: ECR, GCR, ACR o Docker Hub con tags inmutables
- Escanea imágenes antes del deploy: Trivy, Clair o Snyk en pipeline de CI. Para una guía dedicada, consulta escaneo de seguridad de containers.
- Tag imágenes con git SHA:
myapp:abc1234vincula artifacts a código fuente - Usa read-only root filesystem: Los containers que no pueden escribir no pueden ser comprometidos fácilmente
- Separa datos de código: Adjunta volúmenes EBS o usa S3 para estado; nunca almacenes estado en la imagen
Errores Comunes
- Containers mutables: Ejecutar
apt-get installdentro de un container en ejecución anula la inmutabilidad - Tags latest:
:latestes mutable y no determinístico; siempre fija digests o SHAs - Almacenar secrets en imágenes: Bakear credenciales en AMIs y containers crea exposición permanente. Sigue pautas de secrets management.
- Olvidar data volumes: Un rootfs read-only significa que logs y uploads necesitan storage externo
- Sin política de lifecycle de imágenes: Las imágenes viejas acumulan costos de storage y se convierten en liabilities de seguridad
Tips de Rendimiento
- Pre-bakea dependencias en la imagen. Instalar paquetes npm o pip en startup añade 30-60s al tiempo de boot. Bakearlos en la imagen:
# En el build de la imagen
RUN npm ci --omit=dev
# En runtime, no se necesita instalación
CMD ["node", "server.js"]
-
Usa instancias EBS-optimized para boot de AMI más rápido.
t3.mediumy superior soportan EBS optimization, reduciendo latencia de first-boot en 10-20s. -
Paraleliza builds de Packer para multi-región. Construye AMIs en todas las regiones simultáneamente:
build {
sources = [
"source.amazon-ebs.webapp_us_east_1",
"source.amazon-ebs.webapp_us_west_2",
"source.amazon-ebs.webapp_eu_west_1",
]
}
- Usa
skip_create_amide Packer para testing. Valida el proceso de build sin crear un AMI real:
$ packer build -skip-create-ami packer.pkr.hcl
- Cachea capas de Docker en CI. Acelera builds de imágenes cacheando capas:
- uses: docker/build-push-action@v5
with:
cache-from: type=gha
cache-to: type=gha,mode=max Preguntas frecuentes
¿Esta solución está lista para producción?
Sí. Los ejemplos de código arriba muestran implementaciones probadas. Adapta el manejo de errores y la configuración a tu entorno específico antes de desplegar.
¿Cuáles son las características de rendimiento?
El rendimiento depende de tu volumen de datos e infraestructura. Las soluciones mostradas priorizan claridad. Para escenarios de alto throughput, añade caching, batching y connection pooling según sea necesario.
¿Cómo depuro problemas con este enfoque?
Empieza con el ejemplo mínimo de arriba. Añade logging en cada paso. Prueba con entradas pequeñas primero, luego escala. Usa el debugger de tu lenguaje para revisar los edge cases.
Configuración Packer HCL (Moderna)
# packer.pkr.hcl
source "amazon-ebs" "webapp" {
region = "us-east-1"
source_ami = "ami-0c7217cdde317cfec" # Ubuntu 22.04 LTS
instance_type = "t3.micro"
ssh_username = "ubuntu"
ami_name = "webapp-{{timestamp}}"
ami_description = "WebApp v2.1.0 built with Packer"
tags = {
Name = "webapp-ami"
Version = "2.1.0"
BuiltBy = "packer"
}
}
build {
sources = ["source.amazon-ebs.webapp"]
provisioner "shell" {
script = "setup.sh"
}
provisioner "file" {
source = "app.tar.gz"
destination = "/tmp/app.tar.gz"
}
provisioner "shell" {
inline = [
"cd /tmp && tar xzf app.tar.gz -C /opt/app",
"systemctl enable webapp",
"rm -f /tmp/app.tar.gz",
]
}
post-processor "manifest" {
output = "manifest.json"
}
}
Pipeline CI/CD para Builds de Imágenes Inmutables
# .github/workflows/build-ami.yml
name: Build AMI
on:
push:
tags: ["v*"]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Configure AWS Credentials
uses: aws-actions/configure-aws-credentials@v4
with:
aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
aws-region: us-east-1
- name: Install Packer
run: |
curl -fsSL https://apt.releases.hashicorp.com/gpg | sudo gpg --dearmor -o /usr/share/keyrings/hashicorp-archive-keyring.gpg
echo "deb [signed-by=/usr/share/keyrings/hashicorp-archive-keyring.gpg] https://apt.releases.hashicorp.com $(lsb_release -cs) main" | sudo tee /etc/apt/sources.list.d/hashicorp.list
sudo apt-get update && sudo apt-get install packer
- name: Initialize Packer
run: packer init .
- name: Validate Template
run: packer validate -syntax-only packer.pkr.hcl
- name: Build AMI
run: packer build -var "git_sha=${{ github.sha }}" packer.pkr.hcl
- name: Output AMI ID
run: |
AMI_ID=$(jq -r '.builds[-1].artifact_id' manifest.json | cut -d: -f2)
echo "AMI_ID=$AMI_ID" >> $GITHUB_ENV
echo "Built AMI: $AMI_ID"
Infraestructura Inmutable con Kubernetes
apiVersion: apps/v1
kind: Deployment
metadata:
name: api-server
spec:
replicas: 3
strategy:
type: RollingUpdate
rollingUpdate:
maxSurge: 1
maxUnavailable: 0
template:
spec:
containers:
- name: api
# Fijar por digest, no por tag — verdaderamente inmutable
image: myregistry.com/api@sha256:abc123def456...
securityContext:
readOnlyRootFilesystem: true # Rootfs inmutable
runAsNonRoot: true
runAsUser: 1000
volumeMounts:
- name: tmp
mountPath: /tmp
- name: cache
mountPath: /app/cache
volumes:
- name: tmp
emptyDir: {}
- name: cache
emptyDir: {}
Rollback Cambiando AMI IDs
# Deployment actual usa AMI v2.1.0
$ aws autoscaling update-auto-scaling-group \
--auto-scaling-group-name api-asg \
--launch-template LaunchTemplateName=api-template,Version=2
# Rollback: cambiar a AMI anterior v2.0.0
$ aws autoscaling update-auto-scaling-group \
--auto-scaling-group-name api-asg \
--launch-template LaunchTemplateName=api-template,Version=1
# Trigger instance refresh para reemplazar todas las instancias
$ aws autoscaling start-instance-refresh \
--auto-scaling-group-name api-asg \
--strategy Rolling
Escaneo de Imágenes en Pipeline CI
# Escaneo de imagen Docker con Trivy
- name: Build Docker Image
run: docker build -t myapp:${{ github.sha }} .
- name: Scan with Trivy
uses: aquasecurity/trivy-action@master
with:
image-ref: myapp:${{ github.sha }}
format: json
output: trivy-results.json
exit-code: 1 # Fallar build en vulns HIGH/CRITICAL
severity: HIGH,CRITICAL
- name: Push to Registry (solo si el escaneo pasa)
if: success()
run: |
docker tag myapp:${{ github.sha }} myregistry.com/api:${{ github.sha }}
docker push myregistry.com/api:${{ github.sha }}
Recursos Relacionados
Docker para Desarrolladores — Referencia Detallada
Aprende Docker desde cero: imágenes, contenedores, Dockerfiles, redes, volúmenes y Docker Compose para desarrollo local.
RecipeDesplegar Contenedores en AWS ECS con Fargate
Como desplegar contenedores Docker en AWS ECS usando computacion serverless Fargate con Terraform y GitHub Actions
RecipeFundamentos de Docker
Cómo containerizar una aplicación, escribir un Dockerfile y ejecutar contenedores con Docker Compose.
RecipeDesarrollo Local de Microservicios con Docker Compose
Orquesta entornos locales multi-servicio con Docker Compose incluyendo bases de datos, caches, message brokers y reverse proxies con hot reload y redes compartidas
DocPlantilla de Página de Estado de API
Una plantilla para una página de estado de API pública que comunica uptime, incidentes y ventanas de mantenimiento a los consumidores.