AWS Modulo 3: Lambda con Go

Source: Dev.to | Dec 14, 2025 (UTC)

Compilé para Linux sin Salir de mi Mac (y Costó $0.06)

📚 Serie: AWS Zero to Architect – Módulo 3

⏱️ Tiempo de lectura: 20 minutos

💻 Tiempo de implementación: 120 minutos

En los módulos anteriores configuramos AWS, Terraform e IAM. Ahora viene lo divertido: crear tu primera función Lambda en Go que cuesta centavos y es 3x más rápida que Python.

🤔 El Problema que Todos Tenemos

Escenario común:

“Tengo una API simple. ¿Monto un servidor 24/7 que me cuesta $50/mes aunque solo reciba 100 requests/día?”

Respuesta: NO. Usa Lambda.

⚡ Lambda Explicada: Food Truck vs Restaurant

Imagina que tienes un negocio de comida:

🏢 Restaurant (EC2 – Servidor Tradicional)

- Rentas local completo: $500/mes - Pagas luz/agua/gas 24/7 - Staff de tiempo completo - Si hay 3 clientes o 300, pagas lo mismo - Si hay demanda, el local se satura Costo fijo: $500/mes

Enter fullscreen mode

Exit fullscreen mode

🚚 Food Truck (Lambda – Serverless)

- Solo pagas cuando sirves un plato - $0.20 por cada 1 millón de platos - Sin staff fijo (AWS lo maneja) - Escala automáticamente - 3 clientes = $0.0006 - 300 clientes = $0.06 Costo variable: $0-$100/mes

Enter fullscreen mode

Exit fullscreen mode

¿Cuál tiene más sentido para una API que recibe tráfico esporádico?

💰 Pricing Real (Sin Marketing BS)

Mi Lambda Actual

100,000 requests/mes 128MB RAM 200ms promedio de duración Cálculo: Requests: 100,000 ÷ 1,000,000 × $0.20 = $0.02 Compute: 0.128GB × 0.2s × 100,000 × $0.0000166667 = $0.04 Total: $0.06/mes

Enter fullscreen mode

Exit fullscreen mode

Free Tier:

Traducción: Gratis para desarrollo y apps pequeñas.

🐹 ¿Por Qué Go y No Python/Node.js?

Benchmarks Honestos (Mis Propias Mediciones)

Lenguaje

Cold Start

Memory Used

Warm Invoke

Go

156ms

48MB

45ms

Node.js

342ms

89MB

89ms

Python

478ms

127MB

134ms

Java

1,240ms

186MB

203ms

Resultado: Go es 3x más rápido en cold start.

¿Qué es un Cold Start?

Primera invocación del día: 1. AWS crea un container → 100ms 2. Carga tu runtime (Node.js/Python) → 200ms 3. Carga tu código → 100ms Total: ~400ms Con Go: 1. AWS crea container → 100ms 2. Ejecuta binario → 50ms Total: ~150ms

Enter fullscreen mode

Exit fullscreen mode

Para el usuario final: Go responde en 150ms, Python en 400ms.

Ventaja de Costos

Python (512MB): $0.0000083 por 100ms Go (128MB): $0.0000021 por 100ms Ahorro: 75%

Enter fullscreen mode

Exit fullscreen mode

Con 1M requests/mes:

Diferencia: $62/mes × 12 meses = $744/año

🏗️ Arquitectura: Hexagonal en Serverless

¿Qué es Arquitectura Hexagonal?

Idea simple: La lógica de negocio NO debe depender de AWS.

❌ MAL (Acoplado a AWS): func CreateSession(userID string) { dynamodb.PutItem(...) // Directo a AWS } ✅ BIEN (Desacoplado): // Domain (puro Go, sin AWS) type Session struct { ID string UserID string } func NewSession(userID string) *Session { ... } // Adapter (traduce a AWS) type DynamoDBRepo struct { ... } func (r *DynamoDBRepo) Save(session *Session) { ... }

Enter fullscreen mode

Exit fullscreen mode

Ventajas:

Mi Estructura

go-hexagonal-auth/ ├── cmd/lambda/main.go # Lambda handler ├── internal/ │ ├── core/domain/ │ │ └── session.go # Lógica de negocio │ └── adapters/repository/ │ └── dynamodb_session.go # AWS adapter └── terraform/ └── lambda.tf # Infrastructure

Enter fullscreen mode

Exit fullscreen mode

💻 El Código que Importa

Domain Model (Sin AWS)

package domain import ( "time" "github.com/google/uuid" ) type Session struct { SessionID string UserID string ExpiresAt time.Time CreatedAt time.Time } func NewSession(userID string, ttl time.Duration) *Session { now := time.Now() return &Session{ SessionID: uuid.New().String(), UserID: userID, CreatedAt: now, ExpiresAt: now.Add(ttl), } } func (s *Session) IsValid() bool { return s.SessionID != "" && s.UserID != "" }

Enter fullscreen mode

Exit fullscreen mode

Nota: CERO imports de AWS. Lógica pura.

DynamoDB Adapter

package repository import ( "context" "github.com/aws/aws-sdk-go-v2/service/dynamodb" "go-hexagonal-auth/internal/core/domain" ) type DynamoDBRepo struct { client *dynamodb.Client tableName string } func (r *DynamoDBRepo) Save(ctx context.Context, session *domain.Session) error { // Conversión domain → DynamoDB item := map[string]interface{}{ "session_id": session.SessionID, "user_id": session.UserID, "expires_at": session.ExpiresAt.Unix(), } // PutItem en DynamoDB _, err := r.client.PutItem(ctx, &dynamodb.PutItemInput{ TableName: aws.String(r.tableName), Item: marshalMap(item), }) return err }

Enter fullscreen mode

Exit fullscreen mode

Lambda Handler

package main import ( "github.com/aws/aws-lambda-go/lambda" "go-hexagonal-auth/internal/core/domain" "go-hexagonal-auth/internal/adapters/repository" ) func handler(ctx context.Context, request APIGatewayRequest) (Response, error) { // 1. Parse input var req CreateSessionRequest json.Unmarshal([]byte(request.Body), &req) // 2. Create session (domain logic) session := domain.NewSession(req.UserID, 24*time.Hour) // 3. Save (adapter) repo := repository.NewDynamoDBRepo(ctx) repo.Save(ctx, session) // 4. Response return Response{ StatusCode: 201, Body: json.Marshal(session), }, nil } func main() { lambda.Start(handler) }

Enter fullscreen mode

Exit fullscreen mode

Flujo:

  1. Parse JSON
  2. Lógica de negocio (domain)
  3. Persistencia (adapter)
  4. Respuesta

🔨 Compilación Cross-Platform (La Magia de Go)

El Problema

Desarrollo en: macOS ARM64 (M1/M2/M3) Lambda ejecuta: Linux ARM64 ¿Cómo compilo para Linux sin salir de macOS?

Enter fullscreen mode

Exit fullscreen mode

La Solución de Go

# Un solo comando GOOS=linux GOARCH=arm64 go build -o bootstrap ./cmd/lambda

Enter fullscreen mode

Exit fullscreen mode

Eso es todo.

No necesitas:

Go compila nativamente para otras plataformas.

Makefile (Automatización)

build: GOOS=linux GOARCH=arm64 CGO_ENABLED=0 \ go build -ldflags="-s -w" \ -o build/bootstrap ./cmd/lambda zip: build cd build && zip lambda.zip bootstrap

Enter fullscreen mode

Exit fullscreen mode

Comandos:

make build # Compila para Lambda make zip # Crea lambda.zip

Enter fullscreen mode

Exit fullscreen mode

Tamaños:

build/bootstrap: 7.2 MB (sin comprimir) build/lambda.zip: 2.8 MB (comprimido)

Enter fullscreen mode

Exit fullscreen mode

Flags Importantes

-ldflags="-s -w" # Reduce tamaño 30-40% CGO_ENABLED=0 # Binario estático (sin deps C)

Enter fullscreen mode

Exit fullscreen mode

🚀 Deploy con Terraform

Lambda Configuration

resource "aws_lambda_function" "create_session" { filename = "../build/lambda.zip" function_name = "go-hexagonal-auth-dev-create-session" role = aws_iam_role.lambda_execution.arn handler = "bootstrap" runtime = "provided.al2023" # ARM64 (Graviton2 - 20% más barato) architectures = ["arm64"] memory_size = 128 timeout = 10 environment { variables = { DYNAMODB_TABLE_NAME = aws_dynamodb_table.sessions.name } } }

Enter fullscreen mode

Exit fullscreen mode

Características:

¿Por qué ARM64?

Benchmarks:

Arquitectura

Precio/GB-seg

Performance

Relación

x86_64

$0.0000166667

Baseline

1.0x

ARM64

$0.0000133334

+10-15%

1.15x

Resultado: ARM64 es 20% más barato Y 10% más rápido.

Deploy

# 1. Compilar make build zip # 2. Deploy cd terraform terraform apply

Enter fullscreen mode

Exit fullscreen mode

Tiempo de deploy: ~15 segundos

🧪 Testing (La Parte Satisfactoria)

Test Básico

# Invocar Lambda aws lambda invoke \ --function-name go-hexagonal-auth-dev-create-session \ --payload '{"body": "{\"user_id\": \"test-123\"}"}' \ response.json # Ver resultado cat response.json | jq .

Enter fullscreen mode

Exit fullscreen mode

Respuesta:

{ "statusCode": 201, "body": { "session_id": "f7a3b2c1-4d5e-6789-abcd-ef0123456789", "user_id": "test-123", "expires_at": 1734393600, "message": "Session created successfully" } }

Enter fullscreen mode

Exit fullscreen mode

✅ Primera invocación: 156ms (cold start)

✅ Segunda invocación: 45ms (warm)

Verificar en DynamoDB

SESSION_ID=$(cat response.json | jq -r '.body.session_id') aws dynamodb get-item \ --table-name sessions \ --key "{\"session_id\": {\"S\": \"$SESSION_ID\"}}"

Enter fullscreen mode

Exit fullscreen mode

¡Ahí está la sesión! 🎉

CloudWatch Logs

aws logs tail /aws/lambda/go-hexagonal-auth-dev-create-session --follow

Enter fullscreen mode

Exit fullscreen mode

Output:

START RequestId: abc-123 Received request: {"body": "{\"user_id\":\"test-123\"}"} Session created: f7a3b2c1... for user: test-123 END RequestId: abc-123 REPORT Duration: 156ms Memory: 48MB

Enter fullscreen mode

Exit fullscreen mode

Métricas:

🎯 Resultados Reales

Performance

Cold Start (primera invocación del día):

Warm Invocations:

Costos (100k requests/mes)

Go Lambda: 128MB × 200ms × 100,000 requests = $0.06/mes Node.js Lambda (mismo workload): 256MB × 200ms × 100,000 requests = $0.12/mes Ahorro: $0.06/mes × 12 = $0.72/año

Enter fullscreen mode

Exit fullscreen mode

Multiplicado por 10 Lambdas: $7.20/año de ahorro.

🆘 Problemas que Tuve (Y Cómo los Resolví)

Error: “Runtime.ExitError exit status 2”

Causa: Faltaba import de net/http en main.go

Fix:

import ( "net/http" // ← Agregar esto // ... otros imports )

Enter fullscreen mode

Exit fullscreen mode

Error: “DYNAMODB_TABLE_NAME not set”

Causa: Variable de entorno mal configurada en Terraform

Fix:

environment { variables = { DYNAMODB_TABLE_NAME = aws_dynamodb_table.sessions.name # NO: DYNAMODB_TABLE_SESSIONS } }

Enter fullscreen mode

Exit fullscreen mode

Error: “exec format error”

Causa: Compilé para x86 en lugar de ARM64

Fix:

# Verificar arquitectura file build/bootstrap # Debe decir: ARM aarch64 # Recompilar GOARCH=arm64 make build

Enter fullscreen mode

Exit fullscreen mode

🔐 Seguridad: ¿Es Pública Mi Lambda?

Respuesta Corta: NO

Situación actual (Módulo 3):

Internet → ❌ NO PUEDE ACCEDER AWS CLI con credenciales → ✅ PUEDE INVOCAR

Enter fullscreen mode

Exit fullscreen mode

Solo es invocable con:

Nadie en internet puede ejecutarla.

Módulo 4: API Gateway (Próximo)

Ahí sí la haremos pública con:

💡 Lo Que Aprendí

  1. Go es RÁPIDO para serverless

  2. Compilación cross-platform es magia

  3. Arquitectura Hexagonal vale la pena

  4. ARM64 > x86_64

  5. Terraform > ClickOps

📊 Comparación Final

Característica

EC2 t3.micro

Lambda Go

Costo base

$7.50/mes

$0.00

Escalado

Manual

Automático

Cold start

N/A

156ms

Warm invoke

N/A

45ms

Mantenimiento

Tú

AWS

100k req/mes

$7.50

$0.06

Ganador: Lambda (para tráfico esporádico)

🎓 Lo Que Lograste

Si llegaste hasta aquí e implementaste todo:

Y todo por < $0.10/mes. 🎉

🚀 Próximo Paso: API Gateway

En el Módulo 4 vamos a:

La Lambda será accesible desde cualquier lugar (con controles de seguridad).

📦 Código Completo

Todo el código está en GitHub:

Un microservicio de autenticación robusto, escalable y listo para producción, escrito en Go siguiendo principios de Arquitectura Hexagonal.

Diseñado con la seguridad como prioridad, implementando las mejores prácticas de OWASP para la gestión de identidad, sesiones y protección contra ataques.

Este no es solo un login básico. Este proyecto implementa capas de defensa en profundidad:

Read original article →

Залишити відповідь

Ваша e-mail адреса не оприлюднюватиметься. Обов’язкові поля позначені *

Читайте також