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:


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

  1. Obtener imagen firmada del registry.
  2. Verificar firma (cosign) y SBOM asociado.
  3. Aplicar IaC (Terraform para infra, Helm/Kustomize para manifiestos).
  4. Configurar secrets desde Vault/Secret Manager; no exponer valores.
  5. Desplegar con wait y probes (helm upgrade --install --wait --timeout).
  6. Ejecutar validaciones automáticas: E2E, contract tests, smoke de performance.
  7. Evaluar resultados y ejecutar gates: si OK → marcar listo para producción; si KO → rollback y crear ticket.
  8. 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_approval es 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

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.

En el siguiente artículo, “CD — Parte 5: LCA — Load, Stress y Chaos Engineering”, entraremos en la validación de resiliencia real: pruebas de carga, estrés y caos para asegurar que el sistema se mantiene estable y recuperable bajo condiciones extremas.

Comparte esta entrada en:
Safe Creative #1401310112503
CD (Entrega Continua) — Parte 4: Deploy a Staging y Performance Smoke Tests por "www.jarroba.com" esta bajo una licencia Creative Commons
Reconocimiento-NoComercial-CompartirIgual 3.0 Unported License.
Creado a partir de la obra en www.jarroba.com

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *

Este sitio usa Akismet para reducir el spam. Aprende cómo se procesan los datos de tus comentarios.