Saltearse al contenido

Pipelines CI/CD

Tabla de Contenidos

  1. Propósito
  2. ¿Para quién es esto?
  3. Descripción General del Pipeline
  4. Arquitectura del Pipeline
  5. Desglose de Etapas
  6. Determinación de Entorno
  7. Conexiones de Servicio y Variables
  8. Flujo de Despliegue
  9. Solución de Problemas de Pipelines

Propósito

Este documento describe la implementación del pipeline CI/CD para la plataforma Algesta usando Azure Pipelines. Todos los microservicios siguen una estrategia de despliegue consistente basada en tags que automáticamente construye, prueba, dockeriza y despliega a Azure Kubernetes Service (AKS).

Siguiendo esta guía, entenderás:

  • Cómo los tags de git disparan despliegues específicos por entorno
  • La arquitectura del pipeline de cinco etapas (Setup → Build → DockerBuild → Deploy → DeployToK8S)
  • Conexiones de servicio requeridas y variables del pipeline
  • Fallas comunes del pipeline y estrategias de resolución

¿Para quién es esto?

Esta guía es para ingenieros DevOps gestionando infraestructura CI/CD, desarrolladores solucionando fallas de build y gestores de release coordinando despliegues. Asume familiaridad con Azure DevOps, Docker, Kubernetes y conceptos de CI/CD.


Descripción General del Pipeline

Repositorios con Pipelines

Cada Microservicio y el dashboard tienen Azure Pipelines independientes:

RepositorioArchivo de PipelineObjetivo de DespliegueDisparador
algesta-api-gateway-nestjsazure-pipelines.ymlAKS (production/development/Pruebas namespaces)Tag: v*-dev, v*-test, v*-prod
algesta-ms-orders-nestjsazure-pipelines.ymlAKS (production/development/Pruebas namespaces)Tag: v*-dev, v*-test, v*-prod
algesta-ms-notifications-nestjsazure-pipelines.ymlAKS (production/development/Pruebas namespaces)Tag: v*-dev, v*-test, v*-prod
algesta-ms-provider-nestjsazure-pipelines.ymlAKS (production/development/Pruebas namespaces)Tag: v*-dev, v*-test, v*-prod
algesta-dashboard-reactazure-pipelines.ymlFirebase Hosting (Despliegue manual)Tag: v*-dev, v*-prod
Algesta (mono-repo)azure-pipelines.ymlActualizaciones de diagramas StructurizrBranch: main

Selección de Entorno Basada en Tags

Patrón de Tag → Mapeo de Entorno:

v1.2.3-dev → entorno development
v1.2.3-test → entorno test
v1.2.3-prod → entorno production

Ejemplo de Flujo de Trabajo:

  1. Desarrollador hace push de tag: git tag v2.1.0-dev && git push origin v2.1.0-dev
  2. Azure Pipeline detecta el tag vía configuración de trigger
  3. Etapa Setup parsea el sufijo del tag (-dev) y establece variables de entorno
  4. Etapas subsiguientes usan variables de entorno para seleccionar Repositorio ACR, namespace AKS, manifiestos de Kubernetes

Arquitectura del Pipeline

Todos los pipelines de Microservicios siguen esta estructura de cinco etapas:

graph LR
    A[1. Setup<br/>Parse Tag<br/>Set Variables] --> B[2. Build<br/>npm install<br/>npm run build<br/>npm test]
    B --> C[3. DockerBuild<br/>Build Image<br/>Tag with BuildId<br/>Save as TAR]
    C --> D[4. Deploy<br/>Load TAR<br/>Tag latest<br/>Push to ACR]
    D --> E[5. DeployToK8S<br/>kubectl apply<br/>Update Deployment<br/>Verify Rollout]

    style A fill:#e3f2fd,stroke:#1976d2
    style B fill:#fff3e0,stroke:#f57c00
    style C fill:#f3e5f5,stroke:#7b1fa2
    style D fill:#e8f5e9,stroke:#388e3c
    style E fill:#fce4ec,stroke:#c2185b

Características del Pipeline:

  • Disparador: Basado en tags (tags: include: v*)
  • Imagen VM: ubuntu-latest
  • Paralelización: Las etapas dependen de etapas previas (ejecución secuencial)
  • Artefactos: Artefactos de build (etapa 2) e imagen Docker TAR (etapa 3)
  • Secrets: Gestionados vía Variables de Azure Pipeline (credenciales ACR, kubeconfig)

Ubicación del Archivo (ejemplo): algesta-api-gateway-nestjs/azure-pipelines.yml (líneas 1-233)


Desglose de Etapas

Etapa 1: Configuración y Determinación de Entorno

Propósito: Parsear tag de git para determinar entorno objetivo y establecer variables del pipeline

Configuración YAML:

stages:
- stage: Setup
displayName: Setup and Determine Environment
jobs:
- job: DetermineEnv
displayName: Determine Build Environment
steps:
- checkout: self
fetchDepth: 0
- script: |
VERSION=$(echo "$(Build.SourceBranchName)" | sed 's/-.*//')
echo "##vso[task.setvariable variable=PROJECT_VERSION]$VERSION"
echo "Version detected: $VERSION"
TAG_NAME=$(Build.SourceBranchName)
echo "Tag detected: $TAG_NAME"
case "$TAG_NAME" in
*-dev)
ENVIRONMENT="development"
REPO="onklinic/onklinic-dev/api-gateway"
ENV_TO_DEPLOY="infrastructure/terraform-dev"
;;
*-test)
ENVIRONMENT="test"
REPO="onklinic/onklinic-test/api-gateway"
ENV_TO_DEPLOY="infrastructure/terraform-test"
;;
*-prod)
ENVIRONMENT="production"
REPO="onklinic/onklinic-prod/api-gateway"
ENV_TO_DEPLOY="infrastructure/terraform-prod"
;;
*)
echo "Invalid tag format. Skipping build."
exit 1
;;
esac
echo "##vso[task.setvariable variable=ENVIRONMENT;isOutput=true]$ENVIRONMENT"
echo "##vso[task.setvariable variable=REPO;isOutput=true]$REPO"
echo "##vso[task.setvariable variable=ENV_TO_DEPLOY;isOutput=true]$ENV_TO_DEPLOY"
displayName: Determine Build Environment
name: SetVariables

Lógica Clave:

  1. Extraer nombre de tag desde Build.SourceBranchName (ej., v1.2.3-dev)
  2. Parsear número de versión: v1.2.3-devv1.2.3
  3. Coincidencia de patrón en sufijo:
    • -devENVIRONMENT=development, REPO=onklinic/onklinic-dev/api-gateway
    • -testENVIRONMENT=test, REPO=onklinic/onklinic-test/api-gateway
    • -prodENVIRONMENT=production, REPO=onklinic/onklinic-prod/api-gateway
  4. Establecer variables de salida para etapas subsiguientes:
    • ENVIRONMENT: Selector de namespace AKS
    • REPO: Ruta del Repositorio ACR (nota: actualmente referencia GCP, debe actualizarse a ACR)
    • ENV_TO_DEPLOY: Directorio de Terraform (para futura integración de Terraform)

Variables de Salida:

  • stageDependencies.Setup.DetermineEnv.outputs['SetVariables.ENVIRONMENT']
  • stageDependencies.Setup.DetermineEnv.outputs['SetVariables.REPO']

Escenarios de Falla:

  • Formato de tag inválido: Si el tag no coincide con *-dev|*-test|*-prod, el pipeline termina con código 1
  • Tag faltante: Si se dispara manualmente sin tag, Build.SourceBranchName retorna nombre de branch, causando falla

Verificación: Verificar logs del pipeline para:

Version detected: v1.2.3
Tag detected: v1.2.3-dev
Environment detected: development

Etapa 2: Build y Pruebas

Propósito: Instalar dependencias, compilar TypeScript, ejecutar pruebas, publicar artefactos

Configuración YAML:

- stage: Build
displayName: "Test and Analyze"
dependsOn:
- Setup
jobs:
- job: Build
displayName: "Build and Test"
steps:
- checkout: self
fetchDepth: 0
# Install Node.js
- task: NodeTool@0
inputs:
versionSpec: "21.x"
displayName: "Install Node.js"
# Build application
- script: |
npm install --force
npm install webpack
npm run build
# npm run test:cov # Currently commented out
continueOnError: true
displayName: "npm install and build"
# Copy files to staging
- task: CopyFiles@2
inputs:
SourceFolder: "$(Build.SourcesDirectory)"
Contents: |
**/*
!.git/**/*
!node_modules/**/*
TargetFolder: "$(Build.ArtifactStagingDirectory)"
displayName: "Copy built files"
# Publish artifacts
- task: PublishBuildArtifacts@1
inputs:
pathtoPublish: "$(Build.ArtifactStagingDirectory)"
artifactName: "nest-base-3a-mongo-artifacts"
displayName: "Publish artifact"
# Publish test coverage
- task: PublishCodeCoverageResults@2
inputs:
summaryFileLocation: "$(System.DefaultWorkingDirectory)/coverage/cobertura-coverage.xml"

Pasos Clave:

  1. Instalación de Node.js:

    • Versión: 21.x (último LTS)
    • Asegura entorno de build consistente
  2. Instalación de Dependencias:

    • npm install --force: Requerido para módulo nativo bcrypt (reconstruye para Linux)
    • npm install webpack: Requerido para build de NestJS (debería estar en devDependencies)
  3. Build:

    • npm run build: Compila TypeScript a JavaScript (directorio dist/)
    • Usa NestJS CLI y webpack para bundling
  4. Pruebas (actualmente deshabilitadas):

    • npm run test:cov: Ejecuta pruebas Jest con cobertura
    • continueOnError: true: Build tiene éxito incluso si las pruebas fallan (riesgoso para producción)
    • Recomendación: Habilitar pruebas y remover continueOnError para quality gates
  5. Publicación de Artefactos:

    • Excluye node_modules/ y .git/ para reducir tamaño de artefacto
    • Publica al almacenamiento de artefactos de Azure Pipelines
    • Descargado por etapa DockerBuild

Contenidos de Artefactos de Build:

nest-base-3a-mongo-artifacts/
├── dist/ # JavaScript compilado
├── package.json # Manifiesto de dependencias
├── package-lock.json # Archivo de lock
├── Dockerfile # Instrucciones de build Docker
├── src/ # Código fuente (para debugging)
└── coverage/ # Reportes de cobertura de pruebas

Fallas Comunes:

ErrorCausaSolución
npm ERR! code EBADPLATFORMMódulo nativo bcrypt incompatibleAsegurar uso de flag --force
webpack: command not foundWebpack no instaladoAgregar npm install webpack
npm run build failedError de compilación de TypeScriptVerificar logs para type errors
No space left on deviceAlmacenamiento de artefactos llenoLimpiar artefactos antiguos

Verificación:

  • Verificar artefacto publicado en Azure DevOps: Pipelines → [Build] → Artifacts → nest-base-3a-mongo-artifacts
  • Verificar que dist/main.js existe en el artefacto

Etapa 3: Build de Docker

Propósito: Construir imagen Docker y guardar como artefacto TAR para transferencia

Configuración YAML:

- stage: DockerBuild
displayName: "Docker Build Stage"
dependsOn:
- Setup
- Build
variables:
ENVIRONMENT: $[ stageDependencies.Setup.DetermineEnv.outputs['SetVariables.ENVIRONMENT'] ]
REPO: $[ stageDependencies.Setup.DetermineEnv.outputs['SetVariables.REPO'] ]
condition: succeeded()
jobs:
- job: DockerBuild
displayName: "Build Docker Image"
pool:
vmImage: "ubuntu-latest"
steps:
# Download build artifacts
- download: current
artifact: nest-base-3a-mongo-artifacts
# Build Docker image
- task: Docker@2
displayName: "Build Docker Image $(ENVIRONMENT)"
inputs:
containerRegistry: "GCR_ServiceConnection"
command: "build"
repository: "$(REPO)"
dockerfile: "Dockerfile"
tags: |
$(Build.BuildId)
# Save image as TAR
- script: |
echo "Listing Docker images to verify build:"
docker images
echo "Saving Docker image as TAR:"
docker save us-east1-docker.pkg.dev/$REPO:$(Build.BuildId) -o $(Pipeline.Workspace)/docker-image.tar
displayName: "Save Docker Image as TAR"
env:
REPO: $(REPO)
# Publish TAR artifact
- task: PublishBuildArtifacts@1
inputs:
pathtoPublish: "$(Pipeline.Workspace)/docker-image.tar"
artifactName: "docker-image"
displayName: "Publish Docker Image TAR"

Pasos Clave:

  1. Descargar Artefactos de Build:

    • Recupera nest-base-3a-mongo-artifacts de la etapa Build
    • Extrae al directorio de trabajo para contexto de build Docker
  2. Build Docker:

    • Dockerfile: Build multi-etapa (builder + etapa final)
    • Imagen Base: node:20-slim
    • Tag: $(Build.BuildId) (número único de build de Azure Pipeline)
    • Repositorio: Específico por entorno (ej., acralgestaproduction.azurecr.io/api-gateway)
  3. Guardado de Imagen:

    • docker save crea un archivo TAR de la imagen
    • TAR es portable entre agentes de pipeline
    • Almacenado en $(Pipeline.Workspace)/docker-image.tar
  4. Publicación de Artefacto:

    • TAR subido al almacenamiento de artefactos de Azure Pipelines
    • Descargado por etapa Deploy

Dockerfile Structure (from algesta-api-gateway-nestjs/Dockerfile):

# ============================
# Builder Stage
# ============================
FROM node:20-slim AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci
RUN npm install -g husky && npx husky install || echo "husky skipped"
COPY . .
RUN npm run build
# ============================
# Final Stage
# ============================
FROM node:20-slim
WORKDIR /app
COPY package*.json ./
# Install Puppeteer dependencies (for PDF generation)
RUN apt-get update && \
apt-get install -y wget ca-certificates fonts-liberation libappindicator3-1 \
libasound2 libatk-bridge2.0-0 libatk1.0-0 libc6 libdrm2 libgbm1 libnspr4 \
libnss3 libx11-xcb1 libxcomposite1 libxdamage1 libxrandr2 xdg-utils
# Install production dependencies (including bcrypt)
RUN npm install --force --only=production bcrypt
# Copy built application
COPY --from=builder /app/dist ./dist
ENV PUPPETEER_SKIP_CHROMIUM_DOWNLOAD=false
EXPOSE 3000
CMD ["node", "dist/main"]

Optimizaciones del Dockerfile:

  • Build multi-etapa: Reduce tamaño final de imagen (herramientas de build excluidas)
  • Imagen base slim: node:20-slim (~180MB vs ~900MB para node:20)
  • Dependencias solo de producción: --only=production excluye herramientas de dev
  • Layer caching: COPY package*.json antes de npm install para rebuilds más rápidos

Tamaño de Imagen:

  • Tamaño típico: ~350-400MB (incluye Puppeteer + Chromium)
  • Sin Puppeteer: ~150-200MB

Fallas Comunes:

ErrorCausaSolución
Dockerfile not foundProblema de descarga de artefactoAsegurar que CopyFiles@2 incluya Dockerfile
docker save: No such imageBuild falló silenciosamenteVerificar logs de build Docker para errores
permission deniedProblema con daemon de DockerReiniciar daemon de Docker en agente

Verificación:

Ventana de terminal
# En logs de Docker@2, verificar éxito del build:
Successfully built 1234567890ab
Successfully tagged us-east1-docker.pkg.dev/onklinic/onklinic-dev/api-gateway:12345

Etapa 4: Desplegar a Container Registry

Propósito: Hacer push de imagen Docker a Azure Container Registry (ACR)

Configuración YAML:

- stage: Deploy
displayName: "Publish Docker Image"
dependsOn:
- Setup
- Build
- DockerBuild
variables:
ENVIRONMENT: $[ stageDependencies.Setup.DetermineEnv.outputs['SetVariables.ENVIRONMENT'] ]
REPO: $[ stageDependencies.Setup.DetermineEnv.outputs['SetVariables.REPO'] ]
condition: succeeded()
jobs:
- job: Deploy
displayName: "Deploy Docker image to ACR"
pool:
vmImage: "ubuntu-latest"
steps:
# Download Docker image TAR
- download: current
artifact: docker-image
# Login to ACR
- task: Docker@2
inputs:
containerRegistry: "GCR_ServiceConnection"
command: "login"
displayName: "Login $(ENVIRONMENT)"
# Load, tag, and push image
- script: |
echo "Loading Docker image from TAR:"
docker load -i $(Pipeline.Workspace)/docker-image/docker-image.tar
echo "Tagging Docker image:"
docker tag us-east1-docker.pkg.dev/$REPO:$(Build.BuildId) us-east1-docker.pkg.dev/$REPO:latest
echo "Pushing Docker image:"
docker push us-east1-docker.pkg.dev/$REPO:$(Build.BuildId)
docker push us-east1-docker.pkg.dev/$REPO:latest
displayName: "Load, Tag, and Push Docker Image"
env:
REPO: $(REPO)

Pasos Clave:

  1. Descargar TAR de Imagen Docker:

    • Recupera docker-image.tar de etapa DockerBuild
    • TAR contiene la imagen Docker construida
  2. Autenticación ACR:

    • Service Connection: GCR_ServiceConnection (nombre engañoso, debería actualizarse a ACR_ServiceConnection)
    • Método de Autenticación: Service Principal con permisos ACR Push
    • Configurado en Azure DevOps: Project Settings → Service Connections → Docker Registry
  3. Carga de Imagen:

    • docker load -i docker-image.tar importa imagen al daemon Docker local
    • Restaura imagen con tags originales
  4. Etiquetado de Imagen:

    • Tag BuildId: acralgestaproduction.azurecr.io/api-gateway:12345 (inmutable, trazable)
    • Tag Latest: acralgestaproduction.azurecr.io/api-gateway:latest (mutable, conveniencia)
  5. Push de Imagen:

    • Hace push de ambos tags a ACR
    • ACR almacena imágenes en repositorios específicos por entorno

Estructura de Repositorio ACR:

acralgestadev.azurecr.io/
├── api-gateway
│ ├── 12345 (BuildId)
│ └── latest
├── ms-orders
│ ├── 12346 (BuildId)
│ └── latest
├── ms-notifications
│ ├── 12347 (BuildId)
│ └── latest
└── ms-provider
├── 12348 (BuildId)
└── latest
acralgestaproduction.azurecr.io/
├── api-gateway
│ ├── 12349 (BuildId)
│ └── latest
├── ms-orders
│ ├── 12350 (BuildId)
│ └── latest
...

¿Por Qué Dos Tags?

  • BuildId (12345): Inmutable, permite rollback a builds específicos, trazable a ejecución de pipeline
  • Latest (latest): Simplifica Despliegue de Kubernetes (siempre hace pull del más reciente), auto-actualiza en nuevos builds

Fallas Comunes:

ErrorCausaSolución
unauthorized: authentication requiredService connection expiradoActualizar credenciales de service principal en Azure DevOps
denied: requested access to the resource is deniedService principal carece de rol AcrPushOtorgar rol AcrPush en Azure Portal
manifest blob unknownImagen corrupta durante save/loadReconstruir imagen Docker

Verificación:

Ventana de terminal
# List images in ACR
az acr repository list --name acralgestaproduction --output table
# Show tags for repository
az acr repository show-tags --name acralgestaproduction --repository api-gateway --output table

Etapa 5: Desplegar a Kubernetes (Implementación Futura)

Propósito: Aplicar manifiestos de Kubernetes para desplegar imagen actualizada a AKS

Estado Actual: NO IMPLEMENTADO (pipeline aún referencia Terraform + Despliegue de Cloud Run)

Implementación Planificada:

- stage: DeployToK8S
displayName: "Deploy to AKS Stage"
dependsOn:
- Setup
- Build
- DockerBuild
- Deploy
variables:
ENVIRONMENT: $[ stageDependencies.Setup.DetermineEnv.outputs['SetVariables.ENVIRONMENT'] ]
condition: succeeded()
jobs:
- job: DeployToK8S
displayName: "Deploy to AKS"
pool:
vmImage: "ubuntu-latest"
steps:
# Install kubectl
- task: KubectlInstaller@0
inputs:
kubectlVersion: "latest"
# Download kubeconfig from Azure
- task: AzureCLI@2
inputs:
azureSubscription: "AzureServiceConnection"
scriptType: "bash"
scriptLocation: "inlineScript"
inlineScript: |
az aks get-credentials --resource-group rg-algesta-$(ENVIRONMENT) \
--name aks-algesta-$(ENVIRONMENT) \
--overwrite-existing
# Update deployment image
- script: |
kubectl set image deployment/api-gateway-$(ENVIRONMENT) \
api-gateway=acralgestaproduction.azurecr.io/api-gateway:$(Build.BuildId) \
--namespace=$(ENVIRONMENT)
displayName: "Update Kubernetes Deployment"
# Wait for rollout
- script: |
kubectl rollout status deployment/api-gateway-$(ENVIRONMENT) \
--namespace=$(ENVIRONMENT) \
--timeout=5m
displayName: "Verify Deployment Rollout"

Pasos Clave (cuando se implemente):

  1. Instalación de kubectl:

    • Usa tarea KubectlInstaller@0 para instalar último kubectl
  2. Autenticación AKS:

    • az aks get-credentials descarga kubeconfig
    • Requiere service connection de Azure CLI con permisos de lectura AKS
  3. Actualización de Imagen:

    • kubectl set image actualiza Despliegue para usar nuevo tag BuildId
    • Dispara actualización rolling (cero tiempo de inactividad)
  4. Verificación de Rollout:

    • kubectl rollout status espera que Despliegue se estabilice
    • Falla el pipeline si rollout falla (ej., errores de image pull, crash loops)

Alternativa: GitOps con ArgoCD (recomendado para producción):

- script: |
# Update Kubernetes manifest in Git repository
sed -i "s|image: acralgestaproduction.azurecr.io/api-gateway:.*|image: acralgestaproduction.azurecr.io/api-gateway:$(Build.BuildId)|" \
ops-algesta/k8s-manifests/$(ENVIRONMENT)/api-gateway-deployment.yaml
git add ops-algesta/k8s-manifests/$(ENVIRONMENT)/api-gateway-deployment.yaml
git commit -m "Update api-gateway image to $(Build.BuildId)"
git push
displayName: "Update GitOps Manifest"

ArgoCD then automatically syncs changes to AKS cluster.


Environment Determination

Tag Naming Convention

Format: v{MAJOR}.{MINOR}.{PATCH}-{ENVIRONMENT}

Examples:

  • v1.0.0-dev: Development Despliegue
  • v1.2.3-test: Test Despliegue
  • v2.0.0-prod: Production Despliegue

Version Number Conventions:

  • MAJOR: Breaking changes (e.g., API v1 → v2)
  • MINOR: New Funcionalidades, backward-compatible
  • PATCH: Bug fixes, no new Funcionalidades

Environment Configuration

EnvironmentSuffixAKS NamespaceACR RepositorioIngress Host
Development-devdevelopmentacralgestadev.azurecr.io/algesta-api-dev.3astronautas.com
Test-testPruebasacralgestatest.azurecr.io/algesta-api-test.3astronautas.com
Production-prodproductionacralgestaproduction.azurecr.io/algesta-api-prod.3astronautas.com

Service Connections and Variables

Azure DevOps Service Connections

Location: Azure DevOps → Project Settings → Service Connections

Connection NameTypePurposeConfiguration
GCR_ServiceConnectionDocker RegistryACR authentication (name misleading)Service Principal with AcrPush role
AzureServiceConnectionAzure Resource ManagerAKS accessService Principal with AKS Contributor role
sonarCloudSonarCloudCode quality analysis (optional)SonarCloud token

Updating Service Principal Credentials:

Ventana de terminal
# Create new service principal
az ad sp create-for-rbac --name sp-algesta-cicd --role Contributor --scopes /subscriptions/{subscription-id}
# Assign ACR push permissions
az role assignment create --assignee {sp-client-id} \
--role AcrPush \
--scope /subscriptions/{subscription-id}/resourceGroups/rg-algesta-production/providers/Microsoft.ContainerRegistry/registries/acralgestaproduction

Pipeline Variables

Location: Azure DevOps → Pipelines → [Pipeline] → Edit → Variables

Variable NameTypeDescriptionExample Valor
AZURE_DEVOPS_PATSecretPersonal Access Token for Git Operacionesxxxxxxxxxxxxxxxxxxxxx
KEY_GCPSecretGCP service account key (legacy, remove){"type": "service_account", ...}
STRUCTURIZR_WORKSPACE_IDStandardWorkspace ID for C4 diagrams123456
STRUCTURIZR_API_KEYSecretStructurizr API keykey-xxxxxxxxxxxxxxxx
STRUCTURIZR_API_SECRETSecretStructurizr API secretsecret-xxxxxxxxxxxxxxxx
GIT_PATSecretGit Personal Access Tokenxxxxxxxxxxxxxxxxxxxxx

Variable Groups (recommended for centralized management):

variables:
- group: algesta-dev-vars
- group: algesta-prod-vars

Create variable groups in: Azure DevOps → Pipelines → Library → Variable Groups


Despliegue Flow

End-to-End Despliegue Workflow

1. Developer Prepares Code:

Ventana de terminal
# Ensure tests pass locally
npm test
# Commit and push changes
git add .
git commit -m "feat: add new feature"
git push origin main

2. Create and Push Tag:

Ventana de terminal
# For development deployment
git tag v1.2.3-dev
git push origin v1.2.3-dev
# For production deployment (from main branch)
git checkout main
git tag v1.2.3-prod
git push origin v1.2.3-prod

3. Pipeline Execution:

  • Stage 1 (Setup): Parses tag, sets ENVIRONMENT=development, REPO=...
  • Stage 2 (Build): Installs dependencies, builds app, publishes artifacts (~3-5 minutes)
  • Stage 3 (DockerBuild): Builds Docker image, saves as TAR (~2-3 minutes)
  • Stage 4 (Deploy): Pushes image to ACR (~1-2 minutes)
  • Stage 5 (DeployToK8S): (Future) Deploys to AKS (~2-3 minutes)

Total Duration: ~10-15 minutes per Despliegue

4. Manual Despliegue to AKS (current workaround):

Ventana de terminal
# Get AKS credentials
az aks get-credentials --resource-group rg-algesta-production --name aks-algesta-production
# Update deployment with new image
kubectl set image deployment/api-gateway-production \
api-gateway=acralgestaproduction.azurecr.io/api-gateway:12345 \
--namespace=production
# Verify rollout
kubectl rollout status deployment/api-gateway-production --namespace=production

5. Verification:

Ventana de terminal
# Check pod status
kubectl get pods -n production
# Test health endpoint
curl https://algesta-api-prod.3astronautas.com/health

Troubleshooting Pipelines

Issue: Pipeline Not Triggered by Tag

Symptoms:

  • Push tag, but pipeline doesn’t start

Diagnosis:

trigger:
tags:
include:
- v*
pr: none

Solutions:

  1. Verify trigger configuration: Ensure tags: include: v* in azure-pipelines.yml
  2. Check tag format: Tag must match pattern (e.g., v1.0.0-dev, not 1.0.0-dev)
  3. Branch protection: If pipeline requires specific branch, ensure tag is on that branch

Verification:

Ventana de terminal
# Check tag is pushed to remote
git ls-remote --tags origin
# Manually trigger pipeline in Azure DevOps
# Pipelines → [Pipeline] → Run pipeline → Select tag

Issue: Build Stage Fails with bcrypt Error

Symptoms:

npm ERR! code EBADPLATFORM
npm ERR! notsup Unsupported platform for bcrypt@5.1.1: wanted {"os":"linux"} (current: {"os":"darwin"})

Diagnosis:

  • bcrypt is a native Node.js module that compiles C++ code
  • Requires rebuilding for target platform (Linux in Docker)

Solutions:

  1. Use --force flag in Dockerfile:

    RUN npm install --force --only=production bcrypt
  2. Alternative: Use bcryptjs (pure JavaScript):

    Ventana de terminal
    npm uninstall bcrypt
    npm install bcryptjs
  3. Ensure Dockerfile uses multi-stage build:

    # Builder stage compiles TypeScript
    FROM node:20-slim AS builder
    RUN npm ci
    # Final stage installs production deps (including bcrypt)
    FROM node:20-slim
    RUN npm install --force --only=production

Issue: Docker Push Fails with Authentication Error

Symptoms:

unauthorized: authentication required
denied: requested access to the resource is denied

Diagnosis:

  • Service connection credentials expired or incorrect
  • Service principal lacks ACR permissions

Solutions:

  1. Verify Service Connection:

    • Azure DevOps → Project Settings → Service Connections → GCR_ServiceConnection
    • Click “Verify” to test connection
    • If failed, update credentials
  2. Check Service Principal Permissions:

    Ventana de terminal
    # Get service principal ID from service connection
    SP_ID="xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
    # Check role assignments
    az role assignment list --assignee $SP_ID --output table
    # Grant AcrPush role if missing
    az role assignment create --assignee $SP_ID \
    --role AcrPush \
    --scope /subscriptions/{subscription-id}/resourceGroups/rg-algesta-production/providers/Microsoft.ContainerRegistry/registries/acralgestaproduction
  3. Regenerate Service Principal Secret:

    Ventana de terminal
    # Reset credentials
    az ad sp credential reset --id $SP_ID
    # Update service connection in Azure DevOps with new secret

Issue: DeployToK8S Stage Fails (Future Implementación)

Symptoms:

kubectl set image: error: timed out waiting for the condition

Diagnosis:

  • Image pull errors (ACR authentication issue)
  • Application crashes on startup (missing env vars)
  • Insufficient resources (CPU/memory limits)

Solutions:

  1. Check Pod Estado:

    Ventana de terminal
    kubectl get pods -n production
    kubectl describe pod api-gateway-production-xxxxx-yyyyy -n production
  2. Check Pod Logs:

    Ventana de terminal
    kubectl logs api-gateway-production-xxxxx-yyyyy -n production
  3. Common Issues:

    • ImagePullBackOff: AKS can’t pull image from ACR

      Ventana de terminal
      # Verify ACR integration
      az aks check-acr --name aks-algesta-production --resource-group rg-algesta-production --acr acralgestaproduction.azurecr.io
    • CrashLoopBackOff: Application crashes on startup

      Ventana de terminal
      # Check application logs for errors (missing env vars, DB connection)
      kubectl logs api-gateway-production-xxxxx-yyyyy -n production
    • Insufficient Resources:

      Ventana de terminal
      # Check resource requests/limits
      kubectl describe deployment api-gateway-production -n production
      # Increase limits if needed
      kubectl set resources deployment api-gateway-production \
      --limits=cpu=1000m,memory=1Gi \
      --requests=cpu=500m,memory=512Mi \
      -n production

Issue: Pipeline Runs But Doesn’t Deploy Latest Code

Symptoms:

  • Pipeline succeeds, but deployed application shows old code

Diagnosis:

  • Docker layer caching used old code
  • Kubernetes Despliegue uses :latest tag (not updated)

Solutions:

  1. Force Docker Rebuild (no cache):

    - task: Docker@2
    inputs:
    command: "build"
    arguments: "--no-cache"
  2. Use BuildId Tag Instead of latest:

    # In Kubernetes deployment, use specific tag
    kubectl set image deployment/api-gateway-production \
    api-gateway=acralgestaproduction.azurecr.io/api-gateway:$(Build.BuildId) \
    --namespace=production
  3. Restart Despliegue (forces image pull):

    Ventana de terminal
    kubectl rollout restart deployment/api-gateway-production -n production

Related Documentoation:

For Support:

  • Check Azure Pipeline logs: Pipelines → [Build] → Logs
  • Review ACR images: az acr Repositorio show-tags ...
  • Verify AKS Despliegues: kubectl get Despliegues -n production
  • Contact DevOps team for service connection issues