Saltar al contenido

lección 2

Instalar LocalStack y levantar Step Functions en local

Configura LocalStack con Docker para emular AWS Step Functions en tu máquina sin coste ni cuenta de AWS.

55 min

### ¿Por qué necesitamos LocalStack?

AWS Step Functions es un servicio de Amazon Web Services que cuesta dinero real usar en la nube. Cada transición de estado cuesta $0.025 por cada 1.000 transiciones. Hay un nivel gratuito de 4.000 transiciones al mes — el Hello World de esta lección son 3 transiciones, así que en AWS de verdad también sería gratis. Pero el motivo bueno para LocalStack no es el céntimo: es no necesitar cuenta de AWS, ni tarjeta, ni acordarse de apagar nada.

LocalStack es una herramienta open source que emula los servicios de AWS en tu máquina local usando Docker. Es como tener un "mini AWS" en tu portátil. Puedes crear máquinas de estado, buckets S3, funciones Lambda — todo gratis, todo local, todo desechable. Si la lías, solo tienes que parar y reiniciar el contenedor.

LocalStack es la herramienta que me habría ahorrado cientos de euros cuando empecé con AWS. En mis primeros meses dejé un cluster EMR corriendo un fin de semana y el lunes tenía una factura de $200. Con LocalStack practicas sin riesgo financiero alguno.

### Paso 1: Levantar la pila de LocalStack

En la sección anterior (Cloud AWS, lección 2) ya montaste un docker-compose.yml con LocalStack y configuraste el AWS CLI. Vamos a reutilizar esa misma pila — ya tiene Step Functions habilitado. Entra en la carpeta donde la creaste y levántala:

1# Entra en la carpeta de la seccion anterior
2cd aws-localstack-lab
3
4# Levanta la pila (si no esta ya corriendo)
5docker compose up -d
6
7# Verifica que el contenedor esta arrancado
8docker ps | grep localstack

Si ya la tenias levantada, docker compose up -d no hace nada (es idempotente)

1# En PowerShell (Windows):
2cd aws-localstack-lab
3docker compose up -d
4docker ps | Select-String localstack

Equivalente en PowerShell — Select-String sustituye a grep

La imagen esta clavada en localstack/localstack:3.5 a proposito, y no es solo higiene. Desde la version 2026.03.0 (23 de marzo de 2026) LocalStack no arranca sin una cuenta y un token (LOCALSTACK_AUTH_TOKEN). El token del plan Hobby es gratis para uso no comercial, pero exige registrarse. Las etiquetas anteriores a esa fecha — como la 3.5 — siguen descargandose sin cuenta. Por eso usamos esa y no :latest.

### Paso 2: Verificar que Step Functions responde

El contenedor tarda 15-30 segundos en arrancar todos los servicios. Antes de seguir, comprueba que Step Functions esta operativo:

1# Comprobar que el servicio responde (espera 15-30s tras docker compose up)
2aws stepfunctions list-state-machines \
3 --endpoint-url http://localhost:4566
4
5# Respuesta esperada (lista vacia, porque aun no has creado nada):
6# {
7# "stateMachines": []
8# }

Si ves {"stateMachines": []}, Step Functions esta funcionando

### Troubleshooting: los tres errores que vas a ver

  1. 01."Could not connect to the endpoint URL: http://localhost:4566/" — el contenedor aun esta arrancando (tarda 15-30s). Espera y repite. Si sigue despues de un minuto, comprueba con docker ps que el contenedor existe.
  2. 02."Bind for 0.0.0.0:4566 failed: port is already allocated" — tienes otra instancia de LocalStack (o algo mas) ocupando el puerto. Busca con docker ps y baja la otra pila con docker compose down en su carpeta.
  3. 03."Unable to locate credentials" — no has configurado el AWS CLI. Ejecuta aws configure con las credenciales dummy (test/test, region eu-west-1) como hiciste en la S14 L02.

### Amazon States Language (ASL): el lenguaje de las maquinas de estado

Las maquinas de estado de Step Functions se definen en un lenguaje con nombre propio: Amazon States Language (ASL). Es un JSON con una estructura fija: un campo StartAt que dice por donde empieza, y un objeto States con los estados. Lo que vas a escribir a continuacion — StartAt, States, Type, Next, End — es ASL, y es el mismo tanto en AWS como en LocalStack. Lo que aprendes aqui es portable.

### Paso 3: Tu primera maquina de estado (Hello World)

Vamos a crear la maquina de estado mas simple posible: un solo paso que devuelve un mensaje. Esto confirma que todo el flujo funciona — crear, ejecutar, ver resultado.

1# Crear archivo hello-world.json con la definicion
2cat > hello-world.json << 'EOF'
3{
4 "Comment": "Mi primera maquina de estado",
5 "StartAt": "Saludar",
6 "States": {
7 "Saludar": {
8 "Type": "Pass",
9 "Result": {"mensaje": "Hola desde Step Functions en local"},
10 "End": true
11 }
12 }
13}
14EOF

Un unico estado tipo Pass — no ejecuta codigo, solo devuelve un resultado fijo

1# En PowerShell (Windows), no hay heredoc. Escribe el JSON con Set-Content:
2Set-Content hello-world.json '{
3 "Comment": "Mi primera maquina de estado",
4 "StartAt": "Saludar",
5 "States": {
6 "Saludar": {
7 "Type": "Pass",
8 "Result": {"mensaje": "Hola desde Step Functions en local"},
9 "End": true
10 }
11 }
12}'

Equivalente en PowerShell — Set-Content sustituye al heredoc

1# Crear la maquina de estado en LocalStack y guardar el ARN
2SM_ARN=$(aws stepfunctions create-state-machine \
3 --name "HelloWorldMachine" \
4 --definition file://hello-world.json \
5 --role-arn "arn:aws:iam::000000000000:role/DummyRole" \
6 --endpoint-url http://localhost:4566 \
7 --query 'stateMachineArn' --output text)
8
9echo "ARN de la maquina: $SM_ARN"
10# -> arn:aws:states:eu-west-1:000000000000:stateMachine:HelloWorldMachine

El role-arn es ficticio — LocalStack no valida permisos IAM en el plan Hobby

1# En PowerShell:
2$SM_ARN = (aws stepfunctions create-state-machine `
3 --name "HelloWorldMachine" `
4 --definition file://hello-world.json `
5 --role-arn "arn:aws:iam::000000000000:role/DummyRole" `
6 --endpoint-url http://localhost:4566 `
7 --query 'stateMachineArn' --output text)
8
9Write-Host "ARN de la maquina: $SM_ARN"

En PowerShell el backtick sustituye a la barra invertida para partir lineas

### Paso 4: Ejecutar y ver el resultado

1# Ejecutar la maquina de estado
2EXEC_ARN=$(aws stepfunctions start-execution \
3 --state-machine-arn "$SM_ARN" \
4 --input '{"nombre": "Alumno"}' \
5 --endpoint-url http://localhost:4566 \
6 --query 'executionArn' --output text)
7
8echo "ARN de la ejecucion: $EXEC_ARN"

start-execution lanza la maquina y devuelve un ARN de ejecucion unico

1# Ver el resultado de la ejecucion
2aws stepfunctions describe-execution \
3 --execution-arn "$EXEC_ARN" \
4 --endpoint-url http://localhost:4566 \
5 --query '[status, output]' --output text
6
7# Resultado esperado:
8# SUCCEEDED {"mensaje":"Hola desde Step Functions en local"}

--query extrae solo status y output, sin el JSON completo

Si perdiste el ARN de la ejecucion, recuperalo con list-executions:

1# Listar ejecuciones de una maquina
2aws stepfunctions list-executions \
3 --state-machine-arn "$SM_ARN" \
4 --endpoint-url http://localhost:4566

Devuelve todas las ejecuciones con su ARN, estado y fecha

### Paso 5: El historial — ver que hizo cada estado

describe-execution te da el resultado final, pero no te dice que paso por dentro. Para eso esta get-execution-history: lista cada evento de la ejecucion, estado por estado.

1# Ver el historial evento por evento
2aws stepfunctions get-execution-history \
3 --execution-arn "$EXEC_ARN" \
4 --endpoint-url http://localhost:4566 \
5 --query 'events[].[id, type, stateEnteredEventDetails.name]' \
6 --output table

El historial es tu herramienta de debugging principal para Step Functions

### La regla de Result: machaca la entrada

Una cosa que no es obvia y que te va a morder: cuando un estado Pass tiene un campo Result, ese resultado SUSTITUYE lo que recibia como entrada. No se mezcla, no se aniade — se machaca. En el Hello World no importa porque solo hay un estado. Pero en cuanto encadenes dos estados (como en el ejercicio de abajo), el segundo no va a ver lo que hizo el primero: solo ve su propio Result.

Cuando necesites que un estado ANADA su resultado a la entrada sin machacarla, usaras ResultPath — un campo que dice "pon mi resultado aqui dentro del JSON de entrada, en vez de sustituirlo". Lo veras en la siguiente leccion.

### Persistencia: por que tu maquina desaparece al reiniciar

Si haces docker compose down y vuelves a hacer docker compose up, tu HelloWorldMachine ya no estara. Eso es normal: la persistencia entre reinicios es una funcion de pago de LocalStack (plan Base, $39/mes). En el plan gratuito (Hobby), cada reinicio empieza de cero.

No es un problema: es la costumbre correcta. Ten un script que vuelva a crear lo que necesites:

1# crear-maquinas.sh — recrea la infraestructura de cero
2set -e
3aws stepfunctions create-state-machine \
4 --name HelloWorldMachine \
5 --definition file://hello-world.json \
6 --role-arn "arn:aws:iam::000000000000:role/DummyRole" \
7 --endpoint-url http://localhost:4566
8echo "Maquinas creadas"

Infraestructura como codigo: si se puede recrear con un script, no necesita guardarse

### Resumen: que acabas de hacer

  • Levantaste la pila de LocalStack que montaste en la seccion anterior (reutilizas, no duplicas).
  • Verificaste que Step Functions responde con list-state-machines.
  • Creaste tu primera maquina de estado con un Pass que devuelve un mensaje.
  • La ejecutaste y viste el resultado con describe-execution y --query.
  • Aprendiste a consultar el historial con get-execution-history.
  • Entendiste que Result machaca la entrada y que la persistencia es de pago.

A partir de ahora, cada vez que quieras practicar solo necesitas docker compose up -d en la carpeta de la S14 y ya tienes Step Functions disponible. Cuando termines, docker compose down para liberar recursos.

Guarda los ARN en variables de entorno. Un ARN copiado a mano es un ARN que un dia copiaras mal. export SM_ARN="..." despues de cada create, y $SM_ARN en cada comando posterior.

## ejercicios

[01]

Verificar que LocalStack esta funcionando

Escribe los comandos para verificar que LocalStack esta arrancado y que Step Functions responde. Verifica en tres niveles: contenedor, servicio y API.

Cargando editor...
[02]

Crear una maquina de estado con dos pasos

Crea un archivo JSON que defina una maquina de estado con dos estados Pass encadenados: "Inicio" que pasa al estado "Fin". Luego creala en LocalStack y ejecutala.

Cargando editor...
[03]

Escribir un docker-compose.yml de LocalStack de memoria

Sin mirar la leccion, escribe un docker-compose.yml que levante LocalStack con los servicios stepfunctions y s3 habilitados. Solo lo que necesitas, nada mas.

Cargando editor...
[04]

Hacer que la maquina de dos pasos devuelva AMBOS resultados

Tu maquina de dos pasos (Inicio -> Fin) devuelve solo {"paso": 2}. El {"paso": 1} de Inicio se pierde porque Result machaca. Modifica la definicion para que el resultado final contenga los dos: {"primero": {"paso": 1}, "segundo": {"paso": 2}}.

Cargando editor...

Regístrate para guardar tu progreso.

## comentarios

Reporta erratas, ayuda a otros o comparte tu opinión. Sé constructivo.

Inicia sesión para comentar y responder.

cargando comentarios...