CULTIVA IA Platform — Kubernetes + Helm

cultivaai-platform chart · GKE europe-west1 · namespace: cultivaai-prod
⎈ Kubernetes 1.30 ⛵ Helm 3.14 🔄 ArgoCD GitOps 🔐 External Secrets ✅ Producción-ready
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
📦
k8s-manifests repo
update image.tag
PR auto-merge
🔄
ArgoCD Sync
selfHeal: true
prune: true
☸️
GKE Cluster
RollingUpdate
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