15 KiB
🎯 ARQUITECTURA PROPUESTA PARA KUBERNETES
Stack Definitivo:
-
Pre/Post-Evento: Typebot + Formbricks (ambos en K8s)
-
Durante Evento: Claper (en K8s)
-
Datos: PostgreSQL (con operador en K8s)
-
Storage: PVC para persistencia
📦 CONFIGURACIONES KUBERNETES
- Typebot Deployment
typebot-deployment.yaml
apiVersion: v1 kind: Namespace metadata: name: rust-meetup
apiVersion: v1 kind: ConfigMap metadata: name: typebot-config namespace: rust-meetup data: DATABASE_URL: "postgresql://typebot:password@postgres-service:5432/typebot" NEXTAUTH_URL: "https://typebot.rustmeetup.com" NEXT_PUBLIC_VIEWER_URL: "https://typebot.rustmeetup.com" ENCRYPTION_SECRET: "your-encryption-secret-min-32-chars" ADMIN_EMAIL: "admin@rustmeetup.com"
apiVersion: apps/v1 kind: Deployment metadata: name: typebot namespace: rust-meetup spec: replicas: 2 selector: matchLabels: app: typebot template: metadata: labels: app: typebot spec: containers: - name: typebot image: baptistearno/typebot:latest ports: - containerPort: 3000 envFrom: - configMapRef: name: typebot-config resources: requests: memory: "512Mi" cpu: "250m" limits: memory: "1Gi" cpu: "500m" volumeMounts: - name: typebot-storage mountPath: /app/uploads volumes: - name: typebot-storage persistentVolumeClaim: claimName: typebot-pvc
apiVersion: v1 kind: Service metadata: name: typebot-service namespace: rust-meetup spec: selector: app: typebot ports: - port: 80 targetPort: 3000 type: ClusterIP
apiVersion: v1 kind: PersistentVolumeClaim metadata: name: typebot-pvc namespace: rust-meetup spec: accessModes: - ReadWriteMany resources: requests: storage: 10Gi storageClassName: standard
- Formbricks Deployment
formbricks-deployment.yaml
apiVersion: v1 kind: ConfigMap metadata: name: formbricks-config namespace: rust-meetup data: DATABASE_URL: "postgresql://formbricks:password@postgres-service:5432/formbricks" NEXTAUTH_SECRET: "your-nextauth-secret-32-chars" NEXTAUTH_URL: "https://surveys.rustmeetup.com" MAIL_FROM: "noreply@rustmeetup.com" SMTP_HOST: "smtp.gmail.com" SMTP_PORT: "587" SMTP_SECURE: "false"
apiVersion: apps/v1 kind: Deployment metadata: name: formbricks namespace: rust-meetup spec: replicas: 2 selector: matchLabels: app: formbricks template: metadata: labels: app: formbricks spec: containers: - name: formbricks image: formbricks/formbricks:latest ports: - containerPort: 3000 envFrom: - configMapRef: name: formbricks-config resources: requests: memory: "512Mi" cpu: "250m" limits: memory: "1Gi" cpu: "500m" volumeMounts: - name: formbricks-storage mountPath: /home/nextjs/apps/web/uploads volumes: - name: formbricks-storage persistentVolumeClaim: claimName: formbricks-pvc
apiVersion: v1 kind: Service metadata: name: formbricks-service namespace: rust-meetup spec: selector: app: formbricks ports: - port: 80 targetPort: 3000 type: ClusterIP
apiVersion: v1 kind: PersistentVolumeClaim metadata: name: formbricks-pvc namespace: rust-meetup spec: accessModes: - ReadWriteMany resources: requests: storage: 5Gi
- Claper Deployment
claper-deployment.yaml
apiVersion: v1 kind: ConfigMap metadata: name: claper-config namespace: rust-meetup data: DATABASE_URL: "postgresql://claper:password@postgres-service:5432/claper" SECRET_KEY_BASE: "your-secret-key-base-64-chars" PHX_HOST: "claper.rustmeetup.com" PORT: "4000"
apiVersion: apps/v1 kind: Deployment metadata: name: claper namespace: rust-meetup spec: replicas: 2 selector: matchLabels: app: claper template: metadata: labels: app: claper spec: containers: - name: claper image: claperco/claper:latest ports: - containerPort: 4000 envFrom: - configMapRef: name: claper-config resources: requests: memory: "256Mi" cpu: "200m" limits: memory: "512Mi" cpu: "400m" volumeMounts: - name: claper-uploads mountPath: /app/uploads volumes: - name: claper-uploads persistentVolumeClaim: claimName: claper-pvc
apiVersion: v1 kind: Service metadata: name: claper-service namespace: rust-meetup spec: selector: app: claper ports: - port: 80 targetPort: 4000 type: ClusterIP
apiVersion: v1 kind: PersistentVolumeClaim metadata: name: claper-pvc namespace: rust-meetup spec: accessModes: - ReadWriteMany resources: requests: storage: 20Gi
- PostgreSQL con Operador
postgres-operator.yaml
apiVersion: v1 kind: ConfigMap metadata: name: postgres-config namespace: rust-meetup data: init.sql: | CREATE DATABASE typebot; CREATE USER typebot WITH ENCRYPTED PASSWORD 'password'; GRANT ALL PRIVILEGES ON DATABASE typebot TO typebot;
CREATE DATABASE formbricks;
CREATE USER formbricks WITH ENCRYPTED PASSWORD 'password';
GRANT ALL PRIVILEGES ON DATABASE formbricks TO formbricks;
CREATE DATABASE claper;
CREATE USER claper WITH ENCRYPTED PASSWORD 'password';
GRANT ALL PRIVILEGES ON DATABASE claper TO claper;
apiVersion: apps/v1 kind: StatefulSet metadata: name: postgres namespace: rust-meetup spec: serviceName: postgres-service replicas: 1 selector: matchLabels: app: postgres template: metadata: labels: app: postgres spec: containers: - name: postgres image: postgres:15-alpine ports: - containerPort: 5432 env: - name: POSTGRES_PASSWORD value: "superpassword" - name: POSTGRES_USER value: "postgres" - name: PGDATA value: /var/lib/postgresql/data/pgdata volumeMounts: - name: postgres-storage mountPath: /var/lib/postgresql/data - name: postgres-init mountPath: /docker-entrypoint-initdb.d resources: requests: memory: "256Mi" cpu: "250m" limits: memory: "512Mi" cpu: "500m" volumes: - name: postgres-init configMap: name: postgres-config volumeClaimTemplates: - metadata: name: postgres-storage spec: accessModes: ["ReadWriteOnce"] resources: requests: storage: 10Gi
apiVersion: v1 kind: Service metadata: name: postgres-service namespace: rust-meetup spec: selector: app: postgres ports: - port: 5432 type: ClusterIP
- Ingress Configuration
ingress.yaml
apiVersion: networking.k8s.io/v1 kind: Ingress metadata: name: rust-meetup-ingress namespace: rust-meetup annotations: cert-manager.io/cluster-issuer: "letsencrypt-prod" nginx.ingress.kubernetes.io/proxy-body-size: "50m" nginx.ingress.kubernetes.io/proxy-read-timeout: "600" nginx.ingress.kubernetes.io/proxy-send-timeout: "600" spec: ingressClassName: nginx tls: - hosts: - typebot.rustmeetup.com - surveys.rustmeetup.com - claper.rustmeetup.com secretName: rust-meetup-tls rules: - host: typebot.rustmeetup.com http: paths: - path: / pathType: Prefix backend: service: name: typebot-service port: number: 80 - host: surveys.rustmeetup.com http: paths: - path: / pathType: Prefix backend: service: name: formbricks-service port: number: 80 - host: claper.rustmeetup.com http: paths: - path: / pathType: Prefix backend: service: name: claper-service port: number: 80
🚀 HELM CHARTS ALTERNATIVOS
Opción con Helm (Más Mantenible)
values-typebot.yaml
replicaCount: 2 image: repository: baptistearno/typebot tag: latest pullPolicy: IfNotPresent
service: type: ClusterIP port: 80
ingress: enabled: true className: nginx hosts: - host: typebot.rustmeetup.com paths: - path: / pathType: Prefix tls: - secretName: typebot-tls hosts: - typebot.rustmeetup.com
postgresql: enabled: false external: host: postgres-service port: 5432 database: typebot user: typebot
persistence: enabled: true size: 10Gi storageClass: standard
resources: requests: cpu: 250m memory: 512Mi limits: cpu: 500m memory: 1Gi
autoscaling: enabled: true minReplicas: 2 maxReplicas: 10 targetCPUUtilizationPercentage: 70
📊 MONITOREO Y OBSERVABILIDAD
monitoring-stack.yaml
apiVersion: v1 kind: ServiceMonitor metadata: name: typebot-metrics namespace: rust-meetup spec: selector: matchLabels: app: typebot endpoints: - port: metrics interval: 30s
Grafana Dashboard ConfigMap
apiVersion: v1 kind: ConfigMap metadata: name: grafana-dashboards namespace: rust-meetup data: rust-meetup-dashboard.json: | { "dashboard": { "title": "Rust Meetup 2025 Metrics", "panels": [ { "title": "Active Survey Sessions", "targets": [ { "expr": "sum(typebot_active_sessions)" } ] }, { "title": "Response Rate", "targets": [ { "expr": "rate(typebot_responses_total[5m])" } ] }, { "title": "Live Poll Participation", "targets": [ { "expr": "sum(claper_active_participants)" } ] } ] } }
🔧 SCRIPTS DE DEPLOYMENT
Deploy Script
#!/bin/bash
deploy.sh - Deploy completo del stack
echo "🚀 Deploying Rust Meetup 2025 Stack on Kubernetes"
Create namespace
kubectl create namespace rust-meetup --dry-run=client -o yaml | kubectl apply -f -
Deploy PostgreSQL
echo "📊 Deploying PostgreSQL..." kubectl apply -f postgres-operator.yaml
Wait for PostgreSQL to be ready
kubectl wait --for=condition=ready pod -l app=postgres -n rust-meetup --timeout=300s
Deploy applications
echo "🎯 Deploying Typebot..." kubectl apply -f typebot-deployment.yaml
echo "📝 Deploying Formbricks..." kubectl apply -f formbricks-deployment.yaml
echo "🎤 Deploying Claper..." kubectl apply -f claper-deployment.yaml
Deploy Ingress
echo "🌐 Configuring Ingress..." kubectl apply -f ingress.yaml
Check deployment status
echo "✅ Checking deployment status..." kubectl get pods -n rust-meetup kubectl get svc -n rust-meetup kubectl get ingress -n rust-meetup
echo "🎉 Deployment complete!"
Backup Script
#!/bin/bash
backup.sh - Backup de datos
NAMESPACE="rust-meetup" BACKUP_DIR="/backups/$(date +%Y%m%d-%H%M%S)"
Backup PostgreSQL
kubectl exec -n $NAMESPACE postgres-0 -- pg_dumpall -U postgres > $BACKUP_DIR/postgres-backup.sql
Backup persistent volumes
kubectl get pvc -n $NAMESPACE -o json > $BACKUP_DIR/pvcs.json
Upload to S3
aws s3 sync $BACKUP_DIR s3://rust-meetup-backups/
💡 MEJORES PRÁCTICAS
- Seguridad
Network Policy
apiVersion: networking.k8s.io/v1 kind: NetworkPolicy metadata: name: rust-meetup-network-policy namespace: rust-meetup spec: podSelector: {} policyTypes: - Ingress - Egress ingress: - from: - namespaceSelector: matchLabels: name: ingress-nginx egress: - to: - podSelector: matchLabels: app: postgres - to: - namespaceSelector: {} podSelector: matchLabels: k8s-app: kube-dns ports: - protocol: UDP port: 53
- Auto-scaling
apiVersion: autoscaling/v2 kind: HorizontalPodAutoscaler metadata: name: typebot-hpa namespace: rust-meetup spec: scaleTargetRef: apiVersion: apps/v1 kind: Deployment name: typebot minReplicas: 2 maxReplicas: 20 metrics: - type: Resource resource: name: cpu target: type: Utilization averageUtilization: 70 - type: Resource resource: name: memory target: type: Utilization averageUtilization: 80 behavior: scaleUp: stabilizationWindowSeconds: 60 policies: - type: Percent value: 100 periodSeconds: 60
📋 CHECKLIST DE PRODUCCIÓN
Pre-Deployment
-
Cluster Kubernetes ready (EKS/GKE/AKS/on-prem)
-
Ingress controller instalado (nginx/traefik)
-
Cert-manager configurado
-
Storage class disponible
-
Secrets management (Sealed Secrets/Vault)
Deployment
-
Deploy PostgreSQL y verificar
-
Deploy apps y verificar pods running
-
Configurar DNS para subdominios
-
Verificar certificados SSL
-
Test end-to-end de cada aplicación
Post-Deployment
-
Configurar backups automáticos
-
Setup monitoring (Prometheus/Grafana)
-
Configurar alertas
-
Load testing con k6/Locust
-
Documentar runbooks
🎯 VENTAJAS DE ESTA ARQUITECTURA
- Escalabilidad: Auto-scaling basado en métricas
- Resiliencia: Multi-replica deployments
- Portabilidad: Funciona en cualquier K8s
- Observabilidad: Métricas y logs centralizados
- Seguridad: Network policies y RBAC
- Cost-Effective: Optimización de recursos
💰 ESTIMACIÓN DE COSTOS
Componente Recursos Costo Mensual K8s Cluster (3 nodes) 6 vCPU, 12GB RAM $150 Storage 50GB SSD $10 Load Balancer 1 LB $20 Backups S3 100GB $5 TOTAL $185/mes ¿Te gustaría que detalle algún aspecto específico o prepare los templates de Typebot para los cuestionarios?