Manifiestos Kubernetes — LeadPulse AI

Despliegue de producción · Namespace: production · Cluster: gke-leadpulse-prod-eu-west1

Production-ready gcr.io/leadpulse/leadpulse-api:v2.4.1 5 manifiestos generados
🚀
Deployment 3 réplicas
🌐
Service ClusterIP :80
ConfigMap 7 vars
🔐
Secret 3 secretos
💾
PVC 20Gi ssd-premium
🛡
Seguridad non-root + caps-drop
🚀
apps/v1 3 replicas
📄 deployment.yaml
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: leadpulse-api
  namespace: production
  labels:
    app.kubernetes.io/name: leadpulse-api
    app.kubernetes.io/instance: leadpulse-api-prod
    app.kubernetes.io/version: "2.4.1"
    app.kubernetes.io/component: backend
    app.kubernetes.io/part-of: leadpulse
    app.kubernetes.io/managed-by: kubectl
  annotations:
    description: "API REST de scoring de leads con IA para equipos de ventas"
    contact: "platform@leadpulse.ai"
    prometheus.io/scrape: "true"
    prometheus.io/port: "9090"
    prometheus.io/path: "/metrics"
spec:
  replicas: 3
  selector:
    matchLabels:
      app.kubernetes.io/name: leadpulse-api
      app.kubernetes.io/instance: leadpulse-api-prod
  strategy:
    type: RollingUpdate
    rollingUpdate:
      maxSurge: 1
      maxUnavailable: 0
  template:
    metadata:
      labels:
        app.kubernetes.io/name: leadpulse-api
        app.kubernetes.io/instance: leadpulse-api-prod
        app.kubernetes.io/version: "2.4.1"
    spec:
      ## Pod-level security context
      securityContext:
        runAsNonRoot: true
        runAsUser: 1000
        runAsGroup: 1000
        fsGroup: 1000
        seccompProfile:
          type: RuntimeDefault
      containers:
        - name: leadpulse-api
          image: gcr.io/leadpulse/leadpulse-api:v2.4.1
          imagePullPolicy: Always
          ports:
            - containerPort: 8080
              name: http
              protocol: TCP
            - containerPort: 9090
              name: metrics
              protocol: TCP
          ## Container-level security context
          securityContext:
            allowPrivilegeEscalation: false
            readOnlyRootFilesystem: true
            capabilities:
              drop:
                - ALL
          ## Resource requests y limits
          resources:
            requests:
              memory: "256Mi"
              cpu: "250m"
            limits:
              memory: "1Gi"
              cpu: "1000m"
          ## Health checks
          livenessProbe:
            httpGet:
              path: /health
              port: http
            initialDelaySeconds: 30
            periodSeconds: 10
            timeoutSeconds: 5
            failureThreshold: 3
          readinessProbe:
            httpGet:
              path: /ready
              port: http
            initialDelaySeconds: 5
            periodSeconds: 5
            timeoutSeconds: 3
            successThreshold: 1
          ## Variables de entorno desde ConfigMap y Secret
          envFrom:
            - configMapRef:
                name: leadpulse-api-config
            - secretRef:
                name: leadpulse-api-secret
          ## Volumen para cache de modelos ML
          volumeMounts:
            - name: models-cache
              mountPath: /var/lib/leadpulse/models
            - name: tmp
              mountPath: /tmp
      volumes:
        - name: models-cache
          persistentVolumeClaim:
            claimName: leadpulse-api-models
        - name: tmp
          emptyDir: {}
      terminationGracePeriodSeconds: 60
      restartPolicy: Always
🌐
ClusterIP v1
📄 service.yaml
---
apiVersion: v1
kind: Service
metadata:
  name: leadpulse-api
  namespace: production
  labels:
    app.kubernetes.io/name: leadpulse-api
    app.kubernetes.io/instance: leadpulse-api-prod
    app.kubernetes.io/component: backend
    app.kubernetes.io/part-of: leadpulse
  annotations:
    description: "Servicio interno para leadpulse-api"
spec:
  type: ClusterIP
  selector:
    app.kubernetes.io/name: leadpulse-api
    app.kubernetes.io/instance: leadpulse-api-prod
  ports:
    - name: http
      port: 80
      targetPort: http
      protocol: TCP
    - name: metrics
      port: 9090
      targetPort: metrics
      protocol: TCP
  sessionAffinity: None
v1 non-secret
📄 configmap.yaml
---
apiVersion: v1
kind: ConfigMap
metadata:
  name: leadpulse-api-config
  namespace: production
  labels:
    app.kubernetes.io/name: leadpulse-api
    app.kubernetes.io/instance: leadpulse-api-prod
    app.kubernetes.io/component: config
  annotations:
    description: "Configuracion no sensible de leadpulse-api"
data:
  ## Modo y logging
  APP_MODE: "production"
  LOG_LEVEL: "info"
  ## Base de datos
  DATABASE_HOST: "postgres.leadpulse.internal"
  DATABASE_NAME: "leadpulse_prod"
  DATABASE_PORT: "5432"
  ## Cache
  REDIS_URL: "redis://redis.leadpulse.internal:6379"
  ## Modelo IA
  OPENAI_MODEL: "gpt-4o-mini"
  MODEL_CACHE_PATH: "/var/lib/leadpulse/models"
  ## Metricas
  METRICS_PORT: "9090"
  METRICS_PATH: "/metrics"
🔐
Opaque NO commit en Git
📄 secret.yaml ⚠ Usar Sealed Secrets / ESO en produccion
---
apiVersion: v1
kind: Secret
metadata:
  name: leadpulse-api-secret
  namespace: production
  labels:
    app.kubernetes.io/name: leadpulse-api
    app.kubernetes.io/instance: leadpulse-api-prod
    app.kubernetes.io/component: secrets
  annotations:
    description: "Credenciales sensibles de leadpulse-api"
    secret-manager: "gcp-secret-manager"
    rotation-period: "90d"
type: Opaque
stringData:
  ## Reemplazar con valores reales via CI/CD o External Secrets Operator
  DATABASE_PASSWORD: "<inyectado-por-eso>"
  OPENAI_API_KEY: "<inyectado-por-eso>"
  JWT_SECRET_KEY: "<inyectado-por-eso>"
## -----------------------------------------------
## PRODUCCION: Usar ExternalSecret con GCP Secret Manager:
##
## apiVersion: external-secrets.io/v1beta1
## kind: ExternalSecret
## metadata:
##   name: leadpulse-api-secret
##   namespace: production
## spec:
##   refreshInterval: 1h
##   secretStoreRef:
##     name: gcp-secret-store
##     kind: ClusterSecretStore
##   target:
##     name: leadpulse-api-secret
##   data:
##     - secretKey: DATABASE_PASSWORD
##       remoteRef:
##         key: leadpulse/prod/database-password
##     - secretKey: OPENAI_API_KEY
##       remoteRef:
##         key: leadpulse/prod/openai-api-key
##     - secretKey: JWT_SECRET_KEY
##       remoteRef:
##         key: leadpulse/prod/jwt-secret-key
## -----------------------------------------------
💾
20Gi ssd-premium
📄 pvc.yaml
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: leadpulse-api-models
  namespace: production
  labels:
    app.kubernetes.io/name: leadpulse-api
    app.kubernetes.io/instance: leadpulse-api-prod
    app.kubernetes.io/component: storage
  annotations:
    description: "Cache de modelos ML para leadpulse-api"
    backup-policy: "daily"
    backup-retention: "7d"
spec:
  accessModes:
    - ReadWriteOnce
  storageClassName: ssd-premium
  resources:
    requests:
      storage: 20Gi

Seguridad

runAsNonRoot: true (uid 1000)
allowPrivilegeEscalation: false
readOnlyRootFilesystem: true
capabilities.drop: [ALL]
seccompProfile: RuntimeDefault
Secretos no hardcodeados en imagen
Usar External Secrets Operator en prod

Disponibilidad

3 replicas para alta disponibilidad
RollingUpdate con maxUnavailable: 0
livenessProbe configurado
readinessProbe configurado
terminationGracePeriodSeconds: 60
Imagen con tag especifico (v2.4.1)

Recursos

requests.memory: 256Mi
requests.cpu: 250m
limits.memory: 1Gi
limits.cpu: 1000m
PVC 20Gi ssd-premium para ML
EmptyDir /tmp para escritura

Observabilidad

Labels estandar app.kubernetes.io/*
Anotaciones prometheus.io/scrape
Puerto 9090 expuesto para metrics
Anotacion contact y description
Version en labels (2.4.1)
Backup annotations en PVC

📋 Comandos de aplicacion

  • kubectl create namespace production # crear namespace si no existe
  • kubectl apply -f pvc.yaml --dry-run=server -n production # validar PVC
  • kubectl apply -f configmap.yaml -f secret.yaml -n production # config primero
  • kubectl apply -f deployment.yaml -f service.yaml -n production # workload
  • kubectl rollout status deployment/leadpulse-api -n production # verificar rollout
  • kubectl get pods,svc,pvc -n production -l app.kubernetes.io/name=leadpulse-api # estado final