CloudBridge

Abrir la caja negra

Anatomía de un template — CloudFormation

De la caja negra al código

La Semana 1 terminó con la aplicación en línea, desplegada a partir de un archivo que tratamos como una caja negra: taller-semana1.yaml. Esta semana se abre esa caja.

El objetivo no es memorizar la sintaxis de CloudFormation, sino saber leer un template: reconocer qué describe, encontrar dónde se define cada recurso, y entender cómo se conectan entre sí. Esa lectura es lo que convierte la infraestructura de algo que "alguien configuró una vez" en algo que el equipo entiende, revisa, y modifica.

Por qué infraestructura como código

Configurar recursos a mano desde la consola —click-ops— funciona una vez. El problema aparece después: nadie recuerda exactamente qué se configuró, no hay registro de los cambios, y reproducir el mismo ambiente en otra región o cuenta significa repetir decenas de clics sin garantía de que el resultado sea idéntico.

La infraestructura como código (IaC) resuelve esto describiendo los recursos en un archivo de texto versionado. El archivo es la fuente de verdad: se revisa en un pull request, se guarda en el repositorio junto al código, y produce siempre el mismo ambiente. La diferencia es la misma que vimos entre subir archivos a mano y hacer git push: un proceso reproducible en lugar de una serie de pasos manuales.

Click-ops vs. infraestructura como código

Click-opsInfraestructura como código
Pasos manuales en la consolaRecursos descritos en un archivo
Sin registro de cambiosVersionado en git
Difícil de reproducirIdéntico en cada lanzamiento
El conocimiento vive en una personaEl conocimiento vive en el repositorio

Las secciones de un template

Un template de CloudFormation es un archivo YAML (o JSON) con un conjunto de secciones de nivel superior. Solo una es obligatoria —Resources—; las demás son opcionales y aparecen según lo que el template necesite.

SecciónPara qué sirve
ResourcesObligatoria. Los recursos a crear: la tabla, el clúster, el balanceador.
ParametersValores que se proveen al lanzar el stack (por ejemplo, el URI de la imagen).
OutputsValores que el stack expone al terminar (por ejemplo, la URL del ALB).
MappingsTablas de búsqueda fijas (por ejemplo, una AMI distinta por región).
ConditionsReglas que activan o desactivan recursos según los parámetros.

En la Semana 1 ya se interactuó con tres de ellas sin saberlo: se completó un parámetro (el URI de la imagen), y se leyó un output (la URL del ALB) que el template expuso al llegar a CREATE_COMPLETE.

Recursos: nombre lógico y nombre físico

Cada recurso dentro de Resources tiene un nombre lógico (logical ID): el identificador que se le da dentro del template. CloudFormation, al crear el recurso, le asigna además un nombre físico: el identificador real en AWS.

Resources:
  TablaApp:                          # nombre lógico (se elige en el template)
    Type: AWS::DynamoDB::Table
    Properties:
      TableName: taller-datos        # nombre físico (opcional)
      BillingMode: PAY_PER_REQUEST

El nombre lógico (TablaApp) es cómo el resto del template se refiere a este recurso. El nombre físico (taller-datos) es cómo aparece en la consola de DynamoDB. Si no se especifica un nombre físico, CloudFormation genera uno único automáticamente —una práctica habitual, porque evita colisiones de nombres al lanzar el mismo template varias veces.

Funciones intrínsecas: conectar los recursos

Los recursos rara vez son independientes: el servicio ECS necesita el nombre del clúster, el ALB necesita el ID de la subred, la task definition necesita el URI de la imagen. Las funciones intrínsecas permiten referirse a un valor que solo se conoce cuando el stack se crea, sin escribirlo a mano.

Las tres más comunes:

!Ref y !GetAtt son la forma corta de Fn::Ref y Fn::GetAtt; ambas notaciones son equivalentes y se encontrará con las dos al leer templates de la documentación de AWS.

¿Por qué YAML y no JSON?

CloudFormation acepta los dos formatos, y son intercambiables. Este taller usa YAML porque admite comentarios (líneas con #), es más compacto, y ofrece la sintaxis corta de las funciones intrínsecas (!Ref en lugar de { "Ref": "..." }). JSON sigue siendo común en templates generados por herramientas. Saber leer ambos es útil; para escribir a mano, YAML es más cómodo.


Leer el template paso a paso

El template de la Semana 1, por dentro

Ya se conocen las secciones de un template. Ahora se recorre el archivo real que desplegó la aplicación, taller-semana1.yaml, recurso por recurso, conectando cada bloque con lo que se vio en la consola la semana pasada.

Abrir el archivo taller-semana1.yaml en el editor de texto —es el mismo que se subió a CloudFormation. Se leerá de arriba hacia abajo.

El encabezado y los parámetros

El template comienza declarando su versión y los parámetros que recibe al lanzarse:

AWSTemplateFormatVersion: "2010-09-09"
Description: Taller AWS DevOps — Semana 1

Parameters:
  ImageUri:
    Type: String
    Description: URI de la imagen en ECR, con etiqueta.

Aquí está el parámetro que se completó al crear el stack: ImageUri. Es el único valor que el template no puede conocer por sí solo —depende de la imagen que el build publicó en ECR— y por eso se pide al lanzarlo.

La tabla de DynamoDB

Resources:
  TablaApp:
    Type: AWS::DynamoDB::Table
    Properties:
      BillingMode: PAY_PER_REQUEST
      AttributeDefinitions:
        - AttributeName: id
          AttributeType: S
      KeySchema:
        - AttributeName: id
          KeyType: HASH

Este es el recurso que se vio en DynamoDB → Tables. Note que no tiene TableName: CloudFormation le asignó un nombre físico generado, lo que permite lanzar el template varias veces sin colisiones.

El clúster y el servicio ECS

  ClusterApp:
    Type: AWS::ECS::Cluster

  ServicioApp:
    Type: AWS::ECS::Service
    Properties:
      Cluster: !Ref ClusterApp
      DesiredCount: 1
      LaunchType: FARGATE
      TaskDefinition: !Ref TareaApp

Aquí aparece la primera función intrínseca importante: Cluster: !Ref ClusterApp conecta el servicio con el clúster creado más arriba, sin escribir su nombre a mano. Lo mismo TaskDefinition: !Ref TareaApp. Estos son los recursos que se vieron en ECS → Clusters: el clúster, y dentro de él el servicio con su tarea en estado RUNNING.

La task definition y la imagen

  TareaApp:
    Type: AWS::ECS::TaskDefinition
    Properties:
      RequiresCompatibilities: [FARGATE]
      Cpu: "256"
      Memory: "512"
      ContainerDefinitions:
        - Name: app
          Image: !Ref ImageUri
          PortMappings:
            - ContainerPort: 8080

Esta es la pieza clave: Image: !Ref ImageUri es donde el URI pegado al lanzar el stack se convierte en la imagen que ejecuta el contenedor. El parámetro del encabezado llega hasta aquí a través de una sola función intrínseca.

El balanceador y la salida

  BalanceadorApp:
    Type: AWS::ElasticLoadBalancingV2::LoadBalancer
    Properties:
      Type: application

Outputs:
  ALBUrl:
    Description: URL pública de la aplicación
    Value: !Sub "http://${BalanceadorApp.DNSName}"

El output ALBUrl es el valor que se copió de la pestaña Outputs. La función !Sub construye la URL completa insertando el atributo DNSName del balanceador —un valor que solo existe una vez que AWS crea el ALB.

El template completo tiene más recursos

Por claridad, los bloques anteriores omiten los grupos de seguridad, los roles de IAM, el listener del ALB, el target group, y la configuración de red (subredes, VPC). El archivo real los incluye: cada uno aparece como un recurso más en Resources, conectado a los demás con !Ref y !GetAtt. En la sección de buenas prácticas se verá cómo navegar un template largo sin perderse.


Ejercicio 9 — Seguir el rastro de la imagen

Abrir taller-semana1.yaml y seguir el recorrido del URI de la imagen: desde el parámetro que lo recibe, hasta el recurso que finalmente lo usa para ejecutar el contenedor. Identificar el nombre del parámetro, la función intrínseca que lo transporta, y el recurso de destino.


Actualizar un stack con seguridad

Cambiar lo que ya está desplegado

Un template no se lanza una sola vez y se olvida: la infraestructura cambia. Se ajusta la memoria de un contenedor, se agrega un recurso, se sube el número de tareas. En CloudFormation, cambiar el ambiente no significa borrarlo y recrearlo: significa actualizar el stack con una nueva versión del template, y dejar que CloudFormation calcule qué modificar.

La pregunta crítica al actualizar es: ¿qué va a hacer exactamente este cambio antes de aplicarlo? Esa pregunta la responde un change set.

Change sets: ver antes de aplicar

Un change set es una vista previa. Se sube el template modificado, y CloudFormation calcula la diferencia contra el estado actual sin tocar nada todavía. El resultado es una lista de acciones: qué recursos se modifican, cuáles se agregan, cuáles se eliminan, y —lo más importante— cuáles requieren reemplazo.

Este es el ciclo de reconciliación de CloudFormation: comparar el estado deseado (el template) contra el estado real (el stack), calcular la diferencia, y aplicar solo lo necesario para que coincidan.

flowchart LR
  T["Template<br/>(estado deseado)"] --> D{"CloudFormation<br/>compara"}
  S[("Stack actual<br/>(estado real)")] --> D
  D -->|change set| P["Plan de cambios:<br/>crear / modificar / eliminar"]
  P --> A["Aplicar al stack"]
  A --> S

El estado real converge hacia el deseado en cada actualización. La misma idea que encontrará en ECS (un servicio reconcilia las tareas hacia el DesiredCount) y, en general, en toda la infraestructura declarativa.

Tres formas de aplicar un cambio

AcciónQué ocurre
ModificaciónEl recurso se actualiza en sitio, sin interrupción.
Sin interrupciónEl cambio no afecta el servicio (por ejemplo, una etiqueta).
ReemplazoEl recurso se destruye y se crea de nuevo — puede causar interrupción.

El reemplazo es el que hay que vigilar: cambiar ciertas propiedades (por ejemplo, el nombre físico de una tabla) obliga a CloudFormation a crear un recurso nuevo y borrar el anterior. El change set lo marca explícitamente con Replacement: True, dando la oportunidad de detenerse antes de perder datos.

Práctica guiada: escalar la aplicación a dos tareas

Esta práctica aumenta el número de contenedores en ejecución de uno a dos, modificando el template y aplicando el cambio con un change set.

Modificar el template

  1. Abrir taller-semana1.yaml en el editor.

  2. Localizar el recurso ServicioApp (tipo AWS::ECS::Service).

  3. Cambiar la propiedad DesiredCount de 1 a 2:

    ServicioApp:
      Type: AWS::ECS::Service
      Properties:
        DesiredCount: 2        # antes: 1
    
  4. Guardar el archivo.

Crear el change set

  1. En la consola de AWS, abrir CloudFormation y seleccionar el stack taller-<su-nombre>.
  2. Pulsar Stack actions → Create change set for current stack.
  3. Seleccionar Replace existing template → Upload a template file, y subir el taller-semana1.yaml modificado.
  4. Avanzar por las pantallas (los parámetros siguen igual) hasta llegar a la vista del change set. Pulsar Create change set y asignarle un nombre, por ejemplo escalar-a-dos.

Revisar y aplicar

  1. CloudFormation calcula la diferencia y muestra la tabla de cambios. Buscar la fila correspondiente a ServicioApp: la acción debe ser Modify, y la columna Replacement debe decir False —el servicio se actualiza en sitio, sin recrearse.
  2. Confirmar que ningún otro recurso aparece como True en Replacement.
  3. Pulsar Execute change set. CloudFormation aplica solo ese cambio.
  4. En la pestaña Events, seguir la actualización hasta que el stack vuelva a UPDATE_COMPLETE.

Verificar el resultado

  1. Abrir ECS → Clusters → el clúster → el servicio. La cuenta de tareas deseadas ahora es 2, y se verán dos tareas en estado RUNNING.
  2. En EC2 → Target Groups, el target group del ALB muestra dos destinos sanos (healthy): el balanceador ya reparte tráfico entre ambas tareas.

Cuando una actualización falla: el rollback

Si un cambio no puede aplicarse —un valor inválido, un permiso faltante— CloudFormation no deja el stack a medias. Inicia un rollback: deshace los cambios ya aplicados y devuelve el stack al último estado bueno conocido. El estado pasa por UPDATE_ROLLBACK_IN_PROGRESS y termina en UPDATE_ROLLBACK_COMPLETE.

Esto es una red de seguridad: una actualización fallida no rompe el ambiente, lo devuelve a como estaba. La causa del fallo aparece en la pestaña Events, en el primer evento con estado ..._FAILED.

¿Qué es el drift?

El drift ocurre cuando alguien modifica a mano, desde la consola, un recurso que un stack gestiona —por ejemplo, cambia el DesiredCount del servicio directamente en ECS. El template y la realidad dejan de coincidir. CloudFormation ofrece Detect drift (en Stack actions) para comparar el estado real contra el template y listar las diferencias. La regla práctica: si un recurso lo gestiona un stack, cámbielo solo a través del stack.


Ejercicio 10 — Actualizar el stack con un change set

Aumentar el número de tareas del servicio de una a dos. Hacerlo modificando el template, creando un change set, verificando que el servicio se modifica sin reemplazo, y ejecutando el cambio. Confirmar que hay dos tareas en ejecución.


Preguntas puente

Estas preguntas cierran la sesión presencial y abren la remota. Conviene pensarlas mientras lo visto hoy está fresco: se leyó el template y se actualizó el stack con un change set. No buscar las respuestas de inmediato; intentar razonarlas desde lo que se hizo. Al comenzar la sesión remota, se discuten antes de seguir con buenas prácticas y contenedores.


Pregunta 1

Se cambió el DesiredCount del servicio con un change set. ¿Qué pasaría si, en cambio, se lo cambiara a mano desde la consola de ECS? ¿Qué pensaría CloudFormation del estado del stack?


Pregunta 2

El servicio mantiene dos tareas en ejecución. Si una de ellas se cae, ¿quién arranca una nueva, y cómo sabe esa tarea nueva qué imagen ejecutar?


Pregunta 3

Si se borra el stack de CloudFormation, ¿sobreviven los datos guardados en la tabla de DynamoDB? ¿Por qué?