Al final de la lección anterior tenías un clúster arrancado y verificado con un par de comandos prestados. Ahora vamos a por la herramienta en sí. kubectl es el cliente oficial de la API de Kubernetes y va a ser, con diferencia, el programa que más uses en este curso y en tu vida profesional con Kubernetes: crear, consultar, inspeccionar, depurar y borrar pasan todos por él. La buena noticia es que kubectl es extraordinariamente regular: una vez entiendes su gramática, sabes usarlo con cualquier tipo de objeto, incluidos los que aún no existen. Esta lección te enseña esa gramática, el fichero de configuración que decide con qué clúster hablas, los verbos que usarás a diario sobre los componentes de Rutas Norte, los formatos de salida que convierten kubectl en una herramienta de consulta seria, y los ajustes de productividad que separan a quien pelea con el clúster de quien trabaja con soltura.

Contenido

  1. Qué es kubectl e instalación
  2. El fichero kubeconfig: clústeres, usuarios y contextos
  3. Anatomía de un comando kubectl
  4. Los verbos del día a día
  5. Formatos de salida y consultas
  6. Selección por etiquetas y observación en vivo
  7. Imperativo frente a declarativo
  8. Productividad: alias, autocompletado, explain y plugins

  1. Qué es kubectl e instalación

kubectl no tiene inteligencia propia: traduce tus comandos a peticiones HTTPS contra el kube-apiserver. Todo lo que puede hacer kubectl lo podría hacer curl con el certificado adecuado; lo que aporta es comodidad, formato y validación local.

Consecuencia práctica: cualquier cosa que kubectl no te deje hacer, te la está prohibiendo el servidor (RBAC), no la herramienta.

Instalación

Linux:

curl -LO "https://dl.k8s.io/release/$(curl -L -s https://dl.k8s.io/release/stable.txt)/bin/linux/amd64/kubectl"
sudo install -o root -g root -m 0755 kubectl /usr/local/bin/kubectl
rm kubectl

macOS:

brew install kubectl

Windows (PowerShell):

winget install -e --id Kubernetes.kubectl

Verificación:

kubectl version
Client Version: v1.30.2
Kustomize Version: v5.0.4-0.20230601165947-6ce0bf390ce3
Server Version: v1.30.0

Regla de compatibilidad (version skew): kubectl admite una diferencia de una versión menor arriba o abajo respecto al servidor. Un cliente 1.30 funciona con servidores 1.29, 1.30 y 1.31. Con diferencias mayores pueden fallar comandos concretos de forma poco evidente, así que mantén el cliente cerca del servidor.

  1. El fichero kubeconfig: clústeres, usuarios y contextos

Cuando en la lección anterior minikube dijo "kubectl is now configured to use rutas-norte", lo que hizo fue escribir en ~/.kube/config. Ese fichero responde a tres preguntas: ¿a qué servidor hablo?, ¿con qué credenciales? y ¿en qué namespace por defecto?

# ~/.kube/config (simplificado)
apiVersion: v1
kind: Config
clusters:                       # A QUÉ servidor
  - name: rutas-norte
    cluster:
      server: https://192.168.49.2:8443
      certificate-authority: /home/usuario/.minikube/ca.crt
users:                          # CON QUÉ credenciales
  - name: rutas-norte
    user:
      client-certificate: /home/usuario/.minikube/profiles/rutas-norte/client.crt
      client-key: /home/usuario/.minikube/profiles/rutas-norte/client.key
contexts:                       # LA COMBINACIÓN de ambos + namespace
  - name: rutas-norte
    context:
      cluster: rutas-norte
      user: rutas-norte
      namespace: default
current-context: rutas-norte    # cuál está activo ahora

La idea clave: un contexto es una terna (clúster, usuario, namespace). Cambiar de contexto es cambiar de clúster o de identidad con un solo comando. En un entorno profesional tendrás contextos como rutas-norte-dev, rutas-norte-pre y rutas-norte-pro, y equivocarte de contexto es la forma más rápida de provocar un incidente.

Comandos de gestión de contexto

# Ver todos los contextos disponibles; el activo lleva asterisco
kubectl config get-contexts

# Cambiar de contexto
kubectl config use-context rutas-norte

# Ver solo el nombre del contexto activo
kubectl config current-context

# Fijar el namespace por defecto del contexto activo
kubectl config set-context --current --namespace=rutas-norte-dev
CURRENT   NAME          CLUSTER       AUTHINFO      NAMESPACE
*         rutas-norte   rutas-norte   rutas-norte   rutas-norte-dev
          docker-desktop docker-desktop docker-desktop

Ese último comando —fijar el namespace por defecto— te ahorrará escribir -n rutas-norte-dev cientos de veces durante el curso. Hazlo en cuanto crees el namespace en la lección 01-07.

Variable KUBECONFIG: puedes tener varios ficheros y combinarlos. Es lo habitual cuando trabajas con varios clientes o entornos:

export KUBECONFIG=~/.kube/config:~/.kube/rutas-norte-pro.yaml
kubectl config get-contexts

Consejo de seguridad: el kubeconfig contiene credenciales. Trátalo como una clave privada: permisos 600, nunca en Git, nunca por chat.

  1. Anatomía de un comando kubectl

Toda la CLI sigue esta gramática:

kubectl [verbo] [tipo] [nombre] [banderas]
         │       │      │        │
         │       │      │        └── -n, -o, -l, --dry-run, --watch...
         │       │      └─────────── nombre del objeto (opcional: si falta, todos)
         │       └────────────────── pod, deployment, service, pvc, node...
         └────────────────────────── get, describe, apply, delete, logs, exec...

Ejemplos leídos en voz alta:

kubectl get pods                                  # dame todos los pods del namespace actual
kubectl get pod api-reservas -n rutas-norte-dev   # dame ese pod concreto de ese namespace
kubectl describe deployment tienda-web            # explícame en detalle ese Deployment
kubectl delete pod api-reservas                   # borra ese pod

Nombres, plurales y abreviaturas

Kubernetes acepta singular, plural y abreviatura indistintamente. kubectl get po, kubectl get pod y kubectl get pods son idénticos. Las abreviaturas que más ahorran:

Tipo Abreviatura Tipo Abreviatura
pods po services svc
deployments deploy namespaces ns
replicasets rs configmaps cm
statefulsets sts persistentvolumeclaims pvc
daemonsets ds persistentvolumes pv
ingresses ing serviceaccounts sa

La lista completa y autoritativa de tu clúster, con abreviaturas, grupo de API y si el recurso vive en un namespace:

kubectl api-resources
NAME          SHORTNAMES   APIVERSION   NAMESPACED   KIND
configmaps    cm           v1           true         ConfigMap
pods          po           v1           true         Pod
services      svc          v1           true         Service
nodes         no           v1           false        Node
deployments   deploy       apps/v1      true         Deployment
ingresses     ing          networking.k8s.io/v1  true  Ingress

La columna NAMESPACED es importante: explica por qué kubectl get nodes -n rutas-norte-dev ignora el namespace (los nodos son de ámbito de clúster).

El namespace, siempre presente

kubectl get pods                       # namespace del contexto actual
kubectl get pods -n rutas-norte-pre    # un namespace concreto
kubectl get pods -A                    # todos los namespaces (--all-namespaces)

Olvidar el namespace es la causa número uno de "mi pod ha desaparecido".

  1. Los verbos del día a día

Trabajaremos sobre un escenario de Rutas Norte con la tienda web y la API ya desplegadas en rutas-norte-dev.

4.1. get — inventario rápido

kubectl get pods -n rutas-norte-dev
NAME                            READY   STATUS             RESTARTS      AGE
api-reservas-6c8d7f9b45-2xk9p   1/1     Running            0             12m
api-reservas-6c8d7f9b45-hn4vq   1/1     Running            0             12m
tienda-web-7d4f8c6b9-lq2mn      1/1     Running            0             18m
worker-notificaciones-59c7d8f   0/1     CrashLoopBackOff   5 (48s ago)   9m

Cómo leer cada columna:

  • READY 1/1: contenedores listos / contenedores totales del pod. 0/1 significa que el contenedor existe pero no ha superado su sonda de disponibilidad.
  • STATUS: Running es lo normal; Pending (el scheduler no encuentra hueco), ContainerCreating, ImagePullBackOff (no puede descargar la imagen), CrashLoopBackOff (arranca y muere en bucle), Completed (tareas terminadas).
  • RESTARTS: reinicios. Un número que crece es la señal más clara de un problema.
  • AGE: desde la creación del objeto.

En esa salida, worker-notificaciones está claramente roto. Los tres verbos siguientes son cómo se investiga.

4.2. describe — el diagnóstico

kubectl describe pod worker-notificaciones-59c7d8f -n rutas-norte-dev
Name:         worker-notificaciones-59c7d8f
Namespace:    rutas-norte-dev
Node:         rutas-norte/192.168.49.2
Labels:       app=worker-notificaciones
              app.kubernetes.io/part-of=rutas-norte
              entorno=dev
Status:       Running
Containers:
  worker:
    Image:          registry.rutasnorte.example/worker-notificaciones:1.2.0
    State:          Waiting
      Reason:       CrashLoopBackOff
    Last State:     Terminated
      Reason:       Error
      Exit Code:    1
    Restart Count:  5
Events:
  Type     Reason     Age                   From     Message
  ----     ------     ----                  ----     -------
  Normal   Scheduled  9m                    default-scheduler  Successfully assigned ...
  Normal   Pulled     8m (x4 over 9m)       kubelet  Container image already present
  Warning  BackOff    45s (x22 over 8m)     kubelet  Back-off restarting failed container

La sección Events es lo primero que hay que leer siempre. Es el diario de lo que el clúster ha intentado hacer con ese objeto y por qué ha fallado. Aquí nos dice que el contenedor termina con código 1 y el kubelet lo reintenta con espera creciente. También vemos que el scheduler sí lo asignó (luego no es un problema de recursos) y que la imagen se descargó bien (luego no es un problema de registro): el fallo está dentro de la aplicación.

describe funciona con cualquier tipo: kubectl describe node rutas-norte, kubectl describe svc api-reservas, kubectl describe pvc datos-postgres.

4.3. logs — la voz de la aplicación

# Últimas líneas del pod
kubectl logs worker-notificaciones-59c7d8f -n rutas-norte-dev

# Los logs de la ejecución ANTERIOR: imprescindible en CrashLoopBackOff
kubectl logs worker-notificaciones-59c7d8f -n rutas-norte-dev --previous

# Seguir en vivo, últimas 50 líneas, con marcas de tiempo
kubectl logs -f --tail=50 --timestamps api-reservas-6c8d7f9b45-2xk9p -n rutas-norte-dev

# Logs agregados de TODAS las réplicas seleccionadas por etiqueta
kubectl logs -l app=api-reservas -n rutas-norte-dev --tail=20

# Un contenedor concreto de un pod multicontenedor
kubectl logs mi-pod -c sidecar-metricas
[2026-08-05T02:14:07Z] worker-notificaciones v1.2.0 arrancando
[2026-08-05T02:14:07Z] conectando a redis-cache:6379
[2026-08-05T02:14:12Z] ERROR: dial tcp: lookup redis-cache: no such host
[2026-08-05T02:14:12Z] proceso terminado con codigo 1

Ahí está la causa: el worker no encuentra redis-cache por DNS, probablemente porque el Service aún no existe. --previous es la bandera que más se olvida y la más útil: sin ella, en un CrashLoopBackOff verás los logs de un contenedor que acaba de arrancar y aún no ha fallado.

4.4. exec — entrar en el contenedor

# Un comando puntual
kubectl exec api-reservas-6c8d7f9b45-2xk9p -n rutas-norte-dev -- env | grep DB_

# Sesión interactiva
kubectl exec -it api-reservas-6c8d7f9b45-2xk9p -n rutas-norte-dev -- sh
DB_HOST=postgres-reservas
DB_PORT=5432
DB_NAME=reservas

El -- separa las banderas de kubectl del comando a ejecutar dentro; olvidarlo es un error clásico. -it significa interactivo con terminal, igual que en Docker.

Nota profesional: muchas imágenes bien construidas son distroless y no tienen ni sh ni herramientas de red. Para esos casos existe kubectl debug, que inyecta un contenedor efímero con utilidades; lo veremos en el módulo 7.

4.5. apply y delete — cambiar el estado deseado

kubectl apply -f k8s/base/api-reservas.yaml         # un fichero
kubectl apply -f k8s/base/                          # todos los del directorio
kubectl apply -f k8s/base/ --recursive              # incluyendo subdirectorios

kubectl delete -f k8s/base/api-reservas.yaml        # borrar lo que declara el fichero
kubectl delete pod api-reservas-6c8d7f9b45-2xk9p    # borrar un objeto concreto
kubectl delete pods -l app=api-reservas             # borrar por etiqueta

apply es el corazón del modelo declarativo y lo estudiaremos a fondo en la próxima lección, Objetos, Manifiestos YAML y el Modelo Declarativo.

Sobre delete: recuerda que el borrado es asíncrono y que borrar un pod gestionado por un controlador no lo elimina, solo provoca que se cree uno nuevo. Para eliminarlo de verdad hay que borrar el controlador.

4.6. edit — modificación puntual en caliente

kubectl edit deployment api-reservas -n rutas-norte-dev

Abre el objeto en tu editor ($EDITOR) y al guardar aplica los cambios. Es cómodo para experimentar y peligroso en producción: el cambio no queda en Git, así que el siguiente apply desde el repositorio lo revertirá silenciosamente. Úsalo para explorar; nunca como forma de desplegar.

4.7. port-forward — acceso local sin exponer nada

kubectl port-forward pod/tienda-web-7d4f8c6b9-lq2mn 8080:80 -n rutas-norte-dev
Forwarding from 127.0.0.1:8080 -> 80
Forwarding from [::1]:8080 -> 80

Ahora http://localhost:8080 llega a ese pod. El formato es puerto-local:puerto-del-contenedor. También funciona contra un Service (svc/api-reservas 8081:80). Es la forma más rápida de comprobar que algo funciona antes de tener Ingress, y es exactamente lo que usarás en la lección 01-07 para ver la tienda de Rutas Norte en tu navegador. El proceso queda en primer plano: se corta con Ctrl+C.

4.8. explain — la documentación offline

kubectl explain pod.spec.containers.resources
KIND:       Pod
VERSION:    v1
FIELD: resources <ResourceRequirements>
DESCRIPTION:
    Compute Resources required by this container. Cannot be updated.
FIELDS:
  limits <map[string]Quantity>
    Limits describes the maximum amount of compute resources allowed.
  requests <map[string]Quantity>
    Requests describes the minimum amount of compute resources required.

Este comando merece un párrafo aparte porque es el que más rentabilidad tiene a largo plazo. Consulta el esquema de tu propio clúster, incluidos los CRDs instalados, así que nunca te da información desactualizada ni de otra versión. Funciona a cualquier profundidad y con --recursive muestra el árbol completo de campos:

kubectl explain deployment.spec.strategy
kubectl explain ingress.spec --recursive | head -30

En la certificación CKA/CKAD, donde solo se permite consultar la documentación oficial, kubectl explain es el atajo que ahorra minutos.

  1. Formatos de salida y consultas

La bandera -o transforma kubectl de visor en herramienta de consulta.

Formato Para qué sirve
(por defecto) Tabla resumida legible
-o wide Añade columnas: IP del pod, nodo, imagen
-o yaml El objeto completo tal como está en la API, con su status
-o json Igual, en JSON, para procesar con jq
-o name Solo tipo/nombre, ideal para encadenar comandos
-o jsonpath='...' Extrae campos concretos
-o custom-columns=... Tabla a medida
kubectl get pods -o wide -n rutas-norte-dev
NAME                            READY   STATUS    IP           NODE          NOMINATED NODE
api-reservas-6c8d7f9b45-2xk9p   1/1     Running   10.244.0.17  rutas-norte   <none>
tienda-web-7d4f8c6b9-lq2mn      1/1     Running   10.244.0.14  rutas-norte   <none>
# El objeto completo, incluyendo lo que ha escrito el sistema
kubectl get pod api-reservas-6c8d7f9b45-2xk9p -n rutas-norte-dev -o yaml | head -25

# Extraer campos concretos con jsonpath
kubectl get pods -n rutas-norte-dev \
  -o jsonpath='{range .items[*]}{.metadata.name}{"\t"}{.status.podIP}{"\n"}{end}'

# Tabla a medida: nombre, imagen y nodo
kubectl get pods -n rutas-norte-dev \
  -o custom-columns='POD:.metadata.name,IMAGEN:.spec.containers[0].image,NODO:.spec.nodeName'

# Ordenar: los pods que más han reiniciado, primero
kubectl get pods -A --sort-by=.status.containerStatuses[0].restartCount

# Ordenar por antigüedad
kubectl get pods -n rutas-norte-dev --sort-by=.metadata.creationTimestamp
POD                             IMAGEN                                                    NODO
api-reservas-6c8d7f9b45-2xk9p   registry.rutasnorte.example/api-reservas:2.4.0            rutas-norte
tienda-web-7d4f8c6b9-lq2mn      registry.rutasnorte.example/tienda-web:1.8.0              rutas-norte

custom-columns y jsonpath son las dos herramientas que convierten a kubectl en algo apto para auditorías rápidas: "dame todas las imágenes en uso en producción", "dime qué pods no tienen límites de memoria".

  1. Selección por etiquetas y observación en vivo

Recuerda de la lección Conceptos y Terminología Clave que las etiquetas son el mecanismo de acoplamiento del sistema. La bandera -l te da acceso a ese mismo mecanismo desde la CLI:

# Igualdad
kubectl get pods -l app=api-reservas -n rutas-norte-dev

# Varias condiciones (AND)
kubectl get pods -l app=api-reservas,entorno=dev -n rutas-norte-dev

# Desigualdad
kubectl get pods -l 'entorno!=pro' -A

# Conjuntos
kubectl get pods -l 'app in (api-reservas,tienda-web)' -n rutas-norte-dev
kubectl get pods -l 'app notin (informes-ocupacion)' -n rutas-norte-dev

# Existencia de la clave, con cualquier valor
kubectl get pods -l app.kubernetes.io/part-of -A

# Toda la plataforma Rutas Norte, de cualquier tipo, de una sola vez
kubectl get all -l app.kubernetes.io/part-of=rutas-norte -A

Esa última consulta es la razón por la que merece la pena ser disciplinado con el esquema de etiquetas del proyecto: te permite ver, borrar o inspeccionar la plataforma entera con un solo comando.

Observación en vivo con --watch (o -w): el comando no termina y va imprimiendo los cambios conforme ocurren. Es la mejor forma de ver el bucle de reconciliación en acción:

kubectl get pods -l app=api-reservas -n rutas-norte-dev --watch
NAME                            READY   STATUS              RESTARTS   AGE
api-reservas-6c8d7f9b45-2xk9p   1/1     Running             0          12m
api-reservas-6c8d7f9b45-2xk9p   1/1     Terminating         0          14m
api-reservas-6c8d7f9b45-wq7rt   0/1     Pending             0          0s
api-reservas-6c8d7f9b45-wq7rt   0/1     ContainerCreating   0          1s
api-reservas-6c8d7f9b45-wq7rt   1/1     Running             0          4s

Reconoces esa secuencia: es exactamente el recorrido de la lección Arquitectura de KubernetesPending mientras el scheduler decide, ContainerCreating mientras el kubelet trabaja, Running cuando el contenedor vive— ocurriendo ante tus ojos porque el ReplicaSet ha repuesto un pod borrado.

Comandos hermanos que también esperan:

kubectl wait --for=condition=Ready pod -l app=api-reservas -n rutas-norte-dev --timeout=60s
kubectl get events -n rutas-norte-dev --watch

  1. Imperativo frente a declarativo

kubectl admite los dos estilos y conviene tener criterio sobre cuándo usar cada uno.

Aspecto Imperativo Declarativo
Comando típico kubectl create deployment ..., kubectl scale, kubectl expose kubectl apply -f k8s/
Fuente de verdad El historial de tu terminal Los ficheros del repositorio
Reproducible No
Revisable en un pull request No
Velocidad para algo puntual Muy alta Media
Uso recomendado Aprender, pruebas rápidas, exámenes cronometrados Todo lo que llegue a un entorno real
# Imperativo: rápido, pero no queda rastro en ningún fichero
kubectl create deployment api-reservas --image=registry.rutasnorte.example/api-reservas:2.4.0
kubectl scale deployment api-reservas --replicas=4
kubectl expose deployment api-reservas --port=80 --target-port=3000

# Declarativo: el estado deseado vive en Git
kubectl apply -f k8s/base/api-reservas.yaml

El punto intermedio, y probablemente el truco más útil de toda la lección: generar el manifiesto con un comando imperativo y guardarlo, usando --dry-run=client -o yaml.

kubectl create deployment api-reservas \
  --image=registry.rutasnorte.example/api-reservas:2.4.0 \
  --dry-run=client -o yaml > k8s/base/api-reservas.yaml

Esto no crea nada en el clúster: solo imprime el YAML que kubectl habría enviado. Lo editas, lo versionas y lo aplicas. Es la forma habitual de no escribir manifiestos desde cero, y en la CKAD es prácticamente obligatorio para llegar a tiempo. La próxima lección profundiza en --dry-run, kubectl diff y la mecánica interna de apply.

  1. Productividad: alias, autocompletado, explain y plugins

Añade esto a tu ~/.bashrc o ~/.zshrc. La diferencia de comodidad es enorme:

# Autocompletado (bash)
source <(kubectl completion bash)

# Alias corto y autocompletado también para el alias
alias k=kubectl
complete -o default -F __start_kubectl k

# Atajos habituales
alias kgp='kubectl get pods'
alias kgpw='kubectl get pods -o wide'
alias kd='kubectl describe'
alias kl='kubectl logs'
alias kaf='kubectl apply -f'
alias kns='kubectl config set-context --current --namespace'

Para zsh, sustituye completion bash por completion zsh. Con esto, k get po -n <TAB> te completa incluso los nombres de los namespaces existentes, porque el autocompletado consulta la API.

Otras herramientas que conviene conocer, aunque no las usemos aún:

Herramienta Qué aporta
krew Gestor de plugins de kubectl (kubectl krew install ...)
kubectl ctx / kubectl ns (plugins ctx y ns) Cambiar de contexto y namespace de forma interactiva
kubectl tree Ver la jerarquía Deployment → ReplicaSet → Pod
kubectl neat Limpia un -o yaml de campos generados por el sistema
stern Logs agregados y coloreados de muchos pods a la vez
k9s Interfaz de terminal para navegar el clúster

Instalar krew y un plugin, a modo de ejemplo:

kubectl krew install ctx ns
kubectl ns rutas-norte-dev

Nada de esto es imprescindible, pero en un día de trabajo real ahorra horas. Lo esencial es lo anterior: alias, autocompletado y kubectl explain.

Errores Comunes y Consejos

  • Trabajar en el contexto equivocado. El error más caro de todos. Antes de cualquier comando destructivo, kubectl config current-context. Configura tu prompt para mostrar contexto y namespace (kube-ps1, starship, oh-my-zsh) y trátalo como innegociable en cuanto tengas acceso a un entorno de producción.
  • Olvidar el namespace. Si un objeto "no existe", pruébalo con -A antes de concluir nada. Fija el namespace por defecto con kubectl config set-context --current --namespace=....
  • Leer logs sin --previous en un CrashLoopBackOff. Verás el arranque del contenedor actual, no el error que mató al anterior.
  • Omitir el -- en kubectl exec. Sin él, kubectl interpreta las banderas de tu comando como suyas y falla de forma confusa.
  • Usar kubectl edit como método de despliegue. El cambio no queda en ningún sitio y se pierde en el siguiente apply. Sirve para investigar, no para operar.
  • Creer que kubectl delete pod elimina la aplicación. Si hay un controlador detrás, el pod vuelve. Hay que borrar el Deployment, o mejor, el manifiesto con kubectl delete -f.
  • Ignorar kubectl explain. Es documentación exacta de tu versión, sin conexión a internet y sin cambiar de ventana. Acostúmbrate a usarlo antes que a buscar en la web.
  • Consejo: memoriza este trío de diagnóstico y aplícalo siempre en este orden — kubectl get (¿existe y en qué estado?), kubectl describe (¿qué ha intentado el clúster y qué dicen los eventos?), kubectl logs (¿qué dice la aplicación?). Resuelve la gran mayoría de las incidencias.

Ejercicios

Ejercicio 1: Contexto, namespace y exploración

Sobre tu clúster rutas-norte:

  1. Muestra el contexto activo y comprueba que es el correcto.
  2. Crea el namespace rutas-norte-dev y fíjalo como namespace por defecto del contexto.
  3. Lista todos los pods del clúster, de todos los namespaces, mostrando el nodo y la IP.
  4. Averigua, sin salir de la terminal y sin buscar en internet, qué significa el campo spec.restartPolicy de un pod y qué valores admite.
  5. Muestra los pods de kube-system ordenados por antigüedad, del más antiguo al más reciente.

Ejercicio 2: Consultas avanzadas de auditoría

El equipo de seguridad de Rutas Norte pide un inventario. Escribe los comandos que responden a:

  1. Todas las imágenes en uso en el clúster, con el pod y el namespace al que pertenecen, en formato de tabla.
  2. Los pods de todo el clúster ordenados por número de reinicios, para localizar los inestables.
  3. Solo los nombres de los pods que tengan la etiqueta app.kubernetes.io/part-of=rutas-norte, en formato apto para encadenar con otro comando.
  4. Todos los recursos del clúster que no viven en un namespace.

Ejercicio 3: Del imperativo al declarativo

Sin crear nada en el clúster, genera el manifiesto YAML de un pod llamado tienda-web con la imagen registry.rutasnorte.example/tienda-web:1.8.0, guárdalo en k8s/base/tienda-web-pod.yaml, y después explica qué diferencia hay entre ejecutar ese comando con --dry-run=client y sin él. Como comprobación adicional, averigua con kubectl si el recurso Pod pertenece al grupo de API core o a apps.

Soluciones

Solución 1

# 1
kubectl config current-context                       # -> rutas-norte

# 2
kubectl create namespace rutas-norte-dev
kubectl config set-context --current --namespace=rutas-norte-dev

# 3
kubectl get pods -A -o wide

# 4
kubectl explain pod.spec.restartPolicy
FIELD: restartPolicy <string>
DESCRIPTION:
    Restart policy for all containers within the pod. One of Always, OnFailure,
    Never. Default to Always.
# 5
kubectl get pods -n kube-system --sort-by=.metadata.creationTimestamp

Solución 2

# 1
kubectl get pods -A -o custom-columns='NS:.metadata.namespace,POD:.metadata.name,IMAGEN:.spec.containers[*].image'

# 2
kubectl get pods -A --sort-by=.status.containerStatuses[0].restartCount

# 3
kubectl get pods -A -l app.kubernetes.io/part-of=rutas-norte -o name

# 4
kubectl api-resources --namespaced=false

El punto 3 devuelve líneas del tipo pod/api-reservas-6c8d7f9b45-2xk9p, que pueden pasarse directamente a kubectl delete o kubectl describe. El punto 4 lista nodes, namespaces, persistentvolumes, storageclasses, clusterroles, entre otros: los recursos de ámbito de clúster que vimos en la lección de conceptos.

Solución 3

kubectl run tienda-web \
  --image=registry.rutasnorte.example/tienda-web:1.8.0 \
  --dry-run=client -o yaml > k8s/base/tienda-web-pod.yaml

Diferencia: con --dry-run=client, kubectl construye el objeto localmente y lo imprime, sin enviar nada al servidor; no se crea ningún pod y no hace falta ni que el clúster esté arrancado. Sin esa bandera, la petición se envía al apiserver, pasa autenticación, autorización y admisión, se escribe en etcd y el pod se crea de verdad. Existe además --dry-run=server, que sí envía la petición pero le indica al servidor que no persista: valida contra los webhooks de admisión reales sin crear nada. Lo veremos en la próxima lección.

Para el grupo de API:

kubectl api-resources | grep -w pods
pods    po    v1    true    Pod

La columna APIVERSION muestra v1 a secas, sin barra ni prefijo de grupo: eso significa que Pod pertenece al grupo core (o "legacy"), a diferencia de Deployment, que aparece como apps/v1.

Conclusión

kubectl es un cliente HTTP con una gramática muy regular: verbo, tipo, nombre y banderas. Dominarlo consiste en interiorizar esa gramática, saber siempre en qué contexto y namespace estás trabajando, manejar con soltura el trío de diagnóstico getdescribelogs, y aprovechar los formatos de salida y la selección por etiquetas para convertir la CLI en una herramienta de consulta real. Con kubectl explain tienes además documentación exacta de tu propio clúster sin salir de la terminal, y con los alias y el autocompletado el trabajo diario deja de ser tecleo.

En esta lección hemos usado apply sin explicar del todo qué hace por dentro, y hemos visto manifiestos YAML sin desmenuzar su estructura. Esa deuda la salda la siguiente lección, Objetos, Manifiestos YAML y el Modelo Declarativo: anatomía campo a campo de un objeto, grupos y versiones de la API, el YAML que hace falta saber, la diferencia real entre apply, create y replace, las herramientas de validación previa y cómo organizar los manifiestos de Rutas Norte en el directorio k8s/.

Curso de Kubernetes

Módulo 1: Introducción a Kubernetes

Módulo 2: Componentes Principales de Kubernetes

Módulo 3: Gestión de Configuración y Secretos

Módulo 4: Redes en Kubernetes

Módulo 5: Almacenamiento en Kubernetes

Módulo 6: Conceptos Avanzados de Kubernetes

Módulo 7: Monitoreo y Registro

Módulo 8: Seguridad en Kubernetes

Módulo 9: Escalado y Rendimiento

Módulo 10: Ecosistema y Herramientas de Kubernetes

Módulo 11: Estudios de Caso y Aplicaciones del Mundo Real

Módulo 12: Preparación para la Certificación de Kubernetes

© Copyright 2026. Todos los derechos reservados