CI/CD con Docker | Nicolás Garzón
Texto
Copiar commit protegido
→ tests
→ build
→ scan + attestations
→ push
→ digest aprobado
→ staging
→ producciónReconstruir durante deployment produce un artefacto distinto, aunque use el mismo commit. Las bases, dependencias, clocks, registry y parámetros pueden haber cambiado.
lint;
unit tests;
integration tests;
build;
análisis de seguridad;
publicación del artefacto.
seleccionar digest aprobado;
aplicar configuración del ambiente;
ejecutar migrations coordinadas;
rollout;
smoke tests;
observación;
rollback.
La frontera es el digest publicado.
Texto
Copiar checkout SHA
→ validar Dockerfile/Compose
→ construir target test
→ construir runtime
→ ejecutar tests sobre runtime
→ generar SBOM/provenance
→ escanear
→ push
→ firmar digest
→ desplegar staging
→ smoke/integration
→ promover mismo digest
→ rollout producciónTests fuera del container son rápidos, pero no prueban el artefacto final.
tests de código;
target Docker de tests;
smoke tests de la image final;
integración con dependencias reales/efímeras;
prueba de shutdown y health.
Bash
Copiar docker build --target test .
docker build --target runtime -t api:test .
docker run --rm api:test node --version
docker run --rm api:test npm run smokeUn builder moderno permite:
graph de etapas;
cache remota;
multi-platform;
secrets temporales;
provenance y SBOM;
outputs de registry.
El pipeline debe fijar versión/configuración del builder y registrar logs suficientes para reproducir fallos.
Texto
Copiar api:git-abc1234
api:1.8.2
api:mainPero la salida autorizada es:
Texto
Copiar api@sha256:...Registra el digest como output del job y úsalo en todos los stages posteriores.
No promuevas copiando latest; promueve el subject digest o crea tags adicionales que apunten al mismo contenido.
Texto
Copiar cache key/scope
→ layers reutilizables
→ build más rápido
repository;
branch;
plataforma;
nivel de confianza;
target.
Una PR externa no debe escribir cache consumida por releases privilegiados.
El build debe funcionar con cache vacía. La cache es optimización, no fuente única.
Cambios que deben invalidar:
lockfile;
base digest;
Dockerfile;
argumentos que alteran output;
toolchain;
source.
No uses timestamps artificiales que destruyan cache sin motivo. Para rebuild de seguridad, actualiza el digest/base o usa un input versionado y auditable.
Bash
Copiar docker build \
--secret id = npmrc,src= "$RUNNER_TEMP /npmrc" \
.
ARG TOKEN;
.env copiado;
credenciales en command line visible;
logs con headers;
tokens permanentes de registry.
Prefiere identidad federada/OIDC o tokens cortos cuando el registry y plataforma lo soporten.
Los secrets de producción no deben existir en el build.
Aplica mínimo privilegio:
pull requests: lectura, sin secrets de release;
main protegido: push al registry específico;
deployment: acceso al ambiente concreto;
firma: identidad restringida al workflow aprobado.
Protege environments con approvals cuando el riesgo lo requiera.
Riesgos de runners persistentes:
workspace residual;
Docker credentials;
layers con source privado;
mounts;
procesos huérfanos;
cache contaminada.
Prefiere runners efímeros para builds sensibles. Si son persistentes:
limpia de forma segura;
aísla repositorios;
rota credenciales;
monitorea disk;
actualiza Engine/builders;
restringe Docker socket.
Controlar /var/run/docker.sock suele equivaler a control administrativo del runner.
No ejecutes código de PR no confiable con acceso a un socket de un host que contiene secrets o workloads de otros proyectos.
runners efímeros;
builders remotos aislados;
rootless BuildKit;
entornos sandbox.
Asocia cada resultado al digest:
Texto
Copiar sha256:A
├── SBOM
├── scan result
├── provenance
└── signatureEscanea cada plataforma. Define política de findings:
bloqueante;
excepción temporal;
sin fix;
aceptado con controles;
fecha de expiración.
No ocultes el exit code del scanner para que el pipeline “pase”. Tampoco bloquees por conteo bruto sin contexto.
En builds multi-platform, el índice y manifests deben publicarse correctamente. Evita desplegar un tag mientras todavía está incompleto.
Publica a un tag temporal o por digest, verifica y después crea referencias de release.
Bash
Copiar docker compose -p ci-${CI_RUN_ID} up -d --wait
docker compose -p ci-${CI_RUN_ID} run --rm tests
docker compose -p ci-${CI_RUN_ID} logs --no-color > compose.log
docker compose -p ci-${CI_RUN_ID} down -v Usa project name único para evitar colisiones. Ejecuta cleanup incluso si los tests fallan.
No reutilices volumes de datos entre jobs salvo diseño explícito.
Texto
Copiar pre-deploy compatible migration
→ rollout app
→ post-deploy cleanup posteriorLas migrations deben ejecutarse como job coordinado y producir logs/exit code.
Antes de una migration destructiva:
backup;
compatibilidad expand/contract;
rollback;
timeout;
lock impact;
observabilidad.
Reemplaza instancias gradualmente. Requiere compatibilidad entre versiones simultáneas.
Mantiene versión anterior y nueva. Permite cambio rápido, pero datos/schema deben ser compatibles.
Envía pequeña fracción de tráfico y compara métricas.
Compose single-host no proporciona por sí solo todas estas estrategias. Puede implementarse con proxy y procedimientos, pero el riesgo debe aceptarse.
Después del deploy verifica:
digest efectivo;
health/readiness;
endpoint crítico;
conexión a database;
publicación de eventos/jobs;
logs sin errores;
métricas dentro de umbral.
Un health 200 superficial no basta.
Define ventana y criterios:
error rate;
p95/p99;
saturation;
OOM/restarts;
queue lag;
business KPI;
database load.
No marques éxito inmediatamente después de docker compose up -d.
digest anterior disponible;
configuración compatible;
schema compatible;
secrets anteriores o compatibles;
procedimiento probado;
criterio de activación.
Texto
Copiar nuevo digest B falla
→ volver a digest ASi una migration destruyó datos o cambió formato, volver la image no restaura el estado. Por eso database changes requieren estrategia separada.
image digest;
SBOM/provenance;
test reports;
scan result;
deployment metadata;
logs relevantes;
manifest/config resuelto.
Define retención suficiente para rollback e incident response. Garbage collection prematura puede eliminar el único artefacto recuperable.
Texto
Copiar scheduled rebuild
→ nueva base parcheada
→ tests
→ scan
→ rolloutTrátalo como release. Un nuevo digest puede cambiar comportamiento.
YAML
Copiar jobs :
build :
permissions :
contents : read
packages : write
id-token : write
steps :
- uses : actions/checkout@v4
- uses : docker/setup- buildx- action@v3
- uses : docker/login- action@v3
- uses : docker/build- push- action@v6
with :
context : .
push : true
tags : registry.example.com/api: git- ${ { github.sha } }
cache-from : type=registry, ref=registry.example.com/api: buildcache
cache-to : type=registry, ref=registry.example.com/api: buildcache, mode=max
provenance : true
sbom : true Las versiones concretas deben mantenerse actualizadas y revisadas. Fija actions por política de supply chain cuando corresponda.
Bash
Copiar docker inspect api --format '{{.Image}}'
docker image inspect registry.example.com/api@sha256:.. .Compara el digest esperado con el ejecutado. Un tag correcto no es evidencia suficiente.
plataforma distinta;
secret/config diferente;
volume permissions;
migration;
base no promocionada por digest.
Repite con cache vacía y revisa trust boundary.
No promociones hasta verificar manifests y attestations.
Añade wait/smoke y observación.
Schema o secret no son backward compatible.
construir en el servidor de producción;
usar latest como release;
entregar Docker socket a PRs externas;
guardar password de registry permanente;
reconstruir por ambiente;
no conservar digest anterior;
ejecutar migrations desde cada réplica;
ignorar logs cuando falla CI;
limpiar artifacts antes de terminar rollback window.
Build once, promote by digest.
CI prueba el artefacto; CD aplica configuración y rollout.
Cache y runners forman parte de la frontera de confianza.
Secrets de build y producción deben mantenerse separados.
Deployment exitoso exige verificación, no solo comando sin error.
Rollback de image no revierte automáticamente datos.
Comprueba lo aprendido
Diseña un pipeline para una API multi-platform con SBOM y firma.
¿Por qué una PR externa no debe escribir cache de release?
Define criterios de canary y rollback.
¿Cómo demostrarías que staging y producción ejecutan el mismo artefacto?
Diseña una migration compatible con rolling deployment.
Multi-platform builds , donde un solo tag puede resolver a imágenes diferentes según arquitectura y sistema operativo.