Lección: Despliegue en la nube con AWS o Google Cloud
En esta lección, aprenderás a llevar tu microservicio Go, construido con Gorilla/Mux, desde tu máquina local a un entorno de producción robusto y escalable en la nube. Exploraremos las estrategias y servicios clave de AWS y Google Cloud, proporcionando ejemplos prácticos y configurables para que tu API esté lista para atender tráfico real. El enfoque será en contenedores, orquestación y servicios gestionados, que son el estándar para el despliegue moderno de microservicios de alto rendimiento.
Concepto Clave: Infraestructura como Código y Contenedores
Imagina que construir y desplegar tu aplicación es como mudarte de casa. Antes, tenías que contratar camiones, cargadores, y supervisar cada mueble manualmente (servidores físicos, configuraciones manuales). Hoy, con la nube y los contenedores, es como tener una casa prefabricada y un equipo de mudanzas robotizado. Tu casa (la aplicación) se empaqueta en un contenedor estandarizado (un Docker container) que incluye todo lo necesario para funcionar: el código Go, las librerías, el entorno de ejecución. Este contenedor se puede colocar en cualquier terreno (servidor en la nube) que tenga el software para ejecutarlo (un motor de contenedores), y funcionará exactamente igual.
La Infraestructura como Código (IaC) es el plano de ese terreno y los planos de la red de servicios (electricidad, agua). En lugar de hacer clics manuales en una consola web para crear servidores y bases de datos, escribes archivos de configuración (en YAML, JSON, o lenguajes como Terraform) que describen exactamente lo que necesitas. Estos archivos se pueden versionar, compartir y ejecutar de forma repetible, eliminando la deriva de configuración y los errores humanos. En el contexto de microservicios, esto significa que puedes definir tu clúster de contenedores, las reglas de balanceo de carga y las políticas de autoescalado en código, permitiendo despliegues consistentes y rápidos.
Tip: Piensa en tu contenedor Docker como la unidad de despliegue atómica de tu microservicio. Si funciona en tu portátil dentro de un contenedor, funcionará en cualquier nube. La IaC es el manual de instrucciones automatizado para construir el escenario donde ese contenedor vivirá.
Cómo funciona en la práctica: El Camino hacia la Nube
El proceso típico para desplegar un microservicio Go en la nube sigue un pipeline bien definido. Primero, tu código fuente se compila en un binario estático de Go, que es una ventaja enorme del lenguaje, ya que no tiene dependencias externas en tiempo de ejecución. Este binario se integra en una imagen de Docker ligera, típicamente basada en scratch o alpine. Esta imagen se sube a un registro de contenedores como Amazon ECR (Elastic Container Registry) o Google Container Registry (GCR).
Una vez la imagen está en el registro, un servicio de orquestación se encarga de desplegarla. En AWS, el servicio principal es Amazon ECS (Elastic Container Service) o Amazon EKS (Elastic Kubernetes Service). En Google Cloud, es Google Kubernetes Engine (GKE) o Cloud Run (un servicio sin servidor para contenedores). Estos servicios gestionan la ejecución de tus contenedores, asegurando que el número deseado de réplicas esté siempre en ejecución, distribuyendo el tráfico entre ellas mediante un balanceador de carga y reiniciando cualquier instancia que falle. Tu responsabilidad se centra en definir la tarea (task definition en ECS) o el despliegue (deployment en Kubernetes), y la nube maneja el resto.
Para una API REST, el flujo de tráfico es: Cliente -> Balanceador de Carga de Aplicación (AWS ALB/Google Cloud Load Balancer) -> Grupo de contenedores (en ECS/GKE) o instancia de Cloud Run -> Tu microservicio Go. La configuración del balanceador de carga para enrutar el tráfico a tu servicio es un paso crítico que se define como parte de la infraestructura como código.
Código en Acción: Dockerfile y Despliegue en AWS ECS
Vamos a construir un ejemplo completo, desde un Dockerfile optimizado hasta la definición de tarea para AWS ECS. Asumimos que tienes una estructura de proyecto Go básica con un módulo y tu archivo main.go usando Gorilla/Mux.
1. Dockerfile Optimizado para Go
Este Dockerfile utiliza una construcción multi-etapa para crear una imagen final extremadamente ligera y segura.
# Etapa 1: Construcción del binario
FROM golang:1.21-alpine AS builder
WORKDIR /app
COPY go.mod go.sum ./
RUN go mod download
COPY . .
# Compilación estática, crucial para imágenes basadas en 'scratch'
RUN CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -o main ./cmd/api
# Etapa 2: Imagen de ejecución mínima
FROM scratch
WORKDIR /root/
# Copiar el binario desde la etapa de construcción
COPY --from=builder /app/main .
# Copiar certificados CA (necesario para cualquier llamada HTTPS, ej: a una BD)
COPY --from=builder /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/
# Exponer el puerto en el que corre nuestra API
EXPOSE 8080
# Comando de entrada
CMD ["./main"]
2. Definición de Tarea para AWS ECS (task-definition.json)
Este archivo JSON le dice a ECS cómo ejecutar nuestro contenedor. Es el corazón de la configuración de despliegue.
{
"family": "go-api-microservice",
"networkMode": "awsvpc",
"requiresCompatibilities": ["FARGATE"],
"cpu": "256",
"memory": "512",
"executionRoleArn": "arn:aws:iam::123456789012:role/ecsTaskExecutionRole",
"taskRoleArn": "arn:aws:iam::123456789012:role/ecsTaskRole",
"containerDefinitions": [
{
"name": "api-container",
"image": "123456789012.dkr.ecr.us-east-1.amazonaws.com/go-api-repo:latest",
"portMappings": [
{
"containerPort": 8080,
"hostPort": 8080,
"protocol": "tcp"
}
],
"essential": true,
"environment": [
{
"name": "DATABASE_URL",
"value": "postgres://user:[email protected]:5432/db"
},
{
"name": "LOG_LEVEL",
"value": "info"
}
],
"logConfiguration": {
"logDriver": "awslogs",
"options": {
"awslogs-group": "/ecs/go-api-microservice",
"awslogs-region": "us-east-1",
"awslogs-stream-prefix": "ecs"
}
},
"healthCheck": {
"command": ["CMD-SHELL", "curl -f http://localhost:8080/health || exit 1"],
"interval": 30,
"timeout": 5,
"retries": 3,
"startPeriod": 60
}
}
]
}
3. Script de Despliegue Simplificado (deploy.sh)
Un script de Bash que automatiza los pasos comunes: construir, etiquetar, subir y actualizar el servicio.
#!/bin/bash
# Variables
AWS_REGION="us-east-1"
ECR_REPO="123456789012.dkr.ecr.us-east-1.amazonaws.com/go-api-repo"
CLUSTER_NAME="go-microservices-cluster"
SERVICE_NAME="go-api-service"
TASK_FAMILY="go-api-microservice"
# 1. Autenticación en ECR
aws ecr get-login-password --region $AWS_REGION | docker login --username AWS --password-stdin $ECR_REPO
# 2. Construir la imagen Docker
docker build -t go-api-microservice .
# 3. Etiquetar la imagen para ECR
docker tag go-api-microservice:latest $ECR_REPO:latest
# 4. Subir la imagen a ECR
docker push $ECR_REPO:latest
# 5. Registrar una nueva definición de tarea
aws ecs register-task-definition --cli-input-json file://task-definition.json --region $AWS_REGION
# 6. Actualizar el servicio para que use la nueva definición de tarea
aws ecs update-service --cluster $CLUSTER_NAME --service $SERVICE_NAME --task-definition $TASK_FAMILY --region $AWS_REGION --force-new-deployment
echo "Despliegue iniciado. Revisa los logs en CloudWatch."
Errores comunes y cómo evitarlos
Al desplegar en la nube, pequeños descuidos pueden causar grandes dolores de cabeza. Estos son algunos de los errores más frecuentes y cómo mitigarlos.
1. Imágenes de Docker demasiado grandes: Usar la imagen base golang:latest para la imagen final resulta en imágenes de más de 800MB. Esto ralentiza el despliegue y aumenta costos de almacenamiento y transferencia.
Solución: Siempre usa construcciones multi-etapa. Compila en una imagen con el toolchain de Go y copia el binario estático a una imagen mínima como scratch o alpine. La imagen final no debe superar los 20-30MB para un servicio Go típico.
2. Credenciales y secretos hardcodeados en el código o la imagen: Incluir contraseñas de base de datos, tokens de API o claves AWS directamente en el Dockerfile o el código fuente es un grave riesgo de seguridad.
Solución: Usa variables de entorno inyectadas en tiempo de ejecución. En AWS, utiliza AWS Secrets Manager o Parameter Store, y en GCP, Secret Manager. En tu definición de tarea (ECS) o despliegue (Kubernetes), referencia estos secretos de forma segura.
3. Falta de health checks o checks mal configurados: Si no defines un endpoint de salud (/health) o tu check no es significativo, el orquestador no podrá detectar si tu aplicación está realmente sana. Esto puede llevar a que el tráfico se envíe a contenedores que están "up" pero no funcionales.
Solución: Implementa siempre un endpoint de salud que verifique dependencias críticas (BD, caché). Configura el healthCheck en la definición del contenedor (como se muestra en el JSON de ejemplo) para que use ese endpoint. Ajusta startPeriod para dar tiempo al arranque de la app.
4. Permisos IAM insuficientes: El rol de ejecución de la tarea (executionRoleArn) en ECS necesita permisos para extraer la imagen de ECR y escribir logs en CloudWatch. Sin ellos, la tarea fallará silenciosamente en estado "PENDING".
Solución: Asegúrate de que el rol ecsTaskExecutionRole tenga adjuntas las políticas gestionadas AmazonECSTaskExecutionRolePolicy y los permisos necesarios para ECR. Siempre revisa los eventos de la tarea en la consola de ECS cuando falle.
5. No prepararse para el escalado: Desplegar con una sola instancia (réplica) y sin políticas de autoescalado es una receta para un downtime durante picos de tráfico.
Solución: Desde el inicio, configura tu servicio para que corra con al menos 2 réplicas en diferentes zonas de disponibilidad. Define políticas de autoescalado basadas en métricas de CPU o solicitudes por segundo. En ECS, usa Application Auto Scaling. En GKE, configura Horizontal Pod Autoscaler.
Checklist de dominio
Antes de considerar completada esta lección, asegúrate de poder verificar los siguientes puntos:
- Puedo construir una imagen Docker optimizada y multi-etapa para mi microservicio Go, resultando en una imagen final de menos de 50MB.
- Comprendo la diferencia entre servicios de orquestación gestionados: ECS/EKS en AWS vs GKE/Cloud Run en GCP, y puedo elegir el más apropiado para un caso de uso dado.
- Sé cómo subir una imagen a un registro de contenedores (ECR o GCR) y autenticarme desde mi entorno local y CI/CD.
- Puedo definir una tarea en ECS (task definition) o un despliegue en Kubernetes que incluya variables de entorno, checks de salud y configuración de logging.
- Entiendo cómo configurar un balanceador de carga de aplicación (ALB/GLB) para enrutar el tráfico HTTP/HTTPS a mi servicio de contenedores.
- Sé dónde y cómo gestionar secretos de forma segura (Secrets Manager, Secret Manager) para no hardcodear credenciales.
- Puedo interpretar las métricas básicas de mi servicio en CloudWatch (AWS) o Cloud Monitoring (GCP), como CPU, memoria, y conteo de solicitudes.
- He configurado un mínimo de dos réplicas para mi servicio y comprendo los conceptos básicos de autoescalado horizontal.