Réplicas iniciales
10
API×3 · FE×2 · Worker×3 · Redis×2
Max auto-escaldo
50
HPA: API 3→20 · Worker 3→30
Manifiestos YAML
24
8 templates Helm + values staging/prod
Uptime objetivo
99.9%
RollingUpdate + preStop + PDB
Arquitectura del clúster
namespace cultivaai-prod · GKE europe-west1
Topology — Pods & Servicios
×2
Frontend
React/nginx
port :80
HPA off
→
×3
API Backend
Node.js :3000
/api/*
HPA 3→20
→
PostgreSQL
StatefulSet
50Gi PVC
Redis
×2 replicas
10Gi PVC
🟣
AI Worker (Celery)
3→30 pods · consume tareas de Redis Queue · llama a API interna
HPA 3→30
×3
🌐Ingress nginx · TLS cert-manager (Let's Encrypt) ·
app.cultivaia.com →
/ Frontend ·
/api/ API Backend
Estructura del Helm Chart
cultivaai-platform/ — chart completo parameterizado
charts/cultivaai-platform/
├── Chart.yaml # nombre, version, dependencias
├── values.yaml # defaults comunes
├── values-staging.yaml # overrides staging
├── values-prod.yaml # overrides producción
├── templates/
│ ├── _helpers.tpl # labels, fullname helpers
│ ├── namespace.yaml # Namespace + ResourceQuota
│ ├── api-deployment.yaml # API Node.js
│ ├── frontend-deployment.yaml # React/nginx
│ ├── worker-deployment.yaml # Celery workers
│ ├── postgres-statefulset.yaml # PostgreSQL + PVC 50Gi
│ ├── redis-deployment.yaml # Redis cache/queue
│ ├── services.yaml # ClusterIP para cada pod
│ ├── ingress.yaml # nginx + cert-manager
│ ├── hpa.yaml # HPA api + worker
│ ├── pdb.yaml # PodDisruptionBudget
│ ├── network-policies.yaml # tráfico pod-a-pod
│ ├── external-secrets.yaml # ESO → GCP Secret Manager
│ └── cronjob-backup.yaml # backup diario PostgreSQL
└── overlays/
├── staging/ # kustomize overlay staging
└── production/ # kustomize overlay prod
Manifiestos YAML
Todos los recursos generados para cultivaai-prod
Chart.yaml
values.yaml
values-prod.yaml
charts/cultivaai-platform/Chart.yaml
apiVersion: v2 name: cultivaai-platform description: CULTIVA IA — Plataforma SaaS de automatización de marketing con IA type: application version: 1.4.0 appVersion: "2.1.0" maintainers: - name: CULTIVA IA DevOps email: devops@cultivaia.com keywords: [saas, ai-marketing, automation] dependencies: - name: postgresql version: "~15.5" repository: https://charts.bitnami.com/bitnami condition: postgresql.enabled - name: redis version: "~19.6" repository: https://charts.bitnami.com/bitnami condition: redis.enabled
api-deployment.yaml
frontend-deployment.yaml
worker-deployment.yaml
postgres-statefulset.yaml
templates/api-deployment.yaml
apiVersion: apps/v1 kind: Deployment metadata: name: cultivaai-api namespace: {{ .Values.namespace }} labels: app: cultivaai-api version: {{ .Values.api.image.tag }} spec: replicas: {{ .Values.api.replicaCount }} # prod: 3 selector: matchLabels: { app: cultivaai-api } strategy: type: RollingUpdate rollingUpdate: { maxUnavailable: 1, maxSurge: 1 } template: metadata: labels: { app: cultivaai-api, version: {{ .Values.api.image.tag }} } spec: serviceAccountName: cultivaai-api-sa securityContext: runAsNonRoot: true runAsUser: 1000 fsGroup: 2000 containers: - name: api image: eu.gcr.io/cultivaai-prod/api:{{ .Values.api.image.tag }} imagePullPolicy: IfNotPresent ports: [{ containerPort: 3000, name: http }] env: - { name: NODE_ENV, value: production } - { name: PORT, value: "3000" } - name: DATABASE_URL valueFrom: { secretKeyRef: { name: cultivaai-secrets, key: database-url } } - name: REDIS_URL valueFrom: { secretKeyRef: { name: cultivaai-secrets, key: redis-url } } - name: OPENAI_API_KEY valueFrom: { secretKeyRef: { name: cultivaai-secrets, key: openai-key } } resources: requests: { cpu: 250m, memory: 256Mi } limits: { cpu: 500m, memory: 512Mi } readinessProbe: httpGet: { path: /api/health/ready, port: http } initialDelaySeconds: 5 periodSeconds: 10 livenessProbe: httpGet: { path: /api/health/live, port: http } initialDelaySeconds: 15 failureThreshold: 3 securityContext: allowPrivilegeEscalation: false readOnlyRootFilesystem: true capabilities: { drop: [ALL] } lifecycle: preStop: exec: { command: ["/bin/sh", "-c", "sleep 10"] } terminationGracePeriodSeconds: 30
ingress.yaml
hpa.yaml
network-policies.yaml
external-secrets.yaml
argocd-app.yaml
templates/ingress.yaml
apiVersion: networking.k8s.io/v1 kind: Ingress metadata: name: cultivaai-ingress namespace: cultivaai-prod annotations: cert-manager.io/cluster-issuer: letsencrypt-prod nginx.ingress.kubernetes.io/proxy-body-size: "50m" nginx.ingress.kubernetes.io/proxy-read-timeout: "120" nginx.ingress.kubernetes.io/rate-limit: "100" spec: ingressClassName: nginx tls: - hosts: [app.cultivaia.com] secretName: cultivaai-tls rules: - host: app.cultivaia.com http: paths: - path: /api/ pathType: Prefix backend: service: { name: cultivaai-api-svc, port: { number: 80 } } - path: / pathType: Prefix backend: service: { name: cultivaai-frontend-svc, port: { number: 80 } } --- # CronJob — Backup diario PostgreSQL a GCS apiVersion: batch/v1 kind: CronJob metadata: name: cultivaai-db-backup spec: schedule: "0 2 * * *" concurrencyPolicy: Forbid successfulJobsHistoryLimit: 3 jobTemplate: spec: backoffLimit: 2 template: spec: restartPolicy: OnFailure containers: - name: backup image: eu.gcr.io/cultivaai-prod/db-backup:1.0.2 env: - name: GCS_BUCKET value: cultivaai-backups envFrom: [{ secretRef: { name: cultivaai-secrets } }] resources: requests: { cpu: 100m, memory: 128Mi } limits: { cpu: 200m, memory: 256Mi }
Resumen de workloads
Recursos, réplicas y configuración de producción
| Servicio | Kind | Imagen (pinada) | Réplicas | CPU req/limit | Mem req/limit | HPA | Storage |
|---|---|---|---|---|---|---|---|
| API Backend | Deployment | api:2.1.0 |
3 | 250m / 500m | 256Mi / 512Mi | 3→20 CPU70% | — |
| Frontend | Deployment | frontend:2.1.0 |
2 | 100m / 200m | 128Mi / 256Mi | off | — |
| AI Worker | Deployment | worker:2.1.0 |
3 | 500m / 2000m | 512Mi / 2Gi | 3→30 CPU70% | — |
| PostgreSQL | StatefulSet | postgres:16.2 |
1 | 500m / 1000m | 512Mi / 1Gi | off | 50Gi gp3 |
| Redis | Deployment | redis:7.2-alpine |
2 | 100m / 500m | 128Mi / 512Mi | off | 10Gi gp3 |
| DB Backup | CronJob | db-backup:1.0.2 |
0 2 * * * | 100m / 200m | 128Mi / 256Mi | — | GCS bucket |
GitOps con ArgoCD
Flujo automático de deploy desde GitHub a GKE
Developer
git push → main
→
CI/CD Pipeline
build + push image
Trivy scan
Trivy scan
→
k8s-manifests repo
update image.tag
PR auto-merge
PR auto-merge
→
ArgoCD Sync
selfHeal: true
prune: true
prune: true
→
GKE Cluster
RollingUpdate
zero-downtime
zero-downtime
argocd-application.yaml
argocd/cultivaai-platform-app.yaml
apiVersion: argoproj.io/v1alpha1 kind: Application metadata: name: cultivaai-platform namespace: argocd finalizers: - resources-finalizer.argocd.argoproj.io spec: project: cultivaai source: repoURL: https://github.com/cultivaai/k8s-manifests targetRevision: main path: overlays/production helm: valueFiles: [values-prod.yaml] destination: server: https://kubernetes.default.svc namespace: cultivaai-prod syncPolicy: automated: prune: true selfHeal: true allowEmpty: false syncOptions: - CreateNamespace=true - PrunePropagationPolicy=foreground - ApplyOutOfSyncOnly=true retry: limit: 3 backoff: { duration: 5s, factor: 2, maxDuration: 3m } ignoreDifferences: - group: apps kind: Deployment jsonPointers: [/spec/replicas] # HPA manages replicas
Comandos de despliegue y debugging
Flujo completo: install → upgrade → rollback → debug
$# Instalar dependencias del chart
$helm dependency update charts/cultivaai-platform
$# Previsualizar cambios antes de aplicar (helm-diff plugin)
$helm diff upgrade cultivaai-platform charts/cultivaai-platform \
-n cultivaai-prod -f values-prod.yaml
$# Deploy a producción (primera vez)
$helm upgrade --install cultivaai-platform charts/cultivaai-platform \
-n cultivaai-prod --create-namespace \
-f values-prod.yaml --set api.image.tag=2.1.0 --wait
$# Rollback en caso de error
$helm rollback cultivaai-platform 1 -n cultivaai-prod
$# Debug: logs del pod API
$kubectl logs -l app=cultivaai-api -n cultivaai-prod --tail=100 -f
$# Debug: estado HPA
$kubectl get hpa -n cultivaai-prod -w
$# Port-forward API para test local
$kubectl port-forward svc/cultivaai-api-svc 3000:80 -n cultivaai-prod
$# Ver uso de recursos en tiempo real
$kubectl top pods -n cultivaai-prod --sort-by=memory
🔐 Security Checklist — Buenas prácticas aplicadas
✓ runAsNonRoot: true + runAsUser: 1000
✓ readOnlyRootFilesystem: true
✓ capabilities: drop: [ALL]
✓ NetworkPolicy: isolación frontend/worker
✓ Secretos via External Secrets (GCP SM)
✓ Trivy image scan en CI pipeline
✓ Tags pinados — nunca 'latest' en prod
✓ PodDisruptionBudget: minAvailable: 2
Generado por CULTIVA IA · Skill: kubernetes-helm ·
chart v1.4.0 · appVersion 2.1.0
Production-ready
GKE eu-west1