Lesson  in  Kubernetes 101

kubectl declarativo

Del comando al fichero: genera manifiestos con dry-run, previsualiza cambios con diff, aplica directorios completos y personaliza entornos con Kustomize y patches, sin escribir YAML desde cero.

Generar, comparar, aplicar

En la lección anterior terminaste con un export en el bolsillo: do='--dry-run=client -o yaml'. Esta lección lo convierte en un método de trabajo: nunca escribir YAML desde cero, sino generarlo con kubectl, revisarlo, versionarlo y aplicarlo. Es el flujo declarativo con esteroides imperativos.

Trabaja desde la pestaña dev-machine.

Misión 1: la fábrica de manifiestos

Vas a generar tres manifiestos sin crear nada en el clúster, todos en un directorio manifests. Primero prepara el terreno:

mkdir -p /home/laborant/manifests && cd /home/laborant
export do='--dry-run=client -o yaml'

¿Cómo genero un Deployment sin crearlo en el clúster?

kubectl create deployment api --image=ghcr.io/iximiuz/labs/nginx:alpine --replicas=3 $do > manifests/deployment.yaml

¿Cómo genero un Service para ese Deployment que todavía no existe?

kubectl expose -f manifests/deployment.yaml --port=80 --target-port=80 $do > manifests/service.yaml

Fíjate en el matiz: kubectl expose deployment api consultaría el clúster (y api aún no existe), pero expose -f lee el fichero. Todo el flujo ocurre en tu disco.

¿Cómo genero un ConfigMap desde un fichero .env?

printf 'LOG_LEVEL=debug\nAPP_COLOR=azul\n' > .env
kubectl create configmap web-config --from-env-file=.env $do > manifests/configmap.yaml

Abre manifests/configmap.yaml en la pestaña IDE: cada línea del .env se ha convertido en una clave. La misma técnica funciona para Secrets y CronJobs:

kubectl create secret generic db-credentials --from-literal=user=admin --from-literal=password=s3cr3t $do > secret.yaml
kubectl create cronjob limpieza --image=alpine --schedule="0 2 * * *" $do > cronjob.yaml

(Estos dos no forman parte de la misión; genera los que quieras, la práctica es gratis.)

Misión 2: comparar antes de aplicar

Ahora sí, al clúster. Pero el flujo profesional tiene un paso intermedio que la mayoría se salta y luego lamenta:

¿Cómo previsualizo qué cambiaría antes de aplicar?

kubectl diff -f manifests/

Como todo es nuevo, el diff muestra tres objetos enteros en verde. Aburrido ahora, salvavidas cuando el clúster lleve meses vivo. Dos variantes del diff que conviene conocer:

kubectl apply -f manifests/deployment.yaml --dry-run=server
KUBECTL_EXTERNAL_DIFF="diff -u --color=always" kubectl diff -f manifests/

La primera valida contra la API real sin crear nada (detecta errores que el dry-run de cliente no puede, como webhooks de admisión o cuotas). La segunda cambia la herramienta con la que se pinta el diff: KUBECTL_EXTERNAL_DIFF admite el comando y sus argumentos, y kubectl le pasa dos directorios para que los compare. Aquí usamos el diff de siempre con color; si en tu máquina tienes colordiff o delta, van en su lugar — pero tienen que estar instalados, o kubectl falla con un executable file not found in $PATH.

¿Cómo aplico todos los manifiestos de un directorio?

kubectl apply -f manifests/

Con -R desciende recursivamente por subdirectorios. Y el patrón de despliegue que espera a que todo converja antes de continuar (el corazón de cualquier pipeline):

kubectl apply -f manifests/ && kubectl rollout status deployment/api

El resto del ciclo de vida, en tres preguntas

¿Cómo elimino todo lo definido en un directorio, o por etiqueta?

kubectl delete -f manifests/
kubectl delete pods,services -l app=api

(No los ejecutes ahora: la siguiente unidad necesita estos objetos vivos.)

¿Cómo espero a que un recurso alcance una condición antes de continuar?

kubectl wait deployment/api --for=condition=Available --timeout=120s

¿Y cómo exporto el YAML de un recurso existente?

kubectl get deployment api -o yaml > deployment-exportado.yaml

Ábrelo y verás por qué no sirve tal cual para un repositorio: trae status, metadata.uid, resourceVersion, creationTimestamp y las anotaciones que pone el propio clúster. Nada de eso lo escribiste tú y nada de eso debe versionarse.

Hay plugins que limpian esa morralla —kubectl-neat es el más conocido, se instala con krew y no viene de serie—, pero el hábito profesional es otro: lo que va al repositorio se genera con --dry-run=client -o yaml, como en la Misión 1. Exportar sirve para inspeccionar un objeto vivo; para versionar, se genera.

Note

💡 Existe también kubectl apply --prune, que elimina del clúster lo que ya no está en tus ficheros. Es potente y destructivo a partes iguales (pruébalo siempre con --dry-run=server primero), y su mecanismo clásico de --prune-allowlist lleva años en alpha: Kubernetes evoluciona hacia el modelo ApplySet para sustituirlo. Si te interesa, revisa su estado en la versión de tu clúster antes de apostar por él.

Manifiestos generados, comparados y aplicados. La siguiente unidad responde la pregunta que ya te estará rondando: ¿y cuando necesito los mismos manifiestos con variaciones por entorno?

Kustomize y patches

Tienes unos manifiestos base funcionando. Ahora imagina la petición inevitable: "necesitamos lo mismo en producción, pero con 5 réplicas y un prefijo en los nombres". La solución ingenua es copiar los YAML y editarlos, y con ella aparecen los ficheros duplicados que divergen en silencio. La solución integrada en kubectl se llama Kustomize.

A diferencia de Helm (próxima lección), Kustomize no usa plantillas: parte de unos manifiestos base intactos y les aplica transformaciones declaradas en un archivo kustomization.yaml, organizadas en overlays por entorno.

Misión 3: el overlay de producción

Monta la estructura clásica y coloca tu base (los manifiestos de la unidad anterior):

cd /home/laborant
mkdir -p k8s/{base,overlays/{dev,prod}}
cp manifests/deployment.yaml manifests/service.yaml k8s/base/

cat << 'EOF' > k8s/base/kustomization.yaml
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
  - deployment.yaml
  - service.yaml
EOF

Y ahora el overlay de producción, en k8s/overlays/prod/kustomization.yaml:

cat << 'EOF' > k8s/overlays/prod/kustomization.yaml
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
  - ../../base
namePrefix: prod-
replicas:
  - name: api
    count: 5
labels:
  - pairs:
      env: tienda
    includeSelectors: true
EOF

El YAML, explicado con preguntas y respuestas

¿Por qué resources apunta a ../../base en vez de a ficheros?

Porque un overlay consume otra kustomization completa, no manifiestos sueltos. La base ni se entera de que existen overlays: permanece intacta y compartida por todos los entornos.

¿Qué hace exactamente namePrefix?

Renombra todos los recursos generados (api pasa a prod-api) y, lo importante, actualiza también las referencias entre ellos: el Service renombrado sigue apuntando a los Pods correctos. Ese seguimiento de referencias es lo que no consigues con un buscar y reemplazar.

¿replicas y labels modifican mi deployment.yaml?

No tocan un solo byte de la base: son transformaciones aplicadas al vuelo sobre la copia en memoria. replicas cambia el contador del Deployment llamado api, y labels añade la etiqueta a todos los recursos.

¿Y ese includeSelectors: true de qué va?

De la parte peligrosa. Por defecto, labels solo escribe en la metadata de los objetos; con includeSelectors: true escribe además en el selector del Deployment y en las labels de su plantilla de Pod. Aquí lo queremos, porque el overlay produce un Deployment nuevo (prod-api) y sus tres sitios deben ser coherentes.

En un Deployment que ya existe, en cambio, es una bomba: el selector es inmutable, así que la API rechaza el cambio y el despliegue se queda a medias. Esa es la razón de que el antiguo commonLabels —que hacía siempre las dos cosas, sin preguntar— esté deprecado, y de que su sustituto obligue a pedir el comportamiento peligroso a mano.

¿Y si necesito un cambio que no tiene transformador propio?

Para eso están los patches: ficheros YAML parciales que se funden con la base (verás la mecánica en la misión 4). Kustomize también sabe generar ConfigMaps y Secrets desde el propio kustomization (configMapGenerator, secretGenerator) y agrupar funcionalidades reutilizables en components.

El flujo de trabajo tiene dos comandos, y el orden importa:

¿Cómo previsualizo el YAML final que generaría Kustomize?

kubectl kustomize k8s/overlays/prod/

Repasa la salida: nombres con prefijo, 5 réplicas, etiquetas nuevas, y tu base intacta en disco.

¿Cómo aplico un overlay directamente?

kubectl apply -k k8s/overlays/prod/

Observa que ahora conviven api (la base aplicada en la unidad anterior) y prod-api (el overlay): dos entornos del mismo origen, en el mismo clúster de práctica.

Note

💡 kubectl kustomize y apply -k usan la versión de Kustomize empaquetada con kubectl, que a veces va una versión por detrás del binario independiente kustomize. Si algún día necesitas una función muy reciente, instala el binario aparte.

Misión 4: patches para cambios puntuales

No todo merece un overlay. Para retoques quirúrgicos sobre objetos vivos existe kubectl patch, en tres dialectos. Aplica estos dos al Deployment api (el de la base):

¿Cómo cambio el número de réplicas con un patch JSON?

kubectl patch deployment api --type='json' -p='[{"op":"replace","path":"/spec/replicas","value":5}]'

¿Cómo añado una anotación con un patch merge?

kubectl patch deployment api --type='merge' -p='{"metadata":{"annotations":{"deploy-time":"2026-07-09"}}}'

El tercer dialecto, el patch estratégico, es el que usa kubectl apply por debajo: fusiona un YAML parcial con inteligencia sobre el esquema (sabe, por ejemplo, fusionar listas de contenedores por nombre en vez de reemplazarlas). Un fichero como este, aplicado con patch --patch-file, añadiría una variable de entorno sin declarar el resto del contenedor:

spec:
  template:
    spec:
      containers:
        - name: nginx
          env:
            - name: LOG_LEVEL
              value: "debug"

Resumen

  • El método: generar con $do, revisar con diff y aplicar por directorios. Lo que va al repositorio se genera; del clúster solo se exporta para mirarlo.
  • Kustomize deriva entornos desde una base intacta: overlays con namePrefix, replicas, labels y patches.
  • Previsualizar (kubectl kustomize) antes de aplicar (apply -k), siempre.
  • kubectl patch en tres dialectos (json, merge, estratégico) para retoques puntuales con precisión.
Previous lesson
kubectl imperativo
Next lesson
Helm