Kustomize es la herramienta de Kubernetes para personalizar manifiestos YAML sin plantillas: mantienes una base con lo común y aplicas overlays con las diferencias de cada entorno. Viene integrada en kubectl, así que no necesitas instalar nada adicional para usarla. En este tutorial construirás una aplicación de ejemplo con una base y dos overlays (desarrollo y producción), usarás patches, generarás un ConfigMap y desplegarás el resultado en un clúster.

Requisitos previos

  • Una máquina con Ubuntu 24.04 LTS (tu equipo o un VPS de CubePath) con kubectl instalado.
  • Acceso a un clúster de Kubernetes con permisos para crear namespaces y Deployments. Sirve un clúster local con kind o k3s.
  • Conocimientos básicos de Deployments y Services.

Comprueba que kubectl llega al clúster y qué versión de Kustomize trae integrada:

kubectl version
Client Version: v1.33.1
Kustomize Version: v5.6.0
Server Version: v1.33.1

Cualquier kubectl reciente incluye Kustomize v5, que es la versión que se usa en esta guía.

Paso 1: Crear la estructura del proyecto

Kustomize trabaja con directorios. Cada directorio contiene un kustomization.yaml que lista los recursos y las transformaciones que se aplican. Crea la estructura:

mkdir -p ~/mi-aplicacion/base ~/mi-aplicacion/overlays/desarrollo ~/mi-aplicacion/overlays/produccion
cd ~/mi-aplicacion

Al terminar el tutorial el proyecto tendrá esta forma:

  • base/: deployment.yaml, service.yaml y kustomization.yaml, comunes a todos los entornos.
  • overlays/desarrollo/: kustomization.yaml con los cambios para desarrollo.
  • overlays/produccion/: kustomization.yaml, un patch de réplicas y recursos, y el archivo app.env del ConfigMap.

Paso 2: Definir la base

La base contiene los manifiestos que comparten todos los entornos. Crea el Deployment:

nano 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: nginx:1.27
          ports:
            - containerPort: 80
          env:
            - name: ENTORNO
              value: base
          resources:
            requests:
              cpu: 50m
              memory: 64Mi
            limits:
              memory: 128Mi

Crea el Service:

nano base/service.yaml
apiVersion: v1
kind: Service
metadata:
  name: mi-aplicacion
spec:
  selector:
    app: mi-aplicacion
  ports:
    - port: 80
      targetPort: 80

Por último, el kustomization.yaml de la base, que enumera los recursos y añade una etiqueta común:

nano base/kustomization.yaml
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization

resources:
  - deployment.yaml
  - service.yaml

labels:
  - pairs:
      app.kubernetes.io/part-of: mi-aplicacion
      app.kubernetes.io/managed-by: kustomize

Renderiza la base para comprobar que es válida:

kubectl kustomize base

La salida es el YAML final con la etiqueta app.kubernetes.io/part-of añadida a ambos recursos. kubectl kustomize no toca el clúster: solo imprime lo que se aplicaría.

Paso 3: Crear el overlay de desarrollo

Un overlay referencia la base en resources y declara solo lo que cambia. Para desarrollo usarás un namespace propio, un prefijo en los nombres y un patch inline que cambia una variable de entorno:

nano overlays/desarrollo/kustomization.yaml
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization

resources:
  - ../../base

namespace: desarrollo
namePrefix: dev-

patches:
  - patch: |-
      apiVersion: apps/v1
      kind: Deployment
      metadata:
        name: mi-aplicacion
      spec:
        template:
          spec:
            containers:
              - name: app
                env:
                  - name: ENTORNO
                    value: desarrollo

Este patch es de tipo strategic merge: se escribe como un fragmento del recurso y Kustomize lo fusiona con la base, usando el nombre del contenedor (app) y el de la variable (ENTORNO) como claves.

Renderiza el overlay:

kubectl kustomize overlays/desarrollo | grep -E 'name:|namespace:|value:'
  name: dev-mi-aplicacion
  namespace: desarrollo
  name: dev-mi-aplicacion
  namespace: desarrollo
        - name: ENTORNO
          value: desarrollo
        name: app

Los dos recursos tienen ahora el prefijo dev- y el namespace desarrollo, y la variable ENTORNO vale desarrollo.

Paso 4: Crear el overlay de producción con patches y un ConfigMap

Producción necesita más réplicas, más recursos, una imagen fijada y configuración propia. Empieza por un patch strategic merge en un archivo aparte:

nano overlays/produccion/patch-deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: mi-aplicacion
spec:
  replicas: 3
  template:
    spec:
      containers:
        - name: app
          resources:
            requests:
              cpu: 250m
              memory: 256Mi
            limits:
              memory: 512Mi

Crea el archivo de variables con el que Kustomize generará un ConfigMap:

nano overlays/produccion/app.env
LOG_LEVEL=info
CACHE_TTL=300

Ahora el kustomization.yaml de producción. Combina un patch desde archivo, un patch JSON 6902 para un cambio puntual, el cambio de imagen y el generador:

nano overlays/produccion/kustomization.yaml
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization

resources:
  - ../../base

namespace: produccion
namePrefix: prod-

images:
  - name: nginx
    newTag: "1.27-alpine"

configMapGenerator:
  - name: app-config
    envs:
      - app.env

patches:
  - path: patch-deployment.yaml
  - target:
      kind: Deployment
      name: mi-aplicacion
    patch: |-
      - op: replace
        path: /spec/template/spec/containers/0/env/0/value
        value: produccion
      - op: add
        path: /spec/template/spec/containers/0/envFrom
        value:
          - configMapRef:
              name: app-config

Qué hace cada bloque:

  • images cambia la etiqueta de la imagen nginx en todos los recursos sin tocar la base.
  • configMapGenerator crea un ConfigMap a partir de app.env y añade un sufijo con el hash de su contenido (por ejemplo prod-app-config-7h2k9fmt4b). Kustomize actualiza automáticamente la referencia app-config del Deployment, así que cada cambio de configuración provoca un rollout.
  • El patch JSON 6902 usa operaciones exactas (replace, add, remove) sobre rutas del objeto. Es útil cuando el strategic merge no basta, por ejemplo para listas sin clave de fusión.

Renderiza y revisa las partes clave:

kubectl kustomize overlays/produccion | grep -E 'replicas:|image:|app-config|value: produccion'
  name: prod-app-config-7h2k9fmt4b
  replicas: 3
          value: produccion
            name: prod-app-config-7h2k9fmt4b
        image: nginx:1.27-alpine

Paso 5: Desplegar los overlays en el clúster

Kustomize no crea el namespace si no lo incluyes como recurso. Créalos:

kubectl create namespace desarrollo
kubectl create namespace produccion

Antes de aplicar, revisa las diferencias con lo que hay en el clúster. La primera vez todo aparece como nuevo:

kubectl diff -k overlays/produccion

Aplica ambos overlays con la opción -k, que ejecuta Kustomize antes de enviar los manifiestos:

kubectl apply -k overlays/desarrollo
kubectl apply -k overlays/produccion
configmap/prod-app-config-7h2k9fmt4b created
service/prod-mi-aplicacion created
deployment.apps/prod-mi-aplicacion created

Comprueba que el Deployment de producción tiene tres réplicas listas y la variable correcta:

kubectl -n produccion rollout status deployment/prod-mi-aplicacion
kubectl -n produccion exec deploy/prod-mi-aplicacion -- printenv ENTORNO LOG_LEVEL
deployment "prod-mi-aplicacion" successfully rolled out
produccion
info

Paso 6: Actualizar la imagen desde un pipeline de CI/CD

En un pipeline no conviene editar YAML a mano. La CLI independiente de Kustomize incluye kustomize edit, que modifica el kustomization.yaml de forma segura. Descarga el script oficial de instalación, revísalo y ejecútalo; deja el binario en el directorio actual:

curl -fsSLO https://raw.githubusercontent.com/kubernetes-sigs/kustomize/master/hack/install_kustomize.sh
less install_kustomize.sh
bash install_kustomize.sh
sudo install -m 0755 kustomize /usr/local/bin/kustomize
kustomize version

Fija la nueva versión de la imagen en el overlay de producción:

cd ~/mi-aplicacion/overlays/produccion
kustomize edit set image nginx=nginx:1.28-alpine
grep -A2 images kustomization.yaml
images:
- name: nginx
  newTag: 1.28-alpine

En el pipeline, tras este comando, haz commit del cambio y deja que la herramienta de GitOps lo despliegue, o aplícalo directamente con kubectl apply -k . seguido de kubectl -n produccion rollout status deployment/prod-mi-aplicacion.

Si usas Argo CD, no necesitas configurar nada especial: detecta el kustomization.yaml del directorio indicado en path y lo renderiza:

apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
  name: mi-aplicacion-produccion
  namespace: argocd
spec:
  project: default
  source:
    repoURL: https://github.com/your_org/mi-aplicacion.git
    targetRevision: main
    path: overlays/produccion
  destination:
    server: https://kubernetes.default.svc
    namespace: produccion
  syncPolicy:
    automated:
      prune: true
      selfHeal: true

Solución de problemas

accumulating resources: ... no such file or directory: una ruta de resources o patches no existe. Las rutas son relativas al directorio del kustomization.yaml que las declara, no al directorio desde el que ejecutas el comando.

El patch no se aplica o crea un recurso nuevo: el metadata.name y el kind del patch deben coincidir con el recurso de la base, con el nombre original (sin namePrefix).

field is immutable al aplicar: has cambiado etiquetas que forman parte de spec.selector, algo típico al usar commonLabels. Usa labels sin includeSelectors o recrea el Deployment.

Validar contra el esquema del clúster sin aplicar:

kubectl apply -k overlays/produccion --dry-run=server

Conclusión

Has creado una base reutilizable y dos overlays que la adaptan a desarrollo y producción con patches strategic merge y JSON 6902, un ConfigMap generado con hash y un cambio de imagen automatizable. Como siguientes pasos, puedes gestionar los overlays con Argo CD o Flux, añadir componentes reutilizables con kind: Component para funcionalidades opcionales, o combinar Kustomize con Skaffold para el desarrollo local.