Agotamiento de inodos en Kubernetes: por qué las alertas de espacio en disco pasan por alto la verdadera amenaza
Kubelet carece de advertencias tempranas sobre el agotamiento de inodos, lo que provoca desalojos súbitos de pods incluso cuando parece haber suficiente espacio en disco. Descubra cómo los archivos pequeños en las imágenes de contenedores desencadenan este fal
Traducido automáticamente del original en inglés.
Un nodo de trabajo de Kubernetes activó recientemente una alerta crítica por llenado del sistema de archivos, pero los diagnósticos estándar mostraban abundante espacio disponible en el disco. El culpable no era el uso de bytes, sino el agotamiento de inodos, un límite de recursos que kubelet supervisa solo en el momento de la crisis, en lugar de gestionarlo proactivamente como hace con la capacidad de almacenamiento.
Qué sucedió
Un SRE que investigaba una alerta NodeFilesystemFilesFillingUp encontró un estado contradictorio: el disco estaba al 83% de su capacidad, pero la tabla de inodos tenía un 67% de utilización, con solo 5,3 millones de inodos libres restantes. La alerta se disparó debido a la trayectoria del consumo de inodos, no al nivel absoluto. Mientras que el uso del disco se monitorea mediante dos mecanismos—recolección de basura en segundo plano y desalojo duro—los inodos cuentan con una única defensa: el desalojo duro.
La recolección de basura de imágenes de Kubelet se ejecuta cuando el uso de bytes supera el 85%, eliminando imágenes no utilizadas para reducir el uso al 80%. Sin embargo, este proceso ignora por completo el número de archivos. En Linux, kubelet sí monitoriza nodefs.inodesFree e imagefs.inodesFree, pero solo activa acciones cuando los inodos libres caen por debajo del 5%. Esto significa que la primera respuesta automatizada ante la presión de inodos es también la más disruptiva: desalojar pods en ejecución para salvar el nodo.
La investigación reveló que el nodo no sufría de hinchazón de logs ni fugas de volúmenes. En cambio, el drenaje de inodos provenía del almacén de instantáneas (snapshot store) de containerd. Cada capa de imagen desempaquetada crea un directorio de archivos, y las imágenes que contienen miles de archivos pequeños consumen inodos rápidamente. En este caso, un único paquete Node.js contribuyó con más de 21.000 archivos por instantánea. Con múltiples versiones de la misma imagen retenidas en el nodo, se consumieron millones de inodos mientras el espacio en disco permanecía relativamente disponible.
Cómo funciona
Los sistemas de archivos asignan inodos en el momento de la creación basándose en una proporción de inodos, típicamente uno por cada 16.384 bytes de capacidad en ext4. Este número es fijo; no puede crecer posteriormente. Los archivos pequeños son ineficientes porque cada archivo no vacío consume al menos un bloque de 4 KiB y un inodo. Si un sistema de archivos se llena con archivos de un byte, los inodos se agotarán cuando solo se haya utilizado el 25% del espacio en disco.
En el nodo del incidente, los cálculos sugerían una proporción de inodos más ajustada de aproximadamente 8.192 bytes por inodo, probablemente configurada durante el aprovisionamiento inicial. Incluso con este ajuste, los inodos se agotarían al 50% de uso del disco si se llenara con archivos pequeños. El snapshotter overlayfs de containerd desempaqueta cada capa distinta en su propio directorio. Deduplica las capas comprimidas por digest, pero no comparte nada entre las instantáneas desempaquetadas. Si dos compilaciones producen capas con digests diferentes—incluso debido a cambios de marca de tiempo—containerd almacena dos copias completas de cada archivo.
Kubelet no correlaciona el uso de bytes con el uso de inodos. Espera hasta el umbral del 5% de inodos libres antes de actuar. Para entonces, el nodo ya está en modo emergencia. La falta de un paso intermedio de "desalojo suave" o recolección de basura para los inodos significa que los ingenieros no reciben ninguna advertencia hasta que la estabilidad de los pods está en riesgo.
Detalles clave
- La recolección de basura de imágenes de Kubelet se activa al 85% de uso de bytes, pero no tiene un umbral equivalente para el uso de inodos.
- El desalojo duro para inodos solo se activa cuando los inodos libres caen por debajo del 5%, convirtiéndolo en una medida de último recurso.
- Los sistemas de archivos Ext4 tienen un recuento fijo de inodos determinado en la creación, típicamente uno por cada 16.384 bytes a menos que se personalice.
- Containerd desempaca cada digest de capa única en un directorio de instantánea separado, duplicando archivos pequeños entre versiones.
- Un único paquete Node.js como
@mui/icons-materialpuede contribuir con más de 21.000 archivos a una sola capa de imagen. - Usar
du --inodes -xS /var | sort -rhayuda a identificar directorios con alto número de archivos sin cruzar límites de sistemas de archivos.
Por qué importa
Para los ingenieros de infraestructura, este comportamiento expone un punto ciego en la monitorización estándar. La mayoría de los equipos rastrean de cerca los porcentajes de uso del disco, asumiendo que mantenerse por debajo del 80% garantiza la seguridad. Sin embargo, las aplicaciones que generan muchos archivos pequeños—como aquellas con grandes node_modules, entornos virtuales de Python o dependencias vendorizadas—pueden agotar los inodos mucho antes de que el espacio en disco sea crítico. Esto conduce a desalojos inesperados de pods e inestabilidad del nodo que parecen no estar relacionados con las métricas de almacenamiento.
La causa raíz a menudo reside en las prácticas de compilación más que en la configuración del clúster. Los Dockerfiles de etapa única que copian código fuente y dependencias en la imagen final crean capas repletas de archivos pequeños. Sin reglas adecuadas de .dockerignore o compilaciones multi-etapa, cada ejecución de CI puede generar nuevos digests de capa debido a cambios de marca de tiempo, forzando a los nodos a retener múltiples copias de estas capas cargadas de archivos. Comprender este mecanismo permite a los equipos cambiar el enfoque de la depuración reactiva del nodo a la optimización proactiva de imágenes.
Lo que puedes hacer
- Audita tus imágenes de contenedor buscando altos recuentos de archivos usando
du --inodesen los artefactos compilados antes de enviarlos a los registros. - Implementa Dockerfiles multi-etapa para asegurar que solo los artefactos compilados y las dependencias de producción lleguen a la imagen final.
- Usa
.dockerignorepara excluirnode_modules,.gity cachés de compilación de las instruccionesCOPYpara evitar duplicaciones innecesarias de archivos. - Monitoriza el uso de inodos junto con el espacio en disco en tu stack de observabilidad, estableciendo alertas en umbrales más altos.
- Revisa la configuración de inode ratio en tus sistemas de archivos si es posible, aunque ten en cuenta que esto suele requerir reformateo.



