Pipelines CI/CD
Tabla de Contenidos
- Propósito
- ¿Para quién es esto?
- Descripción General del Pipeline
- Arquitectura del Pipeline
- Desglose de Etapas
- Determinación de Entorno
- Conexiones de Servicio y Variables
- Flujo de Despliegue
- 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:
| Repositorio | Archivo de Pipeline | Objetivo de Despliegue | Disparador |
|---|---|---|---|
algesta-api-gateway-nestjs | azure-pipelines.yml | AKS (production/development/Pruebas namespaces) | Tag: v*-dev, v*-test, v*-prod |
algesta-ms-orders-nestjs | azure-pipelines.yml | AKS (production/development/Pruebas namespaces) | Tag: v*-dev, v*-test, v*-prod |
algesta-ms-notifications-nestjs | azure-pipelines.yml | AKS (production/development/Pruebas namespaces) | Tag: v*-dev, v*-test, v*-prod |
algesta-ms-provider-nestjs | azure-pipelines.yml | AKS (production/development/Pruebas namespaces) | Tag: v*-dev, v*-test, v*-prod |
algesta-dashboard-react | azure-pipelines.yml | Firebase Hosting (Despliegue manual) | Tag: v*-dev, v*-prod |
Algesta (mono-repo) | azure-pipelines.yml | Actualizaciones de diagramas Structurizr | Branch: main |
Selección de Entorno Basada en Tags
Patrón de Tag → Mapeo de Entorno:
v1.2.3-dev → entorno developmentv1.2.3-test → entorno testv1.2.3-prod → entorno productionEjemplo de Flujo de Trabajo:
- Desarrollador hace push de tag:
git tag v2.1.0-dev && git push origin v2.1.0-dev - Azure Pipeline detecta el tag vía configuración de trigger
- Etapa
Setupparsea el sufijo del tag (-dev) y establece variables de entorno - 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: SetVariablesLógica Clave:
- Extraer nombre de tag desde
Build.SourceBranchName(ej.,v1.2.3-dev) - Parsear número de versión:
v1.2.3-dev→v1.2.3 - Coincidencia de patrón en sufijo:
-dev→ENVIRONMENT=development,REPO=onklinic/onklinic-dev/api-gateway-test→ENVIRONMENT=test,REPO=onklinic/onklinic-test/api-gateway-prod→ENVIRONMENT=production,REPO=onklinic/onklinic-prod/api-gateway
- Establecer variables de salida para etapas subsiguientes:
ENVIRONMENT: Selector de namespace AKSREPO: 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.SourceBranchNameretorna nombre de branch, causando falla
Verificación: Verificar logs del pipeline para:
Version detected: v1.2.3Tag detected: v1.2.3-devEnvironment detected: developmentEtapa 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:
-
Instalación de Node.js:
- Versión:
21.x(último LTS) - Asegura entorno de build consistente
- Versión:
-
Instalación de Dependencias:
npm install --force: Requerido para módulo nativobcrypt(reconstruye para Linux)npm install webpack: Requerido para build de NestJS (debería estar endevDependencies)
-
Build:
npm run build: Compila TypeScript a JavaScript (directoriodist/)- Usa NestJS CLI y webpack para bundling
-
Pruebas (actualmente deshabilitadas):
npm run test:cov: Ejecuta pruebas Jest con coberturacontinueOnError: true: Build tiene éxito incluso si las pruebas fallan (riesgoso para producción)- Recomendación: Habilitar pruebas y remover
continueOnErrorpara quality gates
-
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
- Excluye
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 pruebasFallas Comunes:
| Error | Causa | Solución |
|---|---|---|
npm ERR! code EBADPLATFORM | Módulo nativo bcrypt incompatible | Asegurar uso de flag --force |
webpack: command not found | Webpack no instalado | Agregar npm install webpack |
npm run build failed | Error de compilación de TypeScript | Verificar logs para type errors |
No space left on device | Almacenamiento de artefactos lleno | Limpiar artefactos antiguos |
Verificación:
- Verificar artefacto publicado en Azure DevOps: Pipelines → [Build] → Artifacts → nest-base-3a-mongo-artifacts
- Verificar que
dist/main.jsexiste 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:
-
Descargar Artefactos de Build:
- Recupera
nest-base-3a-mongo-artifactsde la etapa Build - Extrae al directorio de trabajo para contexto de build Docker
- Recupera
-
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)
-
Guardado de Imagen:
docker savecrea un archivo TAR de la imagen- TAR es portable entre agentes de pipeline
- Almacenado en
$(Pipeline.Workspace)/docker-image.tar
-
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 /appCOPY package*.json ./RUN npm ciRUN npm install -g husky && npx husky install || echo "husky skipped"COPY . .RUN npm run build
# ============================# Final Stage# ============================FROM node:20-slim
WORKDIR /appCOPY 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 applicationCOPY --from=builder /app/dist ./dist
ENV PUPPETEER_SKIP_CHROMIUM_DOWNLOAD=falseEXPOSE 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 paranode:20) - Dependencias solo de producción:
--only=productionexcluye herramientas de dev - Layer caching:
COPY package*.jsonantes denpm installpara rebuilds más rápidos
Tamaño de Imagen:
- Tamaño típico: ~350-400MB (incluye Puppeteer + Chromium)
- Sin Puppeteer: ~150-200MB
Fallas Comunes:
| Error | Causa | Solución |
|---|---|---|
Dockerfile not found | Problema de descarga de artefacto | Asegurar que CopyFiles@2 incluya Dockerfile |
docker save: No such image | Build falló silenciosamente | Verificar logs de build Docker para errores |
permission denied | Problema con daemon de Docker | Reiniciar daemon de Docker en agente |
Verificación:
# En logs de Docker@2, verificar éxito del build:Successfully built 1234567890abSuccessfully tagged us-east1-docker.pkg.dev/onklinic/onklinic-dev/api-gateway:12345Etapa 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:
-
Descargar TAR de Imagen Docker:
- Recupera
docker-image.tarde etapa DockerBuild - TAR contiene la imagen Docker construida
- Recupera
-
Autenticación ACR:
- Service Connection:
GCR_ServiceConnection(nombre engañoso, debería actualizarse aACR_ServiceConnection) - Método de Autenticación: Service Principal con permisos ACR Push
- Configurado en Azure DevOps: Project Settings → Service Connections → Docker Registry
- Service Connection:
-
Carga de Imagen:
docker load -i docker-image.tarimporta imagen al daemon Docker local- Restaura imagen con tags originales
-
Etiquetado de Imagen:
- Tag BuildId:
acralgestaproduction.azurecr.io/api-gateway:12345(inmutable, trazable) - Tag Latest:
acralgestaproduction.azurecr.io/api-gateway:latest(mutable, conveniencia)
- Tag BuildId:
-
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:
| Error | Causa | Solución |
|---|---|---|
unauthorized: authentication required | Service connection expirado | Actualizar credenciales de service principal en Azure DevOps |
denied: requested access to the resource is denied | Service principal carece de rol AcrPush | Otorgar rol AcrPush en Azure Portal |
manifest blob unknown | Imagen corrupta durante save/load | Reconstruir imagen Docker |
Verificación:
# List images in ACRaz acr repository list --name acralgestaproduction --output table
# Show tags for repositoryaz acr repository show-tags --name acralgestaproduction --repository api-gateway --output tableEtapa 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):
-
Instalación de kubectl:
- Usa tarea
KubectlInstaller@0para instalar último kubectl
- Usa tarea
-
Autenticación AKS:
az aks get-credentialsdescarga kubeconfig- Requiere service connection de Azure CLI con permisos de lectura AKS
-
Actualización de Imagen:
kubectl set imageactualiza Despliegue para usar nuevo tag BuildId- Dispara actualización rolling (cero tiempo de inactividad)
-
Verificación de Rollout:
kubectl rollout statusespera 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 Desplieguev1.2.3-test: Test Desplieguev2.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
| Environment | Suffix | AKS Namespace | ACR Repositorio | Ingress Host |
|---|---|---|---|---|
| Development | -dev | development | acralgestadev.azurecr.io/ | algesta-api-dev.3astronautas.com |
| Test | -test | Pruebas | acralgestatest.azurecr.io/ | algesta-api-test.3astronautas.com |
| Production | -prod | production | acralgestaproduction.azurecr.io/ | algesta-api-prod.3astronautas.com |
Service Connections and Variables
Azure DevOps Service Connections
Location: Azure DevOps → Project Settings → Service Connections
| Connection Name | Type | Purpose | Configuration |
|---|---|---|---|
GCR_ServiceConnection | Docker Registry | ACR authentication (name misleading) | Service Principal with AcrPush role |
AzureServiceConnection | Azure Resource Manager | AKS access | Service Principal with AKS Contributor role |
sonarCloud | SonarCloud | Code quality analysis (optional) | SonarCloud token |
Updating Service Principal Credentials:
# Create new service principalaz ad sp create-for-rbac --name sp-algesta-cicd --role Contributor --scopes /subscriptions/{subscription-id}
# Assign ACR push permissionsaz role assignment create --assignee {sp-client-id} \ --role AcrPush \ --scope /subscriptions/{subscription-id}/resourceGroups/rg-algesta-production/providers/Microsoft.ContainerRegistry/registries/acralgestaproductionPipeline Variables
Location: Azure DevOps → Pipelines → [Pipeline] → Edit → Variables
| Variable Name | Type | Description | Example Valor |
|---|---|---|---|
AZURE_DEVOPS_PAT | Secret | Personal Access Token for Git Operaciones | xxxxxxxxxxxxxxxxxxxxx |
KEY_GCP | Secret | GCP service account key (legacy, remove) | {"type": "service_account", ...} |
STRUCTURIZR_WORKSPACE_ID | Standard | Workspace ID for C4 diagrams | 123456 |
STRUCTURIZR_API_KEY | Secret | Structurizr API key | key-xxxxxxxxxxxxxxxx |
STRUCTURIZR_API_SECRET | Secret | Structurizr API secret | secret-xxxxxxxxxxxxxxxx |
GIT_PAT | Secret | Git Personal Access Token | xxxxxxxxxxxxxxxxxxxxx |
Variable Groups (recommended for centralized management):
variables: - group: algesta-dev-vars - group: algesta-prod-varsCreate variable groups in: Azure DevOps → Pipelines → Library → Variable Groups
Despliegue Flow
End-to-End Despliegue Workflow
1. Developer Prepares Code:
# Ensure tests pass locallynpm test
# Commit and push changesgit add .git commit -m "feat: add new feature"git push origin main2. Create and Push Tag:
# For development deploymentgit tag v1.2.3-devgit push origin v1.2.3-dev
# For production deployment (from main branch)git checkout maingit tag v1.2.3-prodgit push origin v1.2.3-prod3. 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):
# Get AKS credentialsaz aks get-credentials --resource-group rg-algesta-production --name aks-algesta-production
# Update deployment with new imagekubectl set image deployment/api-gateway-production \ api-gateway=acralgestaproduction.azurecr.io/api-gateway:12345 \ --namespace=production
# Verify rolloutkubectl rollout status deployment/api-gateway-production --namespace=production5. Verification:
# Check pod statuskubectl get pods -n production
# Test health endpointcurl https://algesta-api-prod.3astronautas.com/healthTroubleshooting Pipelines
Issue: Pipeline Not Triggered by Tag
Symptoms:
- Push tag, but pipeline doesn’t start
Diagnosis:
trigger: tags: include: - v*pr: noneSolutions:
- Verify trigger configuration: Ensure
tags: include: v*inazure-pipelines.yml - Check tag format: Tag must match pattern (e.g.,
v1.0.0-dev, not1.0.0-dev) - Branch protection: If pipeline requires specific branch, ensure tag is on that branch
Verification:
# Check tag is pushed to remotegit ls-remote --tags origin
# Manually trigger pipeline in Azure DevOps# Pipelines → [Pipeline] → Run pipeline → Select tagIssue: Build Stage Fails with bcrypt Error
Symptoms:
npm ERR! code EBADPLATFORMnpm 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:
-
Use
--forceflag in Dockerfile:RUN npm install --force --only=production bcrypt -
Alternative: Use bcryptjs (pure JavaScript):
Ventana de terminal npm uninstall bcryptnpm install bcryptjs -
Ensure Dockerfile uses multi-stage build:
# Builder stage compiles TypeScriptFROM node:20-slim AS builderRUN npm ci# Final stage installs production deps (including bcrypt)FROM node:20-slimRUN npm install --force --only=production
Issue: Docker Push Fails with Authentication Error
Symptoms:
unauthorized: authentication requireddenied: requested access to the resource is deniedDiagnosis:
- Service connection credentials expired or incorrect
- Service principal lacks ACR permissions
Solutions:
-
Verify Service Connection:
- Azure DevOps → Project Settings → Service Connections →
GCR_ServiceConnection - Click “Verify” to test connection
- If failed, update credentials
- Azure DevOps → Project Settings → Service Connections →
-
Check Service Principal Permissions:
Ventana de terminal # Get service principal ID from service connectionSP_ID="xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"# Check role assignmentsaz role assignment list --assignee $SP_ID --output table# Grant AcrPush role if missingaz role assignment create --assignee $SP_ID \--role AcrPush \--scope /subscriptions/{subscription-id}/resourceGroups/rg-algesta-production/providers/Microsoft.ContainerRegistry/registries/acralgestaproduction -
Regenerate Service Principal Secret:
Ventana de terminal # Reset credentialsaz 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 conditionDiagnosis:
- Image pull errors (ACR authentication issue)
- Application crashes on startup (missing env vars)
- Insufficient resources (CPU/memory limits)
Solutions:
-
Check Pod Estado:
Ventana de terminal kubectl get pods -n productionkubectl describe pod api-gateway-production-xxxxx-yyyyy -n production -
Check Pod Logs:
Ventana de terminal kubectl logs api-gateway-production-xxxxx-yyyyy -n production -
Common Issues:
-
ImagePullBackOff: AKS can’t pull image from ACR
Ventana de terminal # Verify ACR integrationaz 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/limitskubectl describe deployment api-gateway-production -n production# Increase limits if neededkubectl 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
:latesttag (not updated)
Solutions:
-
Force Docker Rebuild (no cache):
- task: Docker@2inputs:command: "build"arguments: "--no-cache" -
Use BuildId Tag Instead of
latest:# In Kubernetes deployment, use specific tagkubectl set image deployment/api-gateway-production \api-gateway=acralgestaproduction.azurecr.io/api-gateway:$(Build.BuildId) \--namespace=production -
Restart Despliegue (forces image pull):
Ventana de terminal kubectl rollout restart deployment/api-gateway-production -n production
Related Documentoation:
- Despliegue Guía: Manual Despliegue procedures
- Infrastructure as Code: ACR and AKS provisioning
- Kubernetes Operaciones: AKS cluster management
- Runbooks: Common operational procedures
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