Español
Despliegue
Kubernetes

Despliegue en Kubernetes

Despliega ReliaPulse en Kubernetes para una infraestructura escalable y de grado de producción.

Requisitos Previos

  • Clúster Kubernetes (1.24+)
  • kubectl configurado
  • Helm 3.x (opcional)
  • Controlador de Ingress (nginx, traefik)
  • Aprovisionador de almacenamiento persistente

Vista General de Arquitectura

┌─────────────────────────────────────────────────────────────┐
│                        Ingress                               │
│                    status.example.com                        │
└─────────────────────────┬───────────────────────────────────┘

┌─────────────────────────▼───────────────────────────────────┐
│                     Service (app)                            │
│                      ClusterIP                               │
└─────────────────────────┬───────────────────────────────────┘

         ┌────────────────┼────────────────┐
         │                │                │
    ┌────▼────┐     ┌────▼────┐     ┌────▼────┐
    │   App   │     │   App   │     │  Worker │
    │  Pod 1  │     │  Pod 2  │     │  Pod 1  │
    └────┬────┘     └────┬────┘     └────┬────┘
         │                │                │
         └────────────────┼────────────────┘

    ┌─────────────────────┼─────────────────────┐
    │                     │                     │
┌───▼───┐           ┌────▼────┐           ┌────▼────┐
│PostgreSQL│         │  Redis  │           │  Redis  │
│ Primario │         │ Primario│           │ Réplica │
└──────────┘         └─────────┘           └─────────┘

Configuración de Namespace

# namespace.yaml
apiVersion: v1
kind: Namespace
metadata:
  name: statuspage
kubectl apply -f namespace.yaml

Secrets

# secrets.yaml
apiVersion: v1
kind: Secret
metadata:
  name: statuspage-secrets
  namespace: statuspage
type: Opaque
stringData:
  DATABASE_URL: "postgresql://postgres:password@postgres:5432/statuspage"
  REDIS_URL: "redis://redis:6379"
  NEXTAUTH_SECRET: "tu-clave-secreta-al-menos-32-chars"
  NEXTAUTH_URL: "https://status.example.com"
  # Opcional
  RESEND_API_KEY: ""
  TWILIO_ACCOUNT_SID: ""
  TWILIO_AUTH_TOKEN: ""
kubectl apply -f secrets.yaml

PostgreSQL

Usando Helm (Recomendado)

helm repo add bitnami https://charts.bitnami.com/bitnami
 
helm install postgres bitnami/postgresql \
  --namespace statuspage \
  --set auth.database=statuspage \
  --set auth.postgresPassword=password \
  --set primary.persistence.size=20Gi

Despliegue Manual

# postgres.yaml
apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: postgres
  namespace: statuspage
spec:
  serviceName: postgres
  replicas: 1
  selector:
    matchLabels:
      app: postgres
  template:
    metadata:
      labels:
        app: postgres
    spec:
      containers:
        - name: postgres
          image: postgres:16-alpine
          ports:
            - containerPort: 5432
          env:
            - name: POSTGRES_DB
              value: statuspage
            - name: POSTGRES_PASSWORD
              valueFrom:
                secretKeyRef:
                  name: statuspage-secrets
                  key: POSTGRES_PASSWORD
          volumeMounts:
            - name: postgres-data
              mountPath: /var/lib/postgresql/data
          resources:
            requests:
              memory: "512Mi"
              cpu: "250m"
            limits:
              memory: "2Gi"
              cpu: "1000m"
  volumeClaimTemplates:
    - metadata:
        name: postgres-data
      spec:
        accessModes: ["ReadWriteOnce"]
        resources:
          requests:
            storage: 20Gi
---
apiVersion: v1
kind: Service
metadata:
  name: postgres
  namespace: statuspage
spec:
  selector:
    app: postgres
  ports:
    - port: 5432

Redis

Usando Helm

helm install redis bitnami/redis \
  --namespace statuspage \
  --set architecture=standalone \
  --set auth.enabled=false

Despliegue Manual

# redis.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: redis
  namespace: statuspage
spec:
  replicas: 1
  selector:
    matchLabels:
      app: redis
  template:
    metadata:
      labels:
        app: redis
    spec:
      containers:
        - name: redis
          image: redis:7-alpine
          ports:
            - containerPort: 6379
          resources:
            requests:
              memory: "128Mi"
              cpu: "100m"
            limits:
              memory: "512Mi"
              cpu: "500m"
---
apiVersion: v1
kind: Service
metadata:
  name: redis
  namespace: statuspage
spec:
  selector:
    app: redis
  ports:
    - port: 6379

Despliegue de Aplicación

# app-deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: statuspage-app
  namespace: statuspage
spec:
  replicas: 2
  selector:
    matchLabels:
      app: statuspage
      component: app
  template:
    metadata:
      labels:
        app: statuspage
        component: app
    spec:
      containers:
        - name: app
          image: tu-registro/statuspage:latest
          ports:
            - containerPort: 3000
          envFrom:
            - secretRef:
                name: statuspage-secrets
          resources:
            requests:
              memory: "512Mi"
              cpu: "250m"
            limits:
              memory: "2Gi"
              cpu: "1000m"
          livenessProbe:
            httpGet:
              path: /api/health
              port: 3000
            initialDelaySeconds: 30
            periodSeconds: 10
          readinessProbe:
            httpGet:
              path: /api/health
              port: 3000
            initialDelaySeconds: 5
            periodSeconds: 5
---
apiVersion: v1
kind: Service
metadata:
  name: statuspage-app
  namespace: statuspage
spec:
  selector:
    app: statuspage
    component: app
  ports:
    - port: 80
      targetPort: 3000

Despliegue de Worker

# worker-deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: statuspage-worker
  namespace: statuspage
spec:
  replicas: 2
  selector:
    matchLabels:
      app: statuspage
      component: worker
  template:
    metadata:
      labels:
        app: statuspage
        component: worker
    spec:
      containers:
        - name: worker
          image: tu-registro/statuspage:latest
          command: ["npm", "run", "worker"]
          envFrom:
            - secretRef:
                name: statuspage-secrets
          resources:
            requests:
              memory: "256Mi"
              cpu: "100m"
            limits:
              memory: "1Gi"
              cpu: "500m"

Ingress

Nginx Ingress

# ingress.yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: statuspage-ingress
  namespace: statuspage
  annotations:
    kubernetes.io/ingress.class: nginx
    cert-manager.io/cluster-issuer: letsencrypt-prod
spec:
  tls:
    - hosts:
        - status.example.com
      secretName: statuspage-tls
  rules:
    - host: status.example.com
      http:
        paths:
          - path: /
            pathType: Prefix
            backend:
              service:
                name: statuspage-app
                port:
                  number: 80

Horizontal Pod Autoscaler

# hpa.yaml
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
  name: statuspage-app-hpa
  namespace: statuspage
spec:
  scaleTargetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: statuspage-app
  minReplicas: 2
  maxReplicas: 10
  metrics:
    - type: Resource
      resource:
        name: cpu
        target:
          type: Utilization
          averageUtilization: 70
    - type: Resource
      resource:
        name: memory
        target:
          type: Utilization
          averageUtilization: 80

Migraciones de Base de Datos

Ejecuta migraciones usando un Job:

# migration-job.yaml
apiVersion: batch/v1
kind: Job
metadata:
  name: statuspage-migrate
  namespace: statuspage
spec:
  template:
    spec:
      containers:
        - name: migrate
          image: tu-registro/statuspage:latest
          command: ["npx", "prisma", "migrate", "deploy"]
          envFrom:
            - secretRef:
                name: statuspage-secrets
      restartPolicy: Never
  backoffLimit: 3
kubectl apply -f migration-job.yaml
kubectl logs -f job/statuspage-migrate -n statuspage

Monitoreo

ServiceMonitor de Prometheus

# servicemonitor.yaml
apiVersion: monitoring.coreos.com/v1
kind: ServiceMonitor
metadata:
  name: statuspage
  namespace: statuspage
spec:
  selector:
    matchLabels:
      app: statuspage
  endpoints:
    - port: http
      path: /api/metrics
      interval: 30s

CronJob de Backup

# backup-cronjob.yaml
apiVersion: batch/v1
kind: CronJob
metadata:
  name: postgres-backup
  namespace: statuspage
spec:
  schedule: "0 2 * * *"  # Diario a las 2 AM
  jobTemplate:
    spec:
      template:
        spec:
          containers:
            - name: backup
              image: postgres:16-alpine
              command:
                - /bin/sh
                - -c
                - |
                  pg_dump -h postgres -U postgres statuspage | \
                  gzip > /backup/statuspage-$(date +%Y%m%d).sql.gz
              env:
                - name: PGPASSWORD
                  valueFrom:
                    secretKeyRef:
                      name: statuspage-secrets
                      key: POSTGRES_PASSWORD
              volumeMounts:
                - name: backup
                  mountPath: /backup
          volumes:
            - name: backup
              persistentVolumeClaim:
                claimName: backup-pvc
          restartPolicy: OnFailure

Solución de Problemas

Verificar Estado de Pods

kubectl get pods -n statuspage
kubectl describe pod <nombre-pod> -n statuspage
kubectl logs <nombre-pod> -n statuspage

Verificar Servicios

kubectl get svc -n statuspage
kubectl get ingress -n statuspage

Conexión a Base de Datos

kubectl exec -it postgres-0 -n statuspage -- psql -U postgres -d statuspage

Reiniciar Deployments

kubectl rollout restart deployment/statuspage-app -n statuspage
kubectl rollout restart deployment/statuspage-worker -n statuspage

Relacionado