🛠️Herramientas

k9s: administra Kubernetes desde la terminal sin memorizar todos los comandos de kubectl

Felipe••12 min de lectura
Aprende a explorar pods, logs y eventos con k9s. Diagnostica un CrashLoopBackOff en minikube y entiende contextos, métricas y permisos de Kubernetes.

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 help

En Arch Linux:

sudo pacman -S k9s

En 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 minikube

En 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 nodes

Los 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: 8080

La 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 pods

Al 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 pods

Las 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ónEntrada 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 seleccionadod
Ver su YAMLy
Ver logs cuando la vista lo admitel
Ver logs del contenedor anteriorp
Volver o salir de un filtroEscape
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:

  1. READY y RESTARTS: 0/1 señala que el contenedor no está listo; los reinicios indican que ha habido nuevos intentos.
  2. Describe con d: busca estado actual, último estado terminado, motivo, código de salida y eventos.
  3. Logs anteriores con p: permite consultar la instancia anterior del contenedor, útil si el proceso muere rápidamente. Con varios contenedores, elige el afectado.
  4. 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_MESSAGE

La 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:

EvidenciaHipótesis que conviene comprobar
OOMKilledEl proceso fue terminado por falta de memoria; revisa límites y consumo
ImagePullBackOffNombre de imagen, conectividad o credenciales del registro
Pending con eventos de schedulingRecursos solicitados, restricciones o capacidad de nodos
Fallos de readinessEl contenedor puede seguir ejecutándose, pero no está listo para recibir tráfico
Fallos de livenessLa 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=120s

El 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 laboratorio

Alinear 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.1

Mientras ese proceso siga activo:

curl http://127.0.0.1:8088

La 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-lab

Espera 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-lab

El 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 KUBECONFIG

Conserva 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.

Referencias

#k9s#Kubernetes#kubectl#minikube#CrashLoopBackOff#logs#diagnóstico#RBAC

Artículos relacionados