Events, la primera línea de diagnóstico
Antes de instalar Prometheus, antes de montar Grafana, antes de discutir sobre trazas: Kubernetes ya te está contando lo que pasa, y lo hace en un sitio que casi nadie mira con atención.
El capítulo de observabilidad del libro recorre métricas, logs y trazas, las tres señales que hay que instrumentar. Este módulo empieza antes: por lo que el clúster ya te está diciendo gratis.
Un Event es un objeto de la API, como un Pod o un ConfigMap. Lo emite un componente del clúster (el scheduler, el kubelet, un controlador) cuando le ocurre algo digno de mención a otro objeto. kubectl describe los enseña al final, y ese "al final" es simbólico: es donde la mayoría de la gente deja de leer, justo antes de la respuesta.
En esta lección hay tres Pods rotos esperándote. Cada uno falla por un motivo distinto, y cada uno tiene un componente distinto quejándose de él.
kubectl get pods
Paso 1: imagen-fantasma, el fallo del kubelet
kubectl describe pod imagen-fantasma
Baja hasta Events. Verás una secuencia: el nodo asignado, un intento de descargar la imagen, y un Warning con el fallo.
Ese Warning te dice, con todas las letras, que el manifest de esa etiqueta no existe en el registro. No hay que adivinar nada: el error viene del registro de contenedores, atraviesa el runtime, y el kubelet lo publica en la API para que tú lo leas.
Fíjate también en el Age del evento: algo como 2m (x5 over 3m). Eso significa que el mismo evento se ha repetido cinco veces y Kubernetes lo ha agrupado en una sola línea con un contador, en lugar de inundarte con cinco entradas idénticas.
Paso 2: nodo-imposible, el fallo del scheduler
Este Pod ni siquiera ha llegado a un nodo. Pide 500 CPUs.
kubectl events --for pod/nodo-imposible
💡 kubectl events (disponible desde la versión 1.25 y estable desde la 1.28) es bastante mejor que el viejo kubectl get events: entiende --for, ordena por tiempo de forma sensata y sabe filtrar por tipo. Si llevas años escribiendo kubectl get events --sort-by=.lastTimestamp, ya puedes dejar de hacerlo.
El Reason te dice que no se ha podido programar, y el mensaje detalla por qué falló cada nodo: cuántos por CPU insuficiente, cuántos por taints, cuántos por afinidad. Es, literalmente, el reports de las fases de filtrado del scheduler que estudiaste en el módulo de Scheduling.
Y hay un detalle que merece la pena: mira quién emite este evento. No es el kubelet de ningún nodo, porque ningún kubelet ha visto jamás este Pod. Es el default-scheduler.
Esa es la utilidad real de los Events: te dicen qué componente del clúster tiene el problema. Un FailedScheduling es un problema de capacidad o de reglas de ubicación. Un Failed al descargar una imagen es un problema del nodo, del registro o de las credenciales. No se parecen en nada, y se diagnostican en sitios distintos.
Paso 3: api-rota, el fallo que se repite
kubectl events --for pod/api-rota
kubectl get pod api-rota
Este Pod arranca bien. La imagen se descarga, el contenedor se ejecuta... y a los pocos segundos el kubelet lo mata. Y otra vez. Y otra vez. La columna RESTARTS sube sola.
Los Events cuentan la historia completa: la sonda de liveness apunta a una ruta que devuelve 404, el kubelet lo marca como Unhealthy, lo mata, y el restartPolicy: Always lo levanta de nuevo. Con el tiempo verás también el BackOff: Kubernetes esperando cada vez más entre reintentos.
Aquí está la lección que separa a quien sabe diagnosticar de quien no:
kubectl logs api-rotate enseña la aplicación (y nginx, sin saber nada de todo esto, dirá que va perfectamente).kubectl events --for pod/api-rotate enseña a Kubernetes hablando de la aplicación.
El contenedor no se está muriendo. Lo están matando, y el motivo no aparece en los logs de la aplicación por ninguna parte. Nunca. Si solo miras los logs, este bug te puede costar una tarde.
Paso 4: Los Events son objetos, y se filtran como tal
Un Event es un objeto de la API con campos estructurados. Eso significa que se puede filtrar, ordenar y consultar como cualquier otra cosa:
# Todo lo que ha pasado en el clúster, en orden
kubectl events -A
# Solo los problemas
kubectl events --types=Warning -A
# Todo lo relativo a un objeto concreto
kubectl events --for pod/api-rota
# Y el clásico, por si tu clúster es antiguo
kubectl get events --field-selector type=Warning,reason=FailedScheduling
Guarda el listado de avisos del clúster, que es lo primero que harías en una incidencia real:
kubectl events --types=Warning -A > /home/laborant/warnings.txt
cat /home/laborant/warnings.txt
Ahí tienes, en una sola pantalla, los tres fallos del escenario. Sin instalar nada.
Paso 5: Repara el Pod
Ya sabes exactamente qué le pasa a imagen-fantasma. La etiqueta de la imagen no existe, y la imagen de un Pod no se puede cambiar en caliente:
kubectl delete pod imagen-fantasma
kubectl run imagen-fantasma --image=ghcr.io/iximiuz/labs/nginx:alpine
Paso 6: La letra pequeña
Ahora, la parte que hay que aprenderse antes de confiar en los Events.
Los Events caducan. El API server los borra pasado el --event-ttl, que por defecto es una hora. Se guardan en etcd, y etcd no es un sistema de logs: si guardara todos los eventos de un clúster grande para siempre, se lo comería vivo.
Las consecuencias son muy prácticas y hay que interiorizarlas:
- Un incidente de anoche no tiene Events esta mañana. Se han evaporado. Si nadie los recogió en su momento, la información ya no existe.
- Los Events no son una auditoría. Para eso está el audit log del API server, que es otra cosa completamente distinta: registra quién hizo qué petición, y se guarda en disco.
- Por eso se exportan. Herramientas como
kubernetes-event-exporter, el receptor de eventos de OpenTelemetry, o los agentes de cualquier suite de observabilidad hacen exactamente una cosa: hacenwatchsobre los Events y los mandan a un sitio donde duren más de una hora.
Es decir: el mejor argumento para montar el stack de la lección siguiente no es "quiero gráficas bonitas". Es que Kubernetes tira su mejor información a la basura cada sesenta minutos.
Los Reason que vas a ver el resto de tu vida
| Reason | Quién lo emite | Qué significa |
|---|---|---|
FailedScheduling | scheduler | Ningún nodo cumple los requisitos. Capacidad, taints o afinidad. |
Failed | kubelet | No pudo descargar la imagen (nombre, etiqueta o credenciales). |
BackOff | kubelet | Reintentando con espera creciente. Va con CrashLoopBackOff o ImagePullBackOff. |
Unhealthy | kubelet | Una probe ha fallado. Mira cuál: liveness reinicia, readiness solo saca del Service. |
OOMKilling | kubelet | El contenedor superó su limits.memory y el kernel lo mató. |
Evicted | kubelet | El nodo se quedó sin recursos y desalojó al Pod. Mira su clase QoS. |
FailedMount | kubelet | No pudo montar un volumen. Suele ser un Secret o ConfigMap que no existe. |
Preempted | scheduler | Otro Pod de mayor prioridad se quedó con su sitio. |
NodeNotReady | node controller | El nodo dejó de dar señales de vida. |
Resumen
- Los Events son objetos de la API que emiten los componentes del clúster. Son la primera línea de diagnóstico, y son gratis.
- El
Reasonte dice qué ha fallado. El emisor te dice quién tiene el problema, y por tanto dónde seguir mirando. kubectl logs≠kubectl events: los logs son la aplicación hablando; los eventos son Kubernetes hablando de la aplicación. Un contenedor al que matan por una liveness rota no deja rastro en sus propios logs.kubectl events --types=Warning -Aes el comando con el que empezar cualquier incidencia.- Los Events caducan a la hora y no son una auditoría. Si te importan, hay que exportarlos. Ese es el punto de partida de la lección siguiente.
- Previous lesson
- El operador
- Next lesson
- Métricas de recursos con kubectl top