CD (Entrega Continua) — Parte 4: Deploy a Staging y Performance Smoke Tests
📚 Serie: CI/CD y la IA: De la teoría a la práctica
Esta parte cubre el despliegue en el entorno de preproducción (staging), la validación E2E y de rendimiento ligero, y la aprobación manual antes de promover a producción.
Partes de este bloque:
- Parte 1 — Docker Packaging, SBOM y remediación)
- Parte 2 — Image Scan y Service Tests
- Parte 3 — Deploy a Dev y Smoke Tests
- Parte 4 — Deploy a Staging y Performance Smoke Tests ← estás aquí
- Parte 5 — LCA: Load, Stress y Chaos Engineering
- [Parte 6 — Validaciones finales, Manual Approval y Deploy a Producción](https://jarroba.com/ci-cd-y-la-ia-de-la-teoria-a-la-practica/
Deploy a Staging
Promueve el artefacto ya verificado (misma imagen firmada y SBOM) a un entorno de preproducción que replica la configuración de producción con mayor fidelidad. Staging es el último entorno para validar integraciones, performance a escala reducida, pruebas de regresión y validaciones de compliance antes del despliegue a producción.
Lo que incluye esta etapa
- Selección del artefacto: promover la misma imagen firmada que pasó Dev y los scans.
- Provisionamiento de entorno: aplicar IaC para crear/actualizar recursos (namespaces, ingress, secrets, configmaps) con valores de staging.
- Configuración de datos: usar datos sintéticos o copias anonimizadas; preparar fixtures y migraciones necesarias.
- Despliegue con estrategia realista: Helm/Kustomize con valores de staging; aplicar recursos de red, límites y tolerancias equivalentes a producción.
- Activación de observabilidad completa: métricas, trazas distribuidas y logging centralizado con retención adecuada.
- Ejecución de suites completas: E2E, performance smoke, tests de seguridad adicionales y validaciones de compliance.
- Gates y aprobaciones: gates automáticos y, si procede, aprobación manual antes de promover a producción.
- Registro y trazabilidad: anotar release con commit SHA, tag de imagen, SBOM y firma; almacenar artefactos y reportes.
Pasos
- Obtener imagen firmada del registry.
- Verificar firma (cosign) y SBOM asociado.
- Aplicar IaC (Terraform para infra, Helm/Kustomize para manifiestos).
- Configurar secrets desde Vault/Secret Manager; no exponer valores.
- Desplegar con wait y probes (
helm upgrade --install --wait --timeout). - Ejecutar validaciones automáticas: E2E, contract tests, smoke de performance.
- Evaluar resultados y ejecutar gates: si OK → marcar listo para producción; si KO → rollback y crear ticket.
- Aprobación humana opcional para producción (manual approval step).
Buenas prácticas
- Promover el mismo artefacto firmado entre entornos para garantizar reproducibilidad.
- Paridad de configuración con producción en límites, recursos y sidecars; diferencias solo en datos y endpoints externos.
- Entorno inmutable: evitar cambios ad-hoc en staging; todo por IaC.
- Datos anonimizados o synthetic data para pruebas realistas sin exponer PII.
- Gates claros: definir qué fallos bloquean y cuáles generan tickets.
- Aprobaciones para producción: integrar un paso de aprobación humana con contexto (logs, métricas, reportes).
- Rollback automático y plan de mitigación si las validaciones fallan.
- Observabilidad y trazabilidad: anotar release con metadatos y conservar reportes (E2E, perf, security).
Ventajas
- Alta confianza antes de producción por la similitud con el entorno real.
- Permite pruebas de integración y compliance en condiciones realistas.
- Reduce riesgos de regresiones y errores de configuración.
Desventajas / riesgos
- Coste en recursos y tiempo; pipelines más largos.
- Si no se mantiene paridad, staging puede dar falsos positivos/negativos.
- Gestión de datos sensibles exige controles estrictos.
Ejemplo: job simple GitLab CI para Deploy a Staging
deploy_to_staging:
stage: deploy
image: alpine/helm:3.12.0
variables:
NAMESPACE: staging
before_script:
- echo "$KUBE_CONFIG_BASE64" | base64 -d > /tmp/kubeconfig
- export KUBECONFIG=/tmp/kubeconfig
script:
- echo "Verifying image signature"
- cosign verify --key "$COSIGN_PUBKEY" $REGISTRY/$IMAGE_NAME:${IMAGE_TAG}
- echo "Deploying to staging"
- helm upgrade --install myapp ./chart \
--namespace $NAMESPACE \
--create-namespace \
--set image.repository=$REGISTRY/$IMAGE_NAME \
--set image.tag=${IMAGE_TAG} \
--wait --timeout 10m
- echo "Annotate release metadata"
- kubectl -n $NAMESPACE annotate deployment myapp "ci.commit=${CI_COMMIT_SHA}" --overwrite || true
rules:
- if: $CI_COMMIT_BRANCH =~ /^release\/.*$/ || $CI_COMMIT_BRANCH == "develop"
Ejemplo completo: Deploy a Staging con E2E, Performance y Aprobación manual
Este ejemplo ejecuta en staging la batería completa de pruebas de aceptación (E2E) y pruebas de rendimiento a escala reducida, automatiza la evaluación de resultados y expone un paso de aprobación manual antes de promover a producción.
El siguiente pipeline hace lo siguiente:
- Obtiene la imagen firmada del registry y verifica firma y SBOM.
- Despliega en staging con IaC y Helm/Kustomize usando valores de staging.
- Ejecuta E2E (Cypress o Newman) contra el entorno staging y publica reportes JUnit/HTML.
- Ejecuta pruebas de rendimiento con k6 para medir latencia y throughput en escenarios críticos.
- Evalúa gates automáticos: fallos E2E o umbrales de performance (p. ej. p95 > X ms, error rate > Y%) bloquean la promoción.
- Publica artefactos y reportes para auditoría y trazabilidad.
- Si todo OK crea un paso de aprobación manual para promover a producción.
- Si KO realiza rollback y crea ticket de remediación con evidencia.
stages:
- staging-deploy
- staging-validate
- approval
variables:
REGISTRY: registry.example.com
IMAGE_NAME: myapp
IMAGE_TAG: ${IMAGE_TAG}
NAMESPACE: staging
HELM_CHART_PATH: ./chart
K6_SCRIPT: tests/k6/staging-scenario.js
CYPRESS_CONFIG: cypress.json
deploy_to_staging:
stage: staging-deploy
image: alpine/helm:3.12.0
before_script:
- echo "$KUBE_CONFIG_BASE64" | base64 -d > /tmp/kubeconfig
- export KUBECONFIG=/tmp/kubeconfig
script:
- echo "Verify image signature"
- cosign verify --key "$COSIGN_PUBKEY" $REGISTRY/$IMAGE_NAME:$IMAGE_TAG
- echo "Deploying to staging"
- helm upgrade --install myapp $HELM_CHART_PATH \
--namespace $NAMESPACE \
--create-namespace \
--set image.repository=$REGISTRY/$IMAGE_NAME \
--set image.tag=$IMAGE_TAG \
--wait --timeout 10m
- kubectl -n $NAMESPACE annotate deployment myapp "ci.commit=${CI_COMMIT_SHA}" --overwrite || true
rules:
- if: $CI_COMMIT_BRANCH =~ /^release\/.*$/ || $CI_COMMIT_BRANCH == "develop"
e2e_and_performance:
stage: staging-validate
image: node:20-alpine
needs:
- job: deploy_to_staging
artifacts: false
before_script:
- apk add --no-cache curl bash jq
- npm ci
- echo "$KUBE_CONFIG_BASE64" | base64 -d > /tmp/kubeconfig
- export KUBECONFIG=/tmp/kubeconfig
script:
- echo "Discover service endpoint"
- SERVICE_HOST=$(kubectl -n $NAMESPACE get svc myapp -o jsonpath='{.status.loadBalancer.ingress[0].hostname}' || kubectl -n $NAMESPACE get svc myapp -o jsonpath='{.spec.clusterIP}')
- echo "Service host: $SERVICE_HOST"
# Run E2E with Cypress headless and export JUnit
- npx cypress run --config-file $CYPRESS_CONFIG --env baseUrl="http://$SERVICE_HOST" --reporter junit --reporter-options "mochaFile=reports/cypress-junit-[hash].xml"
- |
if [ $? -ne 0 ]; then
echo "E2E tests failed" >&2
exit 10
fi
# Run k6 performance scenario
- apk add --no-cache curl
- wget -q -O /usr/local/bin/k6 https://github.com/grafana/k6/releases/download/v0.45.0/k6-v0.45.0-linux64 && chmod +x /usr/local/bin/k6
- k6 run --out json=reports/k6-results.json $K6_SCRIPT || true
- |
ERR_RATE=$(jq '[.metrics.iterations.rate] | add' reports/k6-results.json || echo 0)
P95=$(jq '.metrics["http_req_duration"].p[95]' reports/k6-results.json || echo 0)
echo "k6 p95: $P95 ms, err_rate: $ERR_RATE"
if (( $(echo "$P95 > 500" | bc -l) )); then
echo "Performance threshold exceeded: p95 > 500ms" >&2
exit 11
fi
artifacts:
when: always
paths:
- reports/
reports:
junit: reports/cypress-junit-*.xml
rules:
- if: $CI_COMMIT_BRANCH =~ /^release\/.*$/ || $CI_COMMIT_BRANCH == "develop"
staging_to_prod_approval:
stage: approval
image: alpine:3.18
when: manual
allow_failure: false
script:
- echo "Manual approval to promote to production. Review reports and metrics before approving."
rules:
- if: $CI_COMMIT_BRANCH =~ /^release\/.*$/ || $CI_COMMIT_BRANCH == "develop"
Notas:
- E2E usa Cypress en modo headless y exporta JUnit para integración con GitLab Test Reports.
- Performance usa k6 y guarda resultados JSON; el job evalúa umbrales básicos y falla si se exceden. Ajusta métricas y umbrales a tu SLA.
- Artifacts: todos los reportes se guardan para auditoría.
- Approval: el job
staging_to_prod_approvales manual; al aprobarlo se puede disparar el job de promoción a producción que use el mismo artefacto firmado. - Exit codes diferenciados para distinguir fallos E2E y performance.
IA
- Mejora: optimiza valores de recursos, sugiere configuraciones de tolerancia, genera tests E2E y scripts de carga, prioriza findings de seguridad y performance.
- Automatización: puede rellenar plantillas de issue con evidencia, proponer parches y generar PRs de remediación.
- Limitación: no sustituye la ejecución real ni la aprobación humana en decisiones de riesgo; no puede firmar artefactos ni ejecutar despliegues por sí sola sin integración.
Performance Smoke Tests
Ejecuta pruebas de rendimiento ligeras y deterministas tras un despliegue para validar que los endpoints críticos cumplen requisitos básicos de latencia y estabilidad antes de avanzar a pruebas de carga completas o a producción. Buscan detectar degradaciones evidentes (p95/p99, error rate) que indicarían que el despliegue no es apto para promoción.
Pasos
- Selección de escenarios críticos: elegir 2–6 rutas o flujos representativos (login, búsqueda, checkout, endpoints de lectura intensiva).
- Definición de métricas y umbrales: p50/p95/p99, latencia máxima aceptable, tasa de errores máxima, throughput mínimo.
- Ejecución rápida y controlada: usar herramientas como k6 para escenarios cortos (30s–2min) con carga reducida.
- Medición y evaluación automática: calcular percentiles y error rate; comparar con umbrales y decidir pasar/fallar.
- Salida estructurada: exportar resultados en JSON/CSV para ingestión en el pipeline y para adjuntar a reportes.
- Acciones según resultado: si se exceden umbrales críticos → bloquear promoción, crear ticket y/o rollback; si están dentro de límites → continuar.
Buenas prácticas
- Mantener tests cortos y deterministas: objetivo < 2–5 minutos para no alargar pipelines.
- Elegir escenarios de alto impacto en lugar de cubrir todo el API.
- Ejecutar en staging con paridad de recursos para obtener mediciones representativas.
- Correlacionar con trazas y métricas para identificar la causa raíz (CPU, GC, latencia de DB).
- Aislar ruido: ejecutar en ventanas controladas y limpiar datos entre runs.
- Definir umbrales basados en SLAs y revisarlos periódicamente.
- Automatizar decisiones: pasar/fallar pipeline según reglas claras; generar reportes y tickets con evidencia.
Ventajas
- Detección temprana de degradaciones antes de pruebas de carga completas.
- Bajo coste temporal comparado con pruebas de stress.
- Automatización fácil en CI/CD para gates rápidos.
Desventajas
- Cobertura limitada: no sustituye pruebas de carga o stress.
- Resultados sensibles al entorno: si staging no replica producción, las métricas pueden engañar.
- Posible flakiness si no se controlan variables externas.
Herramientas
- k6: ideal para scripts JS ligeros y export JSON.
- Gatling / Locust: alternativas para escenarios más complejos.
- Grafana Tempo / Prometheus: para correlacionar métricas y trazas.
Ejemplos
Ejemplo k6 minimal (tests/k6/perf-smoke.js):
import http from 'k6/http';
import { check } from 'k6';
export let options = { vus: 10, duration: '1m' };
export default function () {
let res = http.get(`${__ENV.BASE_URL}/api/v1/critical`);
check(res, { 'status 200': (r) => r.status === 200 });
}
Evaluación simple (bash):
k6 run --out json=perf.json tests/k6/perf-smoke.js
P95=$(jq '.metrics.http_req_duration.p[95]' perf.json)
if (( $(echo "$P95 > 500" | bc -l) )); then exit 1; fi
GitLab CI job:
performance_smoke:
stage: validate
image: loadimpact/k6:latest
variables:
BASE_URL: "http://myapp-staging.example.com"
K6_SCRIPT: tests/k6/perf-smoke.js
script:
- k6 run --out json=perf.json $K6_SCRIPT
- P95=$(jq '.metrics.http_req_duration.p[95]' perf.json)
- ERR_RATE=$(jq '.metrics.http_req_failed.rate' perf.json)
- echo "p95=$P95 ms err_rate=$ERR_RATE"
- |
if (( $(echo "$P95 > 500" | bc -l) )) || (( $(echo "$ERR_RATE > 0.01" | bc -l) )); then
echo "Performance smoke failed: p95 or error rate exceeded" >&2
exit 1
fi
artifacts:
when: always
paths:
- perf.json
rules:
- if: $CI_COMMIT_BRANCH =~ /^release\/.*$/ || $CI_COMMIT_BRANCH == "develop"
IA
- Mejora: la IA puede sugerir escenarios críticos a partir de logs y telemetría histórica, proponer umbrales realistas y generar scripts k6 automáticamente.
- Diagnóstico: al fallar un test, correlacionar métricas, agrupar anomalías y proponer la causa raíz (latencia de DB, saturación de CPU).
- Automatización: generar tickets con resumen ejecutivo, evidencia y recomendaciones; proponer PRs con ajustes de configuración (timeouts, pool sizes).
- Limitación: la IA no sustituye la ejecución real ni la validación humana de cambios de arquitectura.



