Una sola historia a lo largo de cuatro semanas:
CodeCommit → CodeBuild → ECR → ECS → CloudWatch
| Servicio | Rol en el pipeline |
|---|---|
| CodeCommit | Repositorio de código fuente |
| CodeBuild | Construcción de la imagen |
| ECR | Registro de imágenes Docker |
| CloudFormation | Infraestructura como código |
| ECS + Fargate | Ejecución de contenedores |
Desde el primer día se practica destruir y recrear el ambiente completo.
Si algo sale mal, borramos el stack de CloudFormation, esperamos a que termine, y lo volvemos a
lanzar con los mismos parámetros. El costo de un error se reduce a minutos de espera.
taller-semana1.yaml, provisto por el instructor.Cada ejercicio incluye su solución oculta — botón Ver solución en la guía.
Una combinación de prácticas culturales, servicios y herramientas que aumentan la capacidad de entregar software a alta velocidad.
El objetivo es unificar las áreas de desarrollo y operaciones, las cuales comúnmente suelen estar completamente separadas, para conseguir un mejor flujo desde el código a la aplicación desplegada.
%%{init: {"flowchart": {"defaultRenderer": "elk", "nodeSpacing": 30, "rankSpacing": 40, "padding": 8}, "themeVariables": {"clusterBkg": "#fffbeb", "clusterBorder": "#d97706"}}}%%
flowchart TD
DCORE["Desarrollo · Núcleo<br/>Código · lógica de negocio · pruebas"]
DSHELL["Desarrollo · Interfaz exterior<br/>Empaquetado · requisitos · instrumentación"]
DCORE <--> DSHELL
OCORE["Operaciones · Núcleo<br/>Infraestructura · seguridad · observabilidad · día 2"]
OSHELL["Operaciones · Interfaz exterior<br/>Plataforma · políticas · capacidad · retroalimentación"]
OCORE <--> OSHELL
subgraph CONTRACT["Contrato de despliegue · interfaz compartida"]
direction TB
ART["Artefacto desplegable"]
CFG["Configuración<br/>Argumentos · variables · secretos"]
DEP["Dependencias<br/>Entorno de ejecución · bibliotecas · servicios"]
EXP["Exposición<br/>Protocolos · puertos · cifrado · chequeos de salud"]
ART ~~~ DEP
CFG ~~~ EXP
end
DSHELL <-->|"Definición conjunta"| CONTRACT
OSHELL <-->|"Validación operativa"| CONTRACT
CONTRACT --> PIPELINE["Pipeline CI/CD"]
PIPELINE --> PROD["Aplicación en producción"]
DSHELL <-.-|"Retroalimentación de producción"| PROD
OSHELL <-.-|"Métricas · logs · incidentes"| PROD
classDef core fill:#dbeafe,stroke:#2563eb,color:#172554;
classDef shell fill:#e0e7ff,stroke:#4f46e5,color:#1e1b4b;
classDef contract fill:#fef3c7,stroke:#d97706,color:#451a03;
classDef runtime fill:#dcfce7,stroke:#16a34a,color:#052e16;
class DCORE,OCORE core;
class DSHELL,OSHELL shell;
class ART,CFG,DEP,EXP contract;
class PIPELINE,PROD runtime;
linkStyle 2,3 stroke:none;
linkStyle 8,9 stroke-dasharray:5 4;
Busca automatizar el flujo desde los últimos cambios realizados a la aplicación hasta su despliegue en producción, asegurando que cumple con los requisitos básicos de calidad, así como con el contexto necesario para su monitoreo continuo en día 2.
+-------------------------+ +-------------------+
| CI | | CD |
| Código -> Build -> Test | --> | Deploy -> Monitor |
| Day 1 | | Day 2 |
+-------------------------+ +-------------------+
Obtener el cliente:
xcode-select --install en la Terminal, o si se tiene Homebrew:
brew install git (git-scm.com/download/mac).Verificar la instalación:
git --version
git config --global user.name "Su Nombre"
git config --global user.email su-correo@ejemplo.com
Existent múltiples formas de acceder a CodeCommit en AWS, las cuales dependen del tipo de autenticación que usen.
Por favor, utilicen aquella que se adapta a su usuario.
HTTPS o SSHgit-remote-codecommitAWS CodeCommit es un servicio de control de versiones compatible con Git, alojado completamente en AWS. No requiere instalar ni operar ningún servidor: se crea el repositorio desde la consola, y AWS se encarga de la disponibilidad, la seguridad, y los respaldos.
En este taller cada participante trabaja sobre su propio repositorio individual. Eso
evita conflictos entre participantes y permite avanzar a ritmo propio. El nombre
del repositorio sigue la convención taller-aws-<su-nombre>, donde <su-nombre> es
el primer nombre en minúsculas y sin acentos (por ejemplo: taller-aws-maria).
Permite explorar los archivos y las carpetas de una rama, abrir su contenido y copiar la URL para clonar el repositorio.
Permite proponer la fusión de una rama en otra, revisar los cambios y registrar la discusión antes de integrar el trabajo.
Muestra el historial de cambios de la rama seleccionada. Cada commit identifica quién hizo el cambio, cuándo lo realizó y qué archivos modificó.
Lista las líneas de desarrollo del repositorio y permite crear una rama a partir de
otra, por ejemplo dev desde main.
Permite identificar un commit con un nombre estable, como v1.0.0, para señalar una
versión, una entrega o un punto importante del historial.
Centraliza la configuración del repositorio, como los disparadores (triggers), las reglas de aprobación y las notificaciones. Las opciones disponibles dependen de los permisos de IAM.
gitGraph
commit id: "base"
branch dev
checkout dev
commit id: "D1"
branch feature-work
checkout feature-work
commit id: "F1"
commit id: "F2"
checkout dev
merge feature-work id: "PR #42"
branch staging
checkout staging
commit id: "release candidate" tag: "v1.4.0-rc.1"
checkout dev
branch production
checkout production
merge staging id: "PR #43" tag: "v1.4.0"
gitGraph
commit id: "base"
commit id: "M1"
branch feature-search
checkout feature-search
commit id: "F1"
commit id: "F2"
checkout main
merge feature-search id: "PR #51"
commit id: "M2"
branch release-1-4
checkout release-1-4
commit id: "release candidate" tag: "v1.4.0-rc.1"
checkout main
merge release-1-4 id: "PR #52" tag: "v1.4.0"
gitGraph
commit id: "base"
branch feature-search
checkout feature-search
commit id: "S1"
commit id: "preview PR #57 listo"
checkout main
branch feature-profile
checkout feature-profile
commit id: "P1"
commit id: "preview PR #58 listo"
checkout main
merge feature-search id: "PR #57"
merge feature-profile id: "PR #58"
Los datos y las dependencias externas determinan qué tan fiel puede ser cada ambiente de prueba, especialmente cuando el ambiente nace para un PR y desaparece al terminar.
Cuando contamos con ambientes persistentes, la información puede crecer de forma orgánica, ser administrada por un equipo de datos o provenir de una sincronización controlada desde producción. Con procedimientos repetibles.
En los ambientes efímeros el desafío cambia. La infraestructura se crea de forma dinámica para cada PR, por lo que también se debe producir dinámicamente una fuente de datos útil. La factibilidad del patrón depende, en gran medida, de qué tan fácil sea crear un equivalente de las fuentes de datos productivas.
Por eso, antes de adoptar ambientes efímeros, Desarrollo, QA, Datos y Operaciones deben acordar qué datos necesita cada prueba, cómo se generan, cuánto tardan en prepararse y qué dependencias se pueden emular. Esa conversación define si el ambiente será una validación confiable o solo una aproximación superficial.
No existe un flujo de ramas superior en todos los contextos.
Elegir un modelo es acordar un lenguaje común: qué representa cada rama, qué controles debe pasar un PR, cómo se identifica una versión y cuándo se despliega. Un flujo simple, visible y entendido por Desarrollo y Operaciones es más valioso que seguir una receta popular que no encaja con la organización.
Usaremos la región us-east-2.
taller-aws-<su-nombre>. Usar el primer nombre en
minúsculas y sin acentos (por ejemplo: taller-aws-carlos).Repositorio del taller AWS DevOps — Semana 1.CodeCommit crea el repositorio vacío en segundos y lleva a la vista principal del repositorio.
Ejercicio 1
Clonar el repositorio https://github.com/cloudbridgeuy/courses desde GitHub, crear
un repositorio de CodeCommit llamado taller-aws-<su-nombre>, agregarlo como
remoto, y subir la rama main.
Clonar el código y entrar al directorio:
git clone https://github.com/cloudbridgeuy/courses
cd courses
Abrir CodeCommit, pulsar Create repository, y
crear taller-aws-<su-nombre> (el primer nombre en minúsculas, sin acentos).
Agregar CodeCommit como remoto, según el acceso configurado en los pre-requisitos:
# Variante HTTPS (usuario IAM) — copiar la Clone URL del repositorio
git remote add codecommit https://git-codecommit.<región>.amazonaws.com/v1/repos/taller-aws-<su-nombre>
# Variante SSH (usuario IAM) — copiar la Clone URL del repositorio
git remote add codecommit ssh://git-codecommit.<región>.amazonaws.com/v1/repos/taller-aws-<su-nombre>
# Variante Identity Center (grc) — construirla con la región y el perfil
git remote add codecommit codecommit::<región>://<perfil>@taller-aws-<su-nombre>
Verificar los remotos con git remote -v — deben verse origin (GitHub) y
codecommit.
Subir la rama main:
git push -u codecommit main
En la consola de CodeCommit, abrir el repositorio: los archivos aparecen en la pestaña Code.
Puede ser necesario que tengan que configurar el perfil que van a utilizar
con la variable de entorno AWS_PROFILE.
Con HTTPS, git pedirá el usuario y la contraseña generados en IAM. Con
Identity Center (grc), se reutiliza la sesión activa de la awscli.
Ejercicio 2
Desde la consola de CodeCommit, crear la rama dev a partir de main. Luego
localizar el ID del commit más reciente en esa rama.
dev.main.dev para abrirla.El código fuente no se despliega directamente: primero se transforma en un
artefacto desplegable. En este ejemplo, el artefacto es una imagen de contenedor
construida a partir del Dockerfile en la raíz del repositorio.
buildspec.yml?Un buildspec.yml es una especificación YAML que funciona como el pequeño DSL de
CodeBuild: declara las fases del trabajo y los comandos que se ejecutan en cada una.
version: 0.2
phases:
install:
commands:
- npm ci
build:
commands:
- npm test
- docker build -t app:$IMAGE_TAG .
version selecciona la versión del formato de buildspec.phases organiza el proceso; las fases permitidas son: install, pre_build, build y
post_build.commands contiene la lista de órdenes de shell de cada fase, ejecutadas en orden.env, artifacts, cache y reports.Amazon Elastic Container Registry (ECR) es el registro de imágenes de contenedores de AWS. Como DockerHub
Una vez construida la imagen Docker, hay que guardarla en algún lugar. Amazon ECR (Elastic Container Registry) es el servicio de AWS para almacenar imágenes Docker de forma privada y segura. Cuando ECS necesite lanzar el contenedor en la Semana 3, irá a buscar la imagen directamente a ECR.
El objetivo es solo producir el artefacto a desplegar una vez, para luego promoverlo entre ambientes.
La realidad es que no siempre se sigue esta práctica, y lo que termina pasando es que se termina reconstruyendo el artefacto en cada fase de su ciclo de vida. Algunas veces hay alguna razón para hacerlo, pero otras veces es simplemente un error.
Lo ideal, es que promovamos un mismo artefacto a través de distintos ambientes. Si es necesario que se comporte distinto en cada ambiente (se conecte a distintos servicios de terceros, bases de datos, nivel de logging, etc.) estos cambios de comportamiento deben producirse a través de cambios en su configuración, las cuales puede absorber de diversas maneras: variables de entorno, argumentos, archivos de configuración, etc.
Ahora que tenemos el código en un lugar que controlamos, tenemos que producir el artefacto desplegable de este proyecto: una imagen de contenedor.
%%{init: {"flowchart": {"defaultRenderer": "elk", "nodeSpacing": 40, "rankSpacing": 55, "padding": 12}, "themeVariables": {"clusterBkg": "#fdf4ff", "clusterBorder": "#c925d1", "surface0": "#fdf4ff", "border0": "#c925d1", "edgeLabelBackground": "#ffffff"}}}%%
flowchart LR
subgraph repo["<img src='/static/aws-codecommit.svg' width='40' height='40' /> CodeCommit"]
direction TB
source["Código fuente"]
spec["buildspec.yml"]
end
subgraph cb["<img src='/static/aws-codebuild.svg' width='40' height='40' /> CodeBuild"]
direction LR
dbuild["<img src='/static/docker.svg' width='42' /><br/>docker build"]
image[["Imagen Docker"]]
dpush["<img src='/static/docker.svg' width='42' /><br/>docker push"]
dbuild ==> image ==> dpush
end
ecr[("<img src='/static/aws-ecr.svg' width='44' height='44' /><br/>Amazon ECR")]
source --> dbuild
spec -.->|"fases y comandos"| dbuild
dpush ==> ecr
classDef repoNode fill:#ffffff,stroke:#c925d1,color:#4a044e
classDef dockerNode fill:#eff8ff,stroke:#2396ed,color:#0c4a6e
classDef artifactNode fill:#f1f5f9,stroke:#475569,color:#0f172a
classDef ecrNode fill:#fff7ed,stroke:#ed7100,stroke-width:2px,color:#7c2d12
class source,spec repoNode
class dbuild,dpush dockerNode
class image artifactNode
class ecr ecrNode
%%{init: {"flowchart": {"nodeSpacing": 25, "rankSpacing": 40}}}%%
flowchart LR
build["docker build<br/>(una sola vez)"] ==> img[("Imagen<br/>sha256:9f2c…")]
img -->|"pr-123"| pr["Ambiente efímero"]
img -->|"staging"| stg["Staging"]
img ==>|"v1.4.0"| prod["Producción"]
classDef dockerNode fill:#eff8ff,stroke:#2396ed,color:#0c4a6e
classDef artifactNode fill:#f1f5f9,stroke:#475569,color:#0f172a
classDef envNode fill:#ffffff,stroke:#94a3b8,color:#0f172a
classDef prodNode fill:#f0fdf4,stroke:#16a34a,stroke-width:2px,color:#14532d
class build dockerNode
class img artifactNode
class pr,stg envNode
class prod prodNode
merge, solo se despliega lo que realmente cambió.%%{init: {"flowchart": {"nodeSpacing": 25, "rankSpacing": 40}}}%%
flowchart LR
push["push al PR"] --> hash["tree hash<br/>por servicio"]
hash --> q{"¿existe<br/>en ECR?"}
q -->|"sí"| retag["re-etiquetar<br/>(segundos)"]
q -->|"no"| build["build + push"]
retag ==> env["Ambiente efímero<br/>con los 3 servicios"]
build ==> env
classDef plainNode fill:#ffffff,stroke:#94a3b8,color:#0f172a
classDef decisionNode fill:#fff7ed,stroke:#ed7100,color:#7c2d12
classDef fastNode fill:#f0fdf4,stroke:#16a34a,color:#14532d
classDef dockerNode fill:#eff8ff,stroke:#2396ed,color:#0c4a6e
class push,hash,env plainNode
class q decisionNode
class retag fastNode
class build dockerNode
❯ TREE_SHA=$(git rev-parse "HEAD:crates/server" | cut -c1-12)
❯ echo $TREE_SHA
303776a4ed6a
❯ git rev-parse "HEAD:crates/server" "HEAD:crates" "HEAD:crates/apps"
303776a4ed6ace5e887a17c3f464607601929bfb
5d43ec0a9ceb0cc60fe160f0e501beafb88567fc
b4067697c8594c8cdce9e37e4fc8e4512a837853
❯ TREE_SHA=$(git rev-parse "HEAD:crates/server" "HEAD:crates" "HEAD:crates/apps" \
| git hash-object --stdin \
| cut -c1-12)
❯ echo $TREE_SHA
78eb2d011698
Dockerfile no es solo de los desarrolladoresapt, siempre juntos.Mantener un Artifact Registry interno tampoco es una tarea sencilla, pero de realizarlo correctamente, es algo muy poderoso. AWS nos puede ayudar con la gestión.
apt) quedan fuera — para eso, la imagen base corporativahadolintDL3008 (pins de apt),
DL3006/DL3007 (imagen base sin fijar), y ShellCheck en cada RUN.pre_build; cualquier advertencia corta el build antes
de construir nada.# hadolint ignore=… o
.hadolint.yaml en el repositorio.buildspec.yml — entre hosts, con transferencia.:cache) — cualquier host.%%{init: {"flowchart": {"nodeSpacing": 12, "rankSpacing": 40}}}%%
flowchart LR
local["Cache local"] -.-> cb["CodeBuild"]
s3[("Cache S3")] --> cb
reg[("ECR :cache")] ==> cb
cb ==> out[("Imagen publicada")]
classDef plainNode fill:#ffffff,stroke:#94a3b8,color:#0f172a
classDef s3Node fill:#f0fdf4,stroke:#16a34a,color:#14532d
classDef ecrNode fill:#fff7ed,stroke:#ed7100,stroke-width:2px,color:#7c2d12
classDef cbNode fill:#fdf4ff,stroke:#c925d1,color:#4a044e
classDef artifactNode fill:#f1f5f9,stroke:#475569,color:#0f172a
class local plainNode
class s3 s3Node
class reg ecrNode
class cb cbNode
class out artifactNode
Ejercicio 3
Crear un repositorio privado en Amazon ECR con el nombre taller-aws-<su-nombre>.
Abrir Elastic Container Registry.
En el panel lateral, seleccionar Private registry → Repositories.
Pulsar Create repository.
En Repository name, escribir taller-aws-<su-nombre>.
Dejar Image tag mutability en Mutable.
Pulsar Create repository.
En la lista de repositorios, pulsar sobre el nombre del repositorio recién creado.
Hacer click en la tab Summary.
Copiar el URI completo que aparece en la parte superior. Se necesitará para configurar CodeBuild y para el parámetro de CloudFormation en la sección siguiente. Con la CLI:
export TALLER=taller-aws-<su-nombre>
aws ecr describe-repositories \
--repository-names "$TALLER" \
--query 'repositories[0].repositoryUri' \
--output text
Por ejemplo:
❯ export TALLER=taller-aws-guzman
aws ecr describe-repositories \
--repository-names "$TALLER" \
--query 'repositories[0].repositoryUri' \
--output text
410228653321.dkr.ecr.us-east-2.amazonaws.com/taller-aws-guzman
Ejercicio 4
Configurar un proyecto de CodeBuild que lea el repositorio de CodeCommit, construya la
imagen Docker usando el buildspec.yml incluido en el código, y la publique en el
repositorio de ECR. Ejecutar el build y verificar que la imagen aparece en ECR con la
etiqueta latest.
taller-aws-<su-nombre>-build.Reference type define qué commit del repositorio se convierte en el artefacto:
v1.4.0). Referencia
inmutable, típica para releases.El campo Commit ID – optional que aparece junto a Branch permite fijar el
build a un commit específico de esa rama; además, al saber exactamente qué descargar,
CodeBuild puede clonar menos historial y acortar el build. En el taller se deja vacío:
se construye siempre el HEAD de main.
aws/codebuild/amazonlinux-x86_64-standard:6.0) y, en
Image version, Always use the latest image for this runtime version.kernel-6 (Amazon Linux 2023).Running mode elige dónde corre el buildspec:
aws/codebuild/amazonlinux-x86_64-standard:6.0). Para construir
imágenes, el daemon de Docker tiene que correr dentro de ese contenedor
(Docker-in-Docker), y eso es exactamente lo que habilita la casilla
Privileged. Es la vía clásica y la más documentada.docker del build se descargan
automáticamente a ese servidor
(documentación).
Se cobra aparte del build: por segundo de servidor activo según el compute elegido,
más una tarifa menor de cache at rest mientras está detenido
(pricing). En el taller no hace falta:
el cache en el registro (la etiqueta cache) ya cumple ese rol.En Additional configuration → Compute, seleccionar 4 vCPUs, 8 GiB memory.
En Additional configuration → Environment variables, agregar:
AWS_ACCOUNT_ID = el ID de la cuenta (12 dígitos)IMAGE_REPO_NAME = taller-aws-<su-nombre>IMAGE_TAG = latestEn Buildspec, bajo Build specifications, seleccionar Use a buildspec
file (la consola preselecciona Insert build commands) y dejar Buildspec
name vacío para que use el buildspec.yml de la raíz del repositorio.
En Artifacts, seleccionar No artifacts.
Pulsar Create build project.
En IAM, buscar el rol cuyo nombre comienza con codebuild-taller-aws-<su-nombre>,
adjuntarle la política AmazonEC2ContainerRegistryPowerUser. Con la CLI:
export TALLER=taller-aws-<su-nombre>
ROLE=$(aws iam list-roles \
--query "Roles[?starts_with(RoleName, 'codebuild-$TALLER')].RoleName" \
--output text)
aws iam attach-role-policy \
--role-name "$ROLE" \
--policy-arn arn:aws:iam::aws:policy/AmazonEC2ContainerRegistryPowerUser
Volver a CodeBuild, abrir el proyecto, y pulsar Start build.
En la pestaña Build logs, seguir la ejecución hasta que el estado sea
Succeeded. El log completo queda en CloudWatch Logs (View entire log, o
el grupo /aws/codebuild/taller-aws-<su-nombre>-build); con la CLI:
aws logs tail "/aws/codebuild/$TALLER-build" --since 1h.
En ECR, abrir el repositorio y confirmar las entradas con la fecha de hace unos
minutos: la imagen con sus cuatro tags (latest, branch-main, SHA corto y SHA
completo, todos con el mismo digest) y el cache de capas de buildx con la
etiqueta cache. Con la CLI:
aws ecr describe-images \
--repository-name "$TALLER" \
--query "sort_by(imageDetails,&imagePushedAt)[].{tags:join(', ',imageTags),pushed:imagePushedAt}" \
--output table
Por ejemplo:
❯ aws ecr describe-images \
--repository-name "$TALLER" \
--query "sort_by(imageDetails,&imagePushedAt)[].{tags:join(', ',imageTags),pushed:imagePushedAt}" \
--output table
---------------------------------------------------------------------------------------------------------------------
| DescribeImages |
+-----------------------------------+-------------------------------------------------------------------------------+
| pushed | tags |
+-----------------------------------+-------------------------------------------------------------------------------+
| 2026-07-31T13:56:49.501000-03:00 | branch-main, cd5906be4801, latest, cd5906be4801c59a22b7c6816ea2683e85700fd1 |
| 2026-07-31T13:57:23.661000-03:00 | cache |
+-----------------------------------+-------------------------------------------------------------------------------+
La retención de imágenes es una política de la empresa.
%%{init: {"flowchart": {"nodeSpacing": 25, "rankSpacing": 45}}}%%
flowchart LR
ecr[("<img src='/static/aws-ecr.svg' width='40' height='40' /><br/>repositorio")]
ecr --> q{"¿qué regla<br/>lo limpia?"}
q -->|"por tiempo<br/>(> 90 días)"| del["borra también la<br/>imagen en producción"]
del --> fail["escalar → ✗ pull falla"]
q -->|"últimas N, y<br/>release-* protegido"| keep["lo desplegado<br/>siempre existe"]
keep --> ok["escalar → ✓ pull"]
classDef ecrNode fill:#fff7ed,stroke:#ed7100,stroke-width:2px,color:#7c2d12
classDef decisionNode fill:#fff7ed,stroke:#ed7100,color:#7c2d12
classDef badNode fill:#fef2f2,stroke:#dc2626,color:#7f1d1d
classDef fastNode fill:#f0fdf4,stroke:#16a34a,color:#14532d
class ecr ecrNode
class q decisionNode
class del,fail badNode
class keep,ok fastNode
Hay que tener cuidado con la limpieza de imagenes por tiempo.
Mitigación: usar las "últimas N" en vez de días, tags de release protegidos, re-tag al promocionar, preview antes de aplicar.
Cada push a la región de origen se copia solo.
Clave de un plan de Disaster Recovery.
%%{init: {"flowchart": {"nodeSpacing": 30, "rankSpacing": 55}}}%%
flowchart LR
push["<img src='/static/docker.svg' width='42' /><br/>docker push"]
push ==> src[("<img src='/static/aws-ecr.svg' width='40' height='40' /><br/>us-east-2<br/>(origen)")]
src -.->|"réplica automática"| west[("<img src='/static/aws-ecr.svg' width='40' height='40' /><br/>us-west-2")]
src -.->|"réplica automática"| acct[("<img src='/static/aws-ecr.svg' width='40' height='40' /><br/>otra cuenta")]
west --> dr["DR: recrear el ambiente<br/>sin la región caída"]
classDef dockerNode fill:#eff8ff,stroke:#2396ed,color:#0c4a6e
classDef ecrNode fill:#fff7ed,stroke:#ed7100,stroke-width:2px,color:#7c2d12
classDef fastNode fill:#f0fdf4,stroke:#16a34a,color:#14532d
class push dockerNode
class src,west,acct ecrNode
class dr fastNode
Se replican los pushes, no los borrados: cada destino define su propia lifecycle policy.
En un pull entre cuentas deben abrirse dos puertas; lo público va por Amazon ECR Public.
%%{init: {"flowchart": {"nodeSpacing": 30, "rankSpacing": 50}, "themeVariables": {"clusterBkg": "#f8fafc", "clusterBorder": "#94a3b8", "edgeLabelBackground": "#ffffff"}}}%%
flowchart LR
subgraph consumidora["cuenta consumidora"]
ecs["servicio ECS"]
iam["puerta 1: IAM del<br/>task execution role"]
ecs --> iam
end
subgraph shared["cuenta shared services"]
pol["puerta 2: repository policy<br/>solo pull + aws:PrincipalOrgID"]
repo[("<img src='/static/aws-ecr.svg' width='40' height='40' /><br/>ECR privado")]
pol --> repo
end
iam ==>|"pull"| pol
pub[("<img src='/static/aws-ecr.svg' width='40' height='40' /><br/>ECR Public")] -->|"pull anónimo"| world["cualquiera, sin<br/>cuenta de AWS"]
classDef plainNode fill:#ffffff,stroke:#94a3b8,color:#0f172a
classDef gateNode fill:#fefce8,stroke:#ca8a04,color:#713f12
classDef ecrNode fill:#fff7ed,stroke:#ed7100,stroke-width:2px,color:#7c2d12
class ecs,world plainNode
class iam,pol gateNode
class repo,pub ecrNode
Búsqueda continua de CVEs conocidos (Amazon Inspector). Findings + agentes → remediaciones como PRs automatizadas, sobre el mismo CI/CD.
%%{init: {"flowchart": {"nodeSpacing": 25, "rankSpacing": 45}}}%%
flowchart LR
ecr[("<img src='/static/aws-ecr.svg' width='40' height='40' /><br/>imagen")]
ecr --> insp["Amazon Inspector<br/>escaneo continuo"]
insp -->|"CVE nuevo"| finding["finding"]
finding --> agent["agente"]
agent -->|"PR automatizada"| cc["<img src='/static/aws-codecommit.svg' width='40' height='40' /><br/>repo"]
cc --> cb["<img src='/static/aws-codebuild.svg' width='40' height='40' /><br/>build"]
cb ==>|"imagen corregida"| ecr
classDef ecrNode fill:#fff7ed,stroke:#ed7100,stroke-width:2px,color:#7c2d12
classDef plainNode fill:#ffffff,stroke:#94a3b8,color:#0f172a
classDef badNode fill:#fef2f2,stroke:#dc2626,color:#7f1d1d
classDef agentNode fill:#faf5ff,stroke:#9333ea,color:#581c87
classDef repoNode fill:#ffffff,stroke:#c925d1,color:#4a044e
classDef fastNode fill:#f0fdf4,stroke:#16a34a,color:#14532d
class ecr ecrNode
class insp plainNode
class finding badNode
class agent agentNode
class cc repoNode
class cb fastNode
Ejercicio 5
Editar el proyecto de CodeBuild para activar el cache local con los modos
SourceCache y DockerLayerCache. Ejecutar dos builds seguidos y comparar sus
duraciones en el historial del proyecto.
taller-aws-<su-nombre>-build y pulsar Edit.Ejercicio 6
Configurar un pull-through cache de la galería pública de ECR, y apuntar las dos
líneas FROM del Dockerfile a nuestro registro privado. Al terminar, ningún build
vuelve a descargar rust ni debian desde Docker Hub.
Abrir Elastic Container Registry y, en el panel lateral, seleccionar Private registry → Pull through cache.
Pulsar Add rule. En Registry, seleccionar Amazon ECR Public —no requiere credenciales— y pulsar Next.
Dejar el prefijo predeterminado ecr-public, pulsar Next, y luego Create.
La regla vale para todo el registro privado de la cuenta, una por región: cualquier
repositorio bajo el prefijo ecr-public/ pasa a servirse a través del cache.
Darle permiso al rol de CodeBuild para importar imágenes a través de la regla —la
política AmazonEC2ContainerRegistryPowerUser no lo incluye. En
IAM → Roles, abrir el rol
codebuild-taller-aws-<su-nombre>-..., pulsar Add permissions → Create inline
policy, cambiar a la pestaña JSON, y pegar (reemplazando el ID de cuenta):
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": ["ecr:BatchImportUpstreamImage", "ecr:CreateRepository"],
"Resource": "arn:aws:ecr:*:<AWS_ACCOUNT_ID>:repository/ecr-public/*"
}
]
}
Nombrarla pull-through-cache y guardarla. ecr:CreateRepository solo hace
falta la primera vez, mientras el repositorio de cache todavía no existe.
En el clon local del repositorio, editar las dos líneas FROM del Dockerfile
para que apunten al registro privado. La galería pública de ECR publica las
imágenes oficiales de Docker bajo docker/library/, con el mismo digest que
Docker Hub, así que los pins @sha256: no cambian —solo cambia el host:
FROM <AWS_ACCOUNT_ID>.dkr.ecr.<region>.amazonaws.com/ecr-public/docker/library/rust:1.95-slim-bookworm@sha256:d7482085ff5b415f84dba5647ae71606650bdef00db7aeb69f4b3d170c3e4082 AS builder
FROM <AWS_ACCOUNT_ID>.dkr.ecr.<region>.amazonaws.com/ecr-public/docker/library/debian:bookworm-slim@sha256:7b140f374b289a7c2befc338f42ebe6441b7ea838a042bbd5acbfca6ec875818
Confirmar y publicar el cambio: git add Dockerfile, git commit, git push.
En CodeBuild, pulsar Start build. En los logs, el pull de las imágenes base
ahora sale de dkr.ecr — la primera vez, ECR las importa del upstream; las
siguientes, las sirve directo desde el cache.
Al terminar, volver a ECR: aparecen dos repositorios nuevos,
ecr-public/docker/library/rust y ecr-public/docker/library/debian, con las
imágenes base cacheadas.