Kustomize: Gestión de Configuración en Kubernetes

Kustomize es la herramienta nativa de Kubernetes para personalizar manifiestos YAML sin usar plantillas, permitiendo gestionar configuraciones base y sobrescrituras por entorno de forma limpia y mantenible. Esta guía cubre el uso de bases y overlays, patches, generadores de ConfigMaps y Secrets, transformadores y la integración con pipelines de CI/CD.

Requisitos Previos

  • kubectl 1.14+ (Kustomize viene integrado)
  • Conocimientos básicos de manifiestos Kubernetes
  • Acceso a un clúster Kubernetes
# Verificar que Kustomize está disponible (integrado en kubectl)
kubectl kustomize --help

# Instalar la CLI independiente de Kustomize (versión más reciente)
curl -s "https://raw.githubusercontent.com/kubernetes-sigs/kustomize/master/hack/install_kustomize.sh" | bash
mv kustomize /usr/local/bin/

kustomize version

Conceptos Fundamentales

Kustomize trabaja con el concepto de base y overlay:

  • Base: configuración común que se reutiliza en todos los entornos
  • Overlay: personalizaciones específicas de cada entorno que extienden la base
  • kustomization.yaml: archivo de control que define qué recursos y transformaciones aplicar
# Estructura típica de un proyecto con Kustomize
tree mi-aplicacion/
# mi-aplicacion/
# ├── base/
# │   ├── kustomization.yaml
# │   ├── deployment.yaml
# │   ├── service.yaml
# │   └── configmap.yaml
# └── overlays/
#     ├── desarrollo/
#     │   ├── kustomization.yaml
#     │   └── patch-replicas.yaml
#     ├── staging/
#     │   ├── kustomization.yaml
#     │   └── patch-resources.yaml
#     └── produccion/
#         ├── kustomization.yaml
#         └── patch-produccion.yaml

Bases y Overlays

Crea la capa base con los manifiestos comunes:

# base/deployment.yaml
cat << 'EOF' > base/deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: mi-aplicacion
spec:
  replicas: 1
  selector:
    matchLabels:
      app: mi-aplicacion
  template:
    metadata:
      labels:
        app: mi-aplicacion
    spec:
      containers:
        - name: app
          image: mi-aplicacion:latest
          ports:
            - containerPort: 8080
          resources:
            requests:
              cpu: 100m
              memory: 128Mi
            limits:
              cpu: 500m
              memory: 512Mi
EOF

# base/service.yaml
cat << 'EOF' > base/service.yaml
apiVersion: v1
kind: Service
metadata:
  name: mi-aplicacion
spec:
  selector:
    app: mi-aplicacion
  ports:
    - port: 80
      targetPort: 8080
EOF

# base/kustomization.yaml - Lista los recursos de la base
cat << 'EOF' > base/kustomization.yaml
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization

# Recursos que forman la base
resources:
  - deployment.yaml
  - service.yaml

# Etiquetas comunes aplicadas a todos los recursos
commonLabels:
  gestionado-por: kustomize
  version: v1
EOF

Crea el overlay de producción:

# overlays/produccion/kustomization.yaml
cat << 'EOF' > overlays/produccion/kustomization.yaml
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization

# Referencia a la base
bases:
  - ../../base

# Personalización del namespace de producción
namespace: produccion

# Prefijo para distinguir recursos entre entornos
namePrefix: prod-

# Imagen específica para producción
images:
  - name: mi-aplicacion
    newName: registry.empresa.com/mi-aplicacion
    newTag: v1.5.2

# Patches aplicados solo en producción
patches:
  - path: patch-replicas.yaml
    target:
      kind: Deployment
      name: mi-aplicacion
EOF

# overlays/produccion/patch-replicas.yaml
cat << 'EOF' > overlays/produccion/patch-replicas.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: mi-aplicacion
spec:
  # En producción usamos 3 réplicas para alta disponibilidad
  replicas: 3
EOF

Tipos de Patches

Kustomize soporta dos tipos principales de patches:

Strategic Merge Patch (fusión estratégica):

# patch-recursos.yaml - Actualizar recursos del contenedor
apiVersion: apps/v1
kind: Deployment
metadata:
  name: mi-aplicacion
spec:
  template:
    spec:
      containers:
        - name: app
          # Sobrescribir los límites de recursos para producción
          resources:
            requests:
              cpu: 500m
              memory: 512Mi
            limits:
              cpu: 2000m
              memory: 2Gi

JSON Patch (parches atómicos precisos):

# patch-json.yaml - Patch JSON 6902 para cambios precisos
patches:
  - target:
      kind: Deployment
      name: mi-aplicacion
    patch: |-
      - op: replace
        path: /spec/replicas
        value: 5
      - op: add
        path: /spec/template/spec/containers/0/env/-
        value:
          name: ENTORNO
          value: produccion
      - op: remove
        path: /spec/template/spec/containers/0/resources/limits/cpu

Generadores de ConfigMaps y Secrets

Kustomize puede generar ConfigMaps y Secrets automáticamente desde archivos:

# Crear los archivos de configuración
mkdir -p config/
echo "DEBUG=false" > config/app.env
echo "LOG_LEVEL=info" >> config/app.env
echo "DB_HOST=postgres.produccion.svc.cluster.local" >> config/app.env

# kustomization.yaml con generadores
cat << 'EOF' >> overlays/produccion/kustomization.yaml

# Generar ConfigMap desde archivo de propiedades
configMapGenerator:
  - name: configuracion-app
    envs:
      - config/app.env
    options:
      # No añadir hash al nombre (útil cuando otros recursos referencian por nombre fijo)
      disableNameSuffixHash: true

# Generar Secret desde archivos o literales
secretGenerator:
  - name: credenciales-db
    literals:
      - DB_PASSWORD=secreto123
    type: Opaque
    options:
      disableNameSuffixHash: true
EOF

Transformadores

Los transformadores modifican todos los recursos de forma global:

# kustomization.yaml - Transformadores comunes
transformers:
  - |-
    apiVersion: builtin
    kind: LabelTransformer
    metadata:
      name: etiquetas-comunes
    labels:
      entorno: produccion
      equipo: plataforma
    fieldSpecs:
      - path: metadata/labels
        create: true
      - path: spec/template/metadata/labels
        create: true
        kind: Deployment

annotations:
  # Anotaciones de gestión aplicadas a todos los recursos
  gestionado-por: kustomize
  repositorio: github.com/mi-org/mi-aplicacion
  fecha-despliegue: "2024-01-15"

Personalización por Entorno

# Estructura completa con tres entornos
mkdir -p overlays/{desarrollo,staging,produccion}

# overlays/desarrollo/kustomization.yaml
cat << 'EOF' > overlays/desarrollo/kustomization.yaml
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
bases:
  - ../../base
namespace: desarrollo
images:
  - name: mi-aplicacion
    newTag: latest
patches:
  - patch: |-
      apiVersion: apps/v1
      kind: Deployment
      metadata:
        name: mi-aplicacion
      spec:
        replicas: 1
        template:
          spec:
            containers:
              - name: app
                env:
                  - name: DEBUG
                    value: "true"
EOF

# Ver el resultado final sin aplicar (modo dry-run)
kubectl kustomize overlays/produccion

# Aplicar el overlay de producción al clúster
kubectl apply -k overlays/produccion

# Aplicar el overlay de desarrollo
kubectl apply -k overlays/desarrollo

# Ver diff entre lo que hay en el clúster y lo que se va a aplicar
kubectl diff -k overlays/produccion

Integración con CI/CD

# Script de despliegue en pipeline CI/CD
#!/bin/bash
set -e

ENTORNO=${1:-staging}
VERSION=${2:-latest}

# Verificar que el overlay existe
if [ ! -d "overlays/${ENTORNO}" ]; then
    echo "Error: el overlay ${ENTORNO} no existe"
    exit 1
fi

# Actualizar la imagen con la nueva versión
kustomize edit set image mi-aplicacion="registry.empresa.com/mi-aplicacion:${VERSION}" \
  --kustomization overlays/${ENTORNO}/kustomization.yaml

# Verificar la configuración generada
kustomize build overlays/${ENTORNO} > /tmp/manifiestos-${ENTORNO}.yaml

# Aplicar al clúster (modo server-side apply para mejor gestión de conflictos)
kubectl apply --server-side -f /tmp/manifiestos-${ENTORNO}.yaml

# Verificar el despliegue
kubectl rollout status deployment/mi-aplicacion -n ${ENTORNO}

Integración con Argo CD usando Kustomize:

# Argo CD detecta automáticamente kustomization.yaml
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
  name: mi-aplicacion-prod
  namespace: argocd
spec:
  source:
    repoURL: https://github.com/mi-org/mi-aplicacion
    targetRevision: main
    path: overlays/produccion  # Argo CD usa kustomize automáticamente
  destination:
    server: https://kubernetes.default.svc
    namespace: produccion
  syncPolicy:
    automated:
      prune: true

Solución de Problemas

# Generar y validar los manifiestos sin aplicar
kustomize build overlays/produccion | kubectl apply --dry-run=client -f -

# Depurar el kustomization.yaml
kustomize build overlays/produccion 2>&1 | head -50

# Verificar que todos los recursos referenciados existen
kustomize build base/

# Buscar qué overlay sobreescribe un campo específico
kustomize build overlays/produccion | grep -A5 "replicas"

# Error "map has no entry for key" - recursos no encontrados en la base
ls base/  # Verificar que los archivos existen

# Error de validación de schema
kustomize build overlays/produccion | kubectl apply --dry-run=server -f -

Conclusión

Kustomize ofrece una forma nativa y sin dependencias externas de gestionar configuraciones Kubernetes para múltiples entornos, manteniendo un único conjunto de manifiestos base. La combinación de overlays, patches y generadores permite mantener el principio DRY (Don't Repeat Yourself) mientras se adapta cada despliegue a sus requisitos específicos. Integrado con Argo CD o Flux, Kustomize forma el núcleo de cualquier estrategia GitOps sólida.