Un pod reinicia continuamente. Para investigar necesitas ver su estado, encontrar el contenedor afectado, consultar los logs anteriores y revisar los eventos. Puedes hacerlo con kubectl, pero alternar entre esos datos exige repetir comandos y copiar identificadores.
k9s reúne esas vistas en una interfaz de terminal que se actualiza mientras navegas. Su utilidad aparece cuando puedes seguir una pista de un recurso a otro sin perder de vista el contexto del clúster. No elimina la necesidad de entender Kubernetes: un estado rojo sigue necesitando una explicación.
En este tutorial crearemos un clúster local con minikube y una aplicación que falla deliberadamente por una variable de entorno ausente. Usaremos k9s para inspeccionarla y kubectl para comprobar el diagnóstico y aplicar una corrección explícita.
Qué es k9s y cómo se relaciona con kubectl
k9s es una interfaz de usuario de terminal, o TUI, para explorar y operar recursos de Kubernetes. Permite cambiar de namespace, seleccionar pods, abrir sus logs y consultar el manifiesto sin abandonar la aplicación.
Tanto k9s como kubectl utilizan la API de Kubernetes y las credenciales configuradas para acceder al clúster. k9s no debe entenderse como una colección de alias que simplemente escribe todos los comandos por ti. Ofrece navegación interactiva; kubectl conserva su ventaja para comandos precisos, scripts, automatización y operaciones que quieres registrar de forma reproducible.
La versión utilizada en este laboratorio es k9s v0.51.0. Los atajos dependen de la vista y pueden cambiar o personalizarse: la ayuda ? dentro de tu instalación es una referencia esencial.
Instalación en macOS y Linux
La documentación oficial de instalación incluye paquetes y binarios para varias plataformas. En macOS, con Homebrew:
brew install derailed/k9s/k9s
k9s version
k9s helpEn Arch Linux:
sudo pacman -S k9sEn otras distribuciones puedes descargar el archivo Linux apropiado desde las publicaciones oficiales, elegir amd64 o arm64 según tu equipo, verificar su checksum y colocar el binario en un directorio de tu PATH. No asumas que un paquete de otra distribución o un binario de otra arquitectura funcionará.
El laboratorio también requiere kubectl, minikube y Docker activo. En macOS puedes preparar las herramientas con:
brew install kubectl minikubeEn Linux sigue la instalación de kubectl y la guía de minikube correspondiente a tu distribución. minikube necesita recursos locales e imágenes descargadas; no es una conexión a un clúster productivo.
Kubeconfig y contextos: identifica el destino antes de navegar
Un archivo kubeconfig describe cómo llegar al API server, qué identidad utilizar y qué contextos existen. Un contexto relaciona clúster, usuario y, opcionalmente, namespace. El namespace delimita un grupo de recursos dentro del clúster; no equivale a otro clúster.
Kubernetes permite organizar varios accesos en kubeconfig. Para este ejercicio utilizaremos un archivo independiente, sin leer configuraciones privadas ni mezclar contextos existentes:
mkdir k9s-lab
cd k9s-lab
export KUBECONFIG="$PWD/kubeconfig"
minikube start -p uncodigo-k9s-lab \
--driver=docker --cpus=2 --memory=3072
kubectl --context uncodigo-k9s-lab config current-context
kubectl --context uncodigo-k9s-lab get nodesLos comandos de este artículo están escritos para una shell compatible con Bash. El perfil uncodigo-k9s-lab identifica el clúster nuevo; comprueba que no exista antes de reutilizar ese nombre. Mantén KUBECONFIG en esta terminal y, si abres otra, expórtalo con la ruta absoluta del archivo del laboratorio.
El kubeconfig puede contener material de autenticación o referencias a ejecutables que obtienen credenciales. No lo publiques ni lo copies a un repositorio. Solo utiliza archivos de procedencia confiable.
Desplegar una aplicación que falla de forma controlada
Un Deployment mantiene la cantidad deseada de réplicas de una aplicación. Sus pods contienen los contenedores que ejecutan el proceso. Un Service ofrece un punto de acceso estable a los pods seleccionados por sus etiquetas.
Guarda app.yaml:
apiVersion: v1
kind: Namespace
metadata:
name: k9s-lab
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: demo
namespace: k9s-lab
spec:
replicas: 1
selector:
matchLabels:
app: demo
template:
metadata:
labels:
app: demo
spec:
containers:
- name: app
image: busybox:1.37.0
command: ["sh", "-c"]
args:
- |
if [ -z "$APP_MESSAGE" ]; then
echo "ERROR: falta APP_MESSAGE" >&2
exit 1
fi
mkdir -p /tmp/www
printf '%s\n' "$APP_MESSAGE" > /tmp/www/index.html
echo "Servidor listo en 8080"
exec httpd -f -p 8080 -h /tmp/www
ports:
- containerPort: 8080
readinessProbe:
httpGet:
path: /
port: 8080
initialDelaySeconds: 1
periodSeconds: 2
resources:
requests:
cpu: 10m
memory: 16Mi
limits:
cpu: 100m
memory: 32Mi
---
apiVersion: v1
kind: Service
metadata:
name: demo
namespace: k9s-lab
spec:
selector:
app: demo
ports:
- port: 80
targetPort: 8080La aplicación debería servir un texto por HTTP en el puerto 8080. Antes comprueba APP_MESSAGE; como todavía no la hemos definido, escribe un error y termina con código 1. La imagen y la aplicación son de laboratorio, no un ejemplo completo de despliegue de producción.
Aplica el manifiesto únicamente al contexto local:
kubectl --context uncodigo-k9s-lab apply -f app.yaml
kubectl --context uncodigo-k9s-lab -n k9s-lab get podsAl principio puedes ver ContainerCreating mientras se descarga la imagen. Después aparecerán reinicios y, durante la espera entre intentos, CrashLoopBackOff. Los nombres de pod y los contadores varían. La descarga de una imagen fallida produciría otro diagnóstico, como ImagePullBackOff.
Abrir k9s y orientarte entre recursos
Inicia la herramienta en el namespace del ejercicio y con las modificaciones deshabilitadas:
k9s --kubeconfig "$KUBECONFIG" \
--context uncodigo-k9s-lab \
-n k9s-lab --readonly -c podsLas opciones de k9s permiten fijar contexto, namespace y vista inicial. Confirma el destino mostrado antes de explorar. --readonly es una precaución de la interfaz; más adelante veremos por qué no reemplaza RBAC.
En las siguientes instrucciones, :pods significa pulsar :, escribir pods y confirmar con Enter. No se escribe en una shell.
| Acción | Entrada o atajo |
|---|---|
| Ver pods | :pods |
| Ver deployments | :deploy |
| Ver servicios | :svc |
| Ver eventos | :events |
| Elegir namespace | :ns |
| Elegir contexto | :ctx |
| Filtrar la lista actual | / y texto, seguido de Enter |
| Describir el recurso seleccionado | d |
| Ver su YAML | y |
| Ver logs cuando la vista lo admite | l |
| Ver logs del contenedor anterior | p |
| Volver o salir de un filtro | Escape |
| Ver ayuda de la vista | ? |
| Salir | :q |
Estos atajos se verificaron contra el README oficial de la versión. Usa las flechas para seleccionar y Enter para abrir los detalles disponibles. Las acciones dependen del recurso: un Service no tiene el mismo tipo de logs que un contenedor.
Empieza en :ns, selecciona k9s-lab y vuelve a :pods. En :deploy localiza demo; en :svc encontrarás el Service del mismo nombre. Puedes usar /demo para filtrar. La navegación no cambia el hecho de que son objetos distintos: el Deployment gestiona réplicas y el Service selecciona pods mediante app: demo.
Para entender relaciones entre recursos, k9s también ofrece vistas como :xray deploy k9s-lab. Úsalas como apoyo a la navegación; contrasta selectores, propietarios y estado en los recursos concretos antes de atribuir una causa.
Diagnosticar CrashLoopBackOff con estado, logs y eventos
CrashLoopBackOff indica que Kubernetes está esperando antes de volver a iniciar un contenedor que falla repetidamente. Describe el comportamiento de reintento, no su causa. Tampoco es una fase adicional del Pod: un Pod puede tener fase Running y un contenedor esperando en ese estado. La documentación del ciclo de vida explica esta distinción.
Selecciona el pod de demo y revisa:
- READY y RESTARTS:
0/1señala que el contenedor no está listo; los reinicios indican que ha habido nuevos intentos. - Describe con
d: busca estado actual, último estado terminado, motivo, código de salida y eventos. - Logs anteriores con
p: permite consultar la instancia anterior del contenedor, útil si el proceso muere rápidamente. Con varios contenedores, elige el afectado. - YAML con
y: verifica el comando y la configuración efectiva, especialmente variables de entorno.
En el laboratorio los logs anteriores mostraron:
ERROR: falta APP_MESSAGELa misma comprobación, con un comando reproducible, es:
kubectl --context uncodigo-k9s-lab -n k9s-lab \
logs deployment/demo --previous
kubectl --context uncodigo-k9s-lab -n k9s-lab \
describe pod -l app=demo--previous consulta el contenedor terminado anterior dentro del pod; no recupera indefinidamente todos los intentos ni los logs de un pod ya eliminado. Para conservación histórica necesitas una solución de logs aparte, como explicamos al distinguir logs, métricas y trazas.
En :events, revisa los eventos del namespace y su recurso asociado. Un evento de back-off explica que se están espaciando los reinicios, pero el mensaje concreto de la aplicación aporta aquí la causa. Filtrar por demo ayuda a localizar información; no garantiza una vista histórica completa. Los eventos tienen retención limitada.
Otros casos requieren pistas diferentes:
| Evidencia | Hipótesis que conviene comprobar |
|---|---|
OOMKilled | El proceso fue terminado por falta de memoria; revisa límites y consumo |
ImagePullBackOff | Nombre de imagen, conectividad o credenciales del registro |
Pending con eventos de scheduling | Recursos solicitados, restricciones o capacidad de nodos |
| Fallos de readiness | El contenedor puede seguir ejecutándose, pero no está listo para recibir tráfico |
| Fallos de liveness | La sonda puede provocar reinicios; verifica si representa correctamente la salud |
No atribuyas todos los reinicios a memoria ni cambies límites solo porque una columna esté en rojo. Lee la evidencia y comprueba la hipótesis.
Corregir la configuración y comprobar el resultado
La corrección en este caso es añadir la variable al Deployment, no borrar el pod repetidamente:
kubectl --context uncodigo-k9s-lab -n k9s-lab \
set env deployment/demo APP_MESSAGE='Hola desde el laboratorio'
kubectl --context uncodigo-k9s-lab -n k9s-lab \
rollout status deployment/demo --timeout=120sEl cambio modifica la plantilla y genera un nuevo pod. En k9s vuelve con Escape a :pods, observa el nuevo nombre y espera READY 1/1. Abre sus logs con l: en la ejecución verificada apareció Servidor listo en 8080.
Para mantener la corrección en el archivo fuente, agrega esta sección bajo el contenedor app en app.yaml:
env:
- name: APP_MESSAGE
value: Hola desde el laboratorioAlinear el manifiesto con el estado deseado evita que una aplicación posterior del archivo vuelva a introducir el fallo. En un proyecto real, revisa ese cambio mediante el flujo de Git del equipo.
Para comprobar el Service desde tu equipo, en otra terminal con el kubeconfig correcto:
kubectl --context uncodigo-k9s-lab -n k9s-lab \
port-forward service/demo 8088:80 --address=127.0.0.1Mientras ese proceso siga activo:
curl http://127.0.0.1:8088La respuesta debe ser Hola desde el laboratorio. El port-forward es una conexión temporal para inspección, no una publicación permanente del servicio. Ciérralo con Ctrl+C.
CPU y memoria: qué ocurre cuando faltan métricas
k9s puede mostrar consumo cuando el clúster expone las métricas correspondientes y tu identidad tiene permisos para consultarlas. Un valor ausente no significa consumo cero.
La API de métricas de recursos suele alimentarse mediante Metrics Server. Para el perfil local puedes habilitar el addon:
minikube -p uncodigo-k9s-lab addons enable metrics-server
kubectl --context uncodigo-k9s-lab top pods -n k9s-labEspera a que el componente esté disponible y reúna muestras; la consulta puede fallar al principio. Si k9s se inició antes de que existiera la API, vuelve a abrirlo y revisa CPU y memoria. Si sigue faltando información, comprueba el addon y permisos antes de modificar la aplicación.
CPU suele expresarse en millicores: 100m equivale a una décima de CPU. La memoria se muestra en unidades como MiB. Uso medido, requests y limits son conceptos distintos: requests participa en la planificación y limits restringe recursos de ejecución. Si ves porcentajes, consulta la cabecera y ayuda de la versión para saber contra qué referencia se calculan.
Estas métricas apoyan una inspección puntual. No sustituyen una plataforma de observabilidad con histórico, alertas y correlación de incidentes.
RBAC y operaciones destructivas
Kubernetes aplica autorización mediante mecanismos como RBAC, control de acceso basado en roles. Una identidad puede tener permiso para listar pods sin poder eliminarlos. k9s utiliza esos permisos: una interfaz accesible no concede autoridad adicional. La referencia oficial de RBAC describe recursos, verbos y vínculos de roles.
Antes de trabajar con una identidad de diagnóstico puedes comprobar capacidades:
kubectl --context uncodigo-k9s-lab auth can-i list pods -n k9s-lab
kubectl --context uncodigo-k9s-lab auth can-i get pods/log -n k9s-lab
kubectl --context uncodigo-k9s-lab auth can-i delete pods -n k9s-labEl perfil local suele usar una identidad administradora; estas consultas no la convierten en una identidad limitada. Para inspección real, solicita permisos concretos de lectura y logs en los namespaces necesarios, evitando acceso a Secrets o escritura si no forman parte de la tarea. Puedes ampliar la diferencia entre roles y decisiones por recurso en RBAC y ABAC.
--readonly reduce acciones de modificación en k9s, pero puedes cerrarlo y utilizar la misma credencial desde otra herramienta. La restricción efectiva debe existir también en el API server.
La documentación de k9s identifica Ctrl+D para eliminar y Ctrl+K para una eliminación inmediata sin el mismo diálogo de confirmación. No los utilizaremos. Los atajos de edición, shell y plugins también requieren entender sus efectos; revisa la ayuda y las personalizaciones del equipo. El mínimo privilegio ayuda incluso cuando alguien pulsa la tecla equivocada.
Cuándo elegir k9s y cuándo mantener kubectl
k9s ayuda al explorar un incidente: cambiar de namespace, seguir el estado de un pod, alternar logs y eventos y orientarse entre recursos. kubectl resulta apropiado para dejar comandos reproducibles, automatizar o aplicar manifiestos revisados. En este laboratorio usamos ambos con ese criterio.
No hace falta memorizar cada opción, pero sí comprender qué recurso estás mirando, a qué clúster pertenece y qué evidencia respalda el siguiente paso. El recorrido útil es estado → logs y eventos → hipótesis → cambio acotado → comprobación. Una interfaz rápida mejora la navegación; el diagnóstico sigue dependiendo de esas relaciones.
Cuando termines, cierra k9s con :q, detén cualquier port-forward y elimina únicamente el perfil creado para este laboratorio:
minikube delete -p uncodigo-k9s-lab
unset KUBECONFIGConserva app.yaml si quieres repetirlo. El kubeconfig generado pertenece al laboratorio y no debe versionarse. Eliminar el perfil borra también los recursos que desplegaste en él.
