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: statuspagekubectl apply -f namespace.yamlSecrets
# 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.yamlPostgreSQL
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=20GiDespliegue 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: 5432Redis
Usando Helm
helm install redis bitnami/redis \
--namespace statuspage \
--set architecture=standalone \
--set auth.enabled=falseDespliegue 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: 6379Despliegue 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: 3000Despliegue 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: 80Horizontal 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: 80Migraciones 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: 3kubectl apply -f migration-job.yaml
kubectl logs -f job/statuspage-migrate -n statuspageMonitoreo
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: 30sCronJob 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: OnFailureSolución de Problemas
Verificar Estado de Pods
kubectl get pods -n statuspage
kubectl describe pod <nombre-pod> -n statuspage
kubectl logs <nombre-pod> -n statuspageVerificar Servicios
kubectl get svc -n statuspage
kubectl get ingress -n statuspageConexión a Base de Datos
kubectl exec -it postgres-0 -n statuspage -- psql -U postgres -d statuspageReiniciar Deployments
kubectl rollout restart deployment/statuspage-app -n statuspage
kubectl rollout restart deployment/statuspage-worker -n statuspage