🎯 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 1. 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: "" NEXT_PUBLIC_VIEWER_URL: "" ENCRYPTION_SECRET: "your-encryption-secret-min-32-chars" ADMIN_EMAIL: "" --- 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 1. 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: "" MAIL_FROM: "" 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 1. 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 1. 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 1. 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 1. 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 1. 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 1. Escalabilidad: Auto-scaling basado en métricas 2. Resiliencia: Multi-replica deployments 3. Portabilidad: Funciona en cualquier K8s 4. Observabilidad: Métricas y logs centralizados 5. Seguridad: Network policies y RBAC 6. 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?