3. Infraestructura y Despliegue
3. Infraestructura y Despliegue
Esta sección cubre la infraestructura técnica, entornos de despliegue, estrategias de CI/CD y consideraciones de escalabilidad para el sistema DTEM.
3.1. Arquitectura de Entornos
3.1.1. Entornos Definidos
Development Environment
- Propósito: Desarrollo local y pruebas unitarias
- Infraestructura: Local/Docker Desktop
- Datos: Datos de prueba y mock
- Acceso: Desarrolladores individuales
Staging Environment
- Propósito: Integración y pruebas de aceptación
- Infraestructura: Cloud (similar a producción)
- Datos: Datos anonimizados de producción
- Acceso: Equipo QA y desarrolladores
Production Environment
- Propósito: Operación del sistema en vivo
- Infraestructura: Cloud/On-premise de alta disponibilidad
- Datos: Datos reales de clientes
- Acceso: Personal autorizado únicamente
3.1.2. Diagrama de Entornos
┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐
│ Development │ │ Staging │ │ Production │
│ │ │ │ │ │
│ ┌─────────────┐ │ │ ┌─────────────┐ │ │ ┌─────────────┐ │
│ │Local Docker │ │ │ │Cloud VMs │ │ │ │Cloud Cluster│ │
│ │ │ │ │ │ │ │ │ │ │ │
│ │- PostgreSQL │ │ │ │- PostgreSQL │ │ │ │- PostgreSQL │ │
│ │- Redis │ │ │ │- Redis │ │ │ │- Redis │ │
│ │- Node.js │ │ │ │- Node.js │ │ │ │- Node.js │ │
│ └─────────────┘ │ │ └─────────────┘ │ │ └─────────────┘ │
│ │ │ │ │ │
│ Git Integration │───▶│ CI/CD Pipeline │───▶│ Auto Deploy │
└─────────────────┘ └─────────────────┘ └─────────────────┘
3.2. Docker y Contenerización
3.2.1. Estrategia de Contenerización
Multi-stage Builds
# Dockerfile.base
FROM node:18-alpine AS base
WORKDIR /app
COPY package*.json ./
RUN npm ci --only=production && npm cache clean --force
# Dockerfile.development
FROM base AS development
RUN npm ci
COPY . .
EXPOSE 3000
CMD ["npm", "run", "dev"]
# Dockerfile.production
FROM base AS production
COPY --from=development /app/dist ./dist
EXPOSE 3000
USER node
CMD ["node", "dist/index.js"]
Optimización de Imágenes
# Optimización para producción
FROM node:18-alpine AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci --only=production
FROM node:18-alpine AS runtime
RUN addgroup -g 1001 -S nodejs
RUN adduser -S nextjs -u 1001
WORKDIR /app
COPY --from=builder /app/node_modules ./node_modules
COPY --chown=nextjs:nodejs . .
USER nextjs
EXPOSE 3000
CMD ["npm", "start"]
3.2.2. Docker Compose para Desarrollo
# docker-compose.dev.yml
version: '3.8'
services:
api:
build:
context: .
target: development
volumes:
- .:/app
- /app/node_modules
environment:
- NODE_ENV=development
- DEBUG=*
ports:
- "3000:3000"
- "9229:9229" # Debug port
command: npm run dev:debug
postgres:
image: postgres:14-alpine
environment:
POSTGRES_DB: dtem_dev
POSTGRES_USER: dev
POSTGRES_PASSWORD: dev123
ports:
- "5432:5432"
volumes:
- postgres_dev_data:/var/lib/postgresql/data
redis:
image: redis:7-alpine
ports:
- "6379:6379"
command: redis-server --appendonly yes
volumes:
postgres_dev_data:
3.3. Kubernetes para Producción
3.3.1. Arquitectura Kubernetes
# namespace.yaml
apiVersion: v1
kind: Namespace
metadata:
name: dtem-prod
labels:
name: dtem-prod
environment: production
---
# configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: dtem-config
namespace: dtem-prod
data:
NODE_ENV: "production"
LOG_LEVEL: "info"
API_PORT: "3000"
DB_HOST: "postgres-service"
REDIS_HOST: "redis-service"
---
# secret.yaml
apiVersion: v1
kind: Secret
metadata:
name: dtem-secrets
namespace: dtem-prod
type: Opaque
data:
DB_PASSWORD: <base64-encoded-password>
JWT_SECRET: <base64-encoded-jwt-secret>
REDIS_PASSWORD: <base64-encoded-redis-password>
3.3.2. Deployments
# api-gateway-deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: api-gateway
namespace: dtem-prod
labels:
app: api-gateway
spec:
replicas: 3
selector:
matchLabels:
app: api-gateway
template:
metadata:
labels:
app: api-gateway
spec:
containers:
- name: api-gateway
image: dtem/api-gateway:latest
ports:
- containerPort: 3000
envFrom:
- configMapRef:
name: dtem-config
- secretRef:
name: dtem-secrets
resources:
requests:
memory: "256Mi"
cpu: "250m"
limits:
memory: "512Mi"
cpu: "500m"
livenessProbe:
httpGet:
path: /health
port: 3000
initialDelaySeconds: 30
periodSeconds: 10
readinessProbe:
httpGet:
path: /ready
port: 3000
initialDelaySeconds: 5
periodSeconds: 5
---
# dte-service-deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: dte-service
namespace: dtem-prod
spec:
replicas: 5
selector:
matchLabels:
app: dte-service
template:
metadata:
labels:
app: dte-service
spec:
containers:
- name: dte-service
image: dtem/dte-service:latest
envFrom:
- configMapRef:
name: dtem-config
- secretRef:
name: dtem-secrets
resources:
requests:
memory: "512Mi"
cpu: "500m"
limits:
memory: "1Gi"
cpu: "1000m"
3.3.3. Services y Load Balancing
# api-gateway-service.yaml
apiVersion: v1
kind: Service
metadata:
name: api-gateway-service
namespace: dtem-prod
spec:
selector:
app: api-gateway
ports:
- protocol: TCP
port: 80
targetPort: 3000
type: ClusterIP
---
# ingress.yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: dtem-ingress
namespace: dtem-prod
annotations:
kubernetes.io/ingress.class: nginx
cert-manager.io/cluster-issuer: letsencrypt-prod
nginx.ingress.kubernetes.io/rate-limit: "100"
nginx.ingress.kubernetes.io/rate-limit-window: "1m"
spec:
tls:
- hosts:
- api.dtem.empresa.cl
- dtem.empresa.cl
secretName: dtem-tls
rules:
- host: api.dtem.empresa.cl
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: api-gateway-service
port:
number: 80
- host: dtem.empresa.cl
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: web-frontend-service
port:
number: 80
3.4. CI/CD Pipeline
3.4.1. GitLab CI Configuration
# .gitlab-ci.yml
stages:
- test
- build
- deploy-staging
- deploy-production
variables:
DOCKER_REGISTRY: registry.gitlab.com/empresa/dtem
KUBERNETES_NAMESPACE_STAGING: dtem-staging
KUBERNETES_NAMESPACE_PROD: dtem-prod
# Test Stage
test:unit:
stage: test
image: node:18-alpine
script:
- npm ci
- npm run test:unit
coverage: '/Lines\s*:\s*(\d+\.\d+)%/'
artifacts:
reports:
junit: junit.xml
coverage_report:
coverage_format: cobertura
path: coverage/cobertura-coverage.xml
test:integration:
stage: test
image: docker:20.10
services:
- docker:20.10-dind
script:
- docker-compose -f docker-compose.test.yml up -d
- npm run test:integration
- docker-compose -f docker-compose.test.yml down
dependencies:
- test:unit
# Build Stage
build:docker:
stage: build
image: docker:20.10
services:
- docker:20.10-dind
script:
- docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD $CI_REGISTRY
- docker build -t $DOCKER_REGISTRY/api-gateway:$CI_COMMIT_SHA -f Dockerfile.gateway .
- docker build -t $DOCKER_REGISTRY/dte-service:$CI_COMMIT_SHA -f Dockerfile.dte .
- docker push $DOCKER_REGISTRY/api-gateway:$CI_COMMIT_SHA
- docker push $DOCKER_REGISTRY/dte-service:$CI_COMMIT_SHA
only:
- main
- develop
# Deploy Staging
deploy:staging:
stage: deploy-staging
image: bitnami/kubectl:latest
script:
- kubectl config use-context $KUBE_CONTEXT_STAGING
- kubectl set image deployment/api-gateway api-gateway=$DOCKER_REGISTRY/api-gateway:$CI_COMMIT_SHA -n $KUBERNETES_NAMESPACE_STAGING
- kubectl set image deployment/dte-service dte-service=$DOCKER_REGISTRY/dte-service:$CI_COMMIT_SHA -n $KUBERNETES_NAMESPACE_STAGING
- kubectl rollout status deployment/api-gateway -n $KUBERNETES_NAMESPACE_STAGING
- kubectl rollout status deployment/dte-service -n $KUBERNETES_NAMESPACE_STAGING
environment:
name: staging
url: https://staging.dtem.empresa.cl
only:
- develop
# Deploy Production
deploy:production:
stage: deploy-production
image: bitnami/kubectl:latest
script:
- kubectl config use-context $KUBE_CONTEXT_PROD
- kubectl set image deployment/api-gateway api-gateway=$DOCKER_REGISTRY/api-gateway:$CI_COMMIT_SHA -n $KUBERNETES_NAMESPACE_PROD
- kubectl set image deployment/dte-service dte-service=$DOCKER_REGISTRY/dte-service:$CI_COMMIT_SHA -n $KUBERNETES_NAMESPACE_PROD
- kubectl rollout status deployment/api-gateway -n $KUBERNETES_NAMESPACE_PROD
- kubectl rollout status deployment/dte-service -n $KUBERNETES_NAMESPACE_PROD
environment:
name: production
url: https://dtem.empresa.cl
when: manual
only:
- main
3.4.2. Quality Gates
# quality-gate.yml
quality_gate:
stage: test
image: sonarsource/sonar-scanner-cli:latest
script:
- sonar-scanner
-Dsonar.projectKey=dtem
-Dsonar.sources=src
-Dsonar.host.url=$SONAR_HOST_URL
-Dsonar.login=$SONAR_TOKEN
-Dsonar.qualitygate.wait=true
allow_failure: false
only:
- merge_requests
- main
- develop
security:scan:
stage: test
image: owasp/zap2docker-stable
script:
- mkdir -p /zap/wrk/
- /zap/zap-baseline.py -t http://api-gateway:3000 -J gl-sast-report.json || true
artifacts:
reports:
sast: gl-sast-report.json
only:
- main
- develop
3.5. Escalabilidad y Auto-scaling
3.5.1. Horizontal Pod Autoscaler (HPA)
# hpa.yaml
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: api-gateway-hpa
namespace: dtem-prod
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: api-gateway
minReplicas: 3
maxReplicas: 10
metrics:
- type: Resource
resource:
name: cpu
target:
type: Utilization
averageUtilization: 70
- type: Resource
resource:
name: memory
target:
type: Utilization
averageUtilization: 80
behavior:
scaleDown:
stabilizationWindowSeconds: 300
policies:
- type: Percent
value: 10
periodSeconds: 60
scaleUp:
stabilizationWindowSeconds: 60
policies:
- type: Percent
value: 50
periodSeconds: 60
---
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: dte-service-hpa
namespace: dtem-prod
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: dte-service
minReplicas: 5
maxReplicas: 20
metrics:
- type: Resource
resource:
name: cpu
target:
type: Utilization
averageUtilization: 60
- type: Pods
pods:
metric:
name: dte_processing_rate
target:
type: AverageValue
averageValue: "10"
3.5.2. Vertical Pod Autoscaler (VPA)
# vpa.yaml
apiVersion: autoscaling.k8s.io/v1
kind: VerticalPodAutoscaler
metadata:
name: api-gateway-vpa
namespace: dtem-prod
spec:
targetRef:
apiVersion: apps/v1
kind: Deployment
name: api-gateway
updatePolicy:
updateMode: "Auto"
resourcePolicy:
containerPolicies:
- containerName: api-gateway
maxAllowed:
cpu: 2
memory: 2Gi
minAllowed:
cpu: 100m
memory: 128Mi
3.5.3. Cluster Autoscaler
# cluster-autoscaler.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: cluster-autoscaler
namespace: kube-system
spec:
replicas: 1
selector:
matchLabels:
app: cluster-autoscaler
template:
metadata:
labels:
app: cluster-autoscaler
spec:
containers:
- image: k8s.gcr.io/autoscaling/cluster-autoscaler:v1.21.0
name: cluster-autoscaler
resources:
limits:
cpu: 100m
memory: 300Mi
requests:
cpu: 100m
memory: 300Mi
command:
- ./cluster-autoscaler
- --v=4
- --stderrthreshold=info
- --cloud-provider=aws
- --skip-nodes-with-local-storage=false
- --expander=least-waste
- --node-group-auto-discovery=asg:tag=k8s.io/cluster-autoscaler/enabled,k8s.io/cluster-autoscaler/dtem-prod
- --balance-similar-node-groups
- --skip-nodes-with-system-pods=false
3.6. Gestión de Configuración
3.6.1. GitOps con ArgoCD
# argocd-application.yaml
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: dtem-prod
namespace: argocd
spec:
project: default
source:
repoURL: https://gitlab.com/empresa/dtem-k8s.git
targetRevision: HEAD
path: production
destination:
server: https://kubernetes.default.svc
namespace: dtem-prod
syncPolicy:
automated:
prune: true
selfHeal: true
syncOptions:
- CreateNamespace=true
retry:
limit: 5
backoff:
duration: 5s
factor: 2
maxDuration: 3m
3.6.2. Configuración con Helm
# Chart.yaml
apiVersion: v2
name: dtem
description: DTEM Helm Chart
type: application
version: 1.0.0
appVersion: "3.0.0"
# values.yaml
global:
imageRegistry: registry.gitlab.com/empresa/dtem
imageTag: "latest"
namespace: dtem-prod
apiGateway:
replicaCount: 3
image:
repository: api-gateway
service:
type: ClusterIP
port: 80
resources:
requests:
memory: "256Mi"
cpu: "250m"
limits:
memory: "512Mi"
cpu: "500m"
dteService:
replicaCount: 5
image:
repository: dte-service
resources:
requests:
memory: "512Mi"
cpu: "500m"
limits:
memory: "1Gi"
cpu: "1000m"
postgresql:
enabled: true
auth:
postgresPassword: "secure-password"
database: "dtem_prod"
primary:
persistence:
enabled: true
size: 100Gi
redis:
enabled: true
auth:
enabled: true
password: "redis-password"
master:
persistence:
enabled: true
size: 20Gi
3.7. Monitoreo de Infraestructura
3.7.1. Prometheus Monitoring
# servicemonitor.yaml
apiVersion: monitoring.coreos.com/v1
kind: ServiceMonitor
metadata:
name: dtem-metrics
namespace: dtem-prod
labels:
app: dtem
spec:
selector:
matchLabels:
app: api-gateway
endpoints:
- port: metrics
interval: 30s
path: /metrics
3.7.2. Grafana Dashboards
{
"dashboard": {
"title": "DTEM System Overview",
"panels": [
{
"title": "Request Rate",
"type": "graph",
"targets": [
{
"expr": "rate(http_requests_total[5m])",
"legendFormat": "{{method}} {{status}}"
}
]
},
{
"title": "Response Time",
"type": "graph",
"targets": [
{
"expr": "histogram_quantile(0.95, rate(http_request_duration_seconds_bucket[5m]))",
"legendFormat": "95th percentile"
}
]
},
{
"title": "Error Rate",
"type": "singlestat",
"targets": [
{
"expr": "rate(http_requests_total{status=~\"5..\"}[5m]) / rate(http_requests_total[5m])",
"legendFormat": "Error Rate"
}
]
}
]
}
}
3.8. Resiliencia y Alta Disponibilidad
3.8.1. Pod Disruption Budgets
# pdb.yaml
apiVersion: policy/v1
kind: PodDisruptionBudget
metadata:
name: api-gateway-pdb
namespace: dtem-prod
spec:
minAvailable: 2
selector:
matchLabels:
app: api-gateway
---
apiVersion: policy/v1
kind: PodDisruptionBudget
metadata:
name: dte-service-pdb
namespace: dtem-prod
spec:
minAvailable: 3
selector:
matchLabels:
app: dte-service
3.8.2. Circuit Breaker Pattern
# circuit-breaker.yaml
apiVersion: networking.istio.io/v1beta1
kind: DestinationRule
metadata:
name: dte-service-circuit-breaker
namespace: dtem-prod
spec:
host: dte-service
trafficPolicy:
connectionPool:
tcp:
maxConnections: 100
http:
http1MaxPendingRequests: 50
maxRequestsPerConnection: 10
outlierDetection:
consecutiveErrors: 3
interval: 30s
baseEjectionTime: 30s
Próxima sección: 4. Seguridad