Por qué df y du no coinciden: Encontrando gigabytes ocultos en Linux
El comando df -h muestra un uso del disco del 95%, mientras que du -sh /* suma solo el 20%. El 75% restante es invisible para du pero ocupa bloques del disco. La razón son los descriptores de archivos abiertos por procesos que apuntan a archivos eliminados. df lee el superbloque del sistema de archivos directamente, du recorre el árbol de directorios e ignora los archivos sin entradas en él.
Un inodo almacena metadatos del archivo, incluida la lista de bloques de datos. rm solo elimina la entrada del directorio (enlace físico), el contador de enlaces del inodo disminuye, pero si un proceso mantiene un descriptor, los bloques permanecen asignados.
Diagrama:
Antes de rm:
directorio inodo bloques
/var/log/ --> app.log --> [bloque1][bloque2]
^
descriptor de nginx (pid 1234, fd 7)
Después de rm:
directorio inodo bloques
/var/log/ --> (eliminado) --> [bloque1][bloque2]
^
descriptor de nginx
du no ve el archivo en /var/log/, df registra los bloques ocupados.
Casos clásicos del problema
Rotación de logs: logrotate ejecuta rm en un archivo antiguo, pero nginx o java mantienen un descriptor desde el inicio. El proceso escribe en el archivo eliminado, df registra un mayor uso, du no.
Otros escenarios:
- Demonios de larga duración con logs abiertos.
- Errores en scripts de limpieza donde rm precede al cierre del descriptor.
- Contenedores Docker donde procesos internos mantienen archivos del host.
El problema se acumula: los gigabytes desaparecen sin ser notados hasta que el disco se llena.
Encontrar procesos con archivos eliminados
Usa lsof +L1 para archivos con NLINK < 1 (eliminados del sistema de archivos pero abiertos):
COMANDO PID USUARIO FD TIPO DISPOSITIVO TAMAÑO/OFF NLINK NODO NOMBRE
nginx 1234 www 7w REG 8,1 2147483648 0 12345 /var/log/app.log (eliminado)
java 5678 app 3w REG 8,1 536870912 0 67890 /var/log/service.log (eliminado)
Campos clave:
- COMANDO/PID: proceso e ID.
- FD: descriptor (7w — escritura, número 7).
- TAMAÑO/OFF: tamaño — fuente de los GB perdidos.
- NLINK=0: confirmación de eliminación.
- NOMBRE (eliminado): ruta antes de rm.
Filtro por tamaño: lsof +L1 | awk '$7 > 104857600 {print}' (archivos >100MB).
Liberar espacio sin reiniciar
Para proceso PID 1234, FD 7:
$ > /proc/1234/fd/7
/proc es procfs en memoria. El comando pone a cero el archivo a través del descriptor, el kernel libera bloques al instante. df se actualiza, el proceso continúa escribiendo en fd 7.
Verificar: lsof +L1 | grep 1234 — TAMAÑO/OFF=0 o vacío.
Automatización con script:
#!/bin/bash
lsof +L1 | awk '$7 > 104857600 && $9 ~ /eliminado/ {print $2, $8}' | while read pid fd; do
echo "Poniendo a cero $pid/$fd"
> /proc/$pid/fd/$fd
echo "Espacio liberado"
done
Prevenir el problema en logrotate
Evitar rm en la rotación. En /etc/logrotate.d/app:
/var/log/app.log {
daily
rotate 7
compress
copytruncate
}
copytruncate copia el log, luego trunca el original (mecanismo >). El descriptor no cambia.
Desventaja: micro-retraso en la copia — posible pérdida de líneas.
Alternativa sin pérdidas:
- logrotate: mv antiguo → nuevo archivo.
- kill -HUP <pid> (SIGHUP para reabrir log).
nginx: nginx -s reopen. systemd: systemctl reload nginx.
Monitoreo y alertas
Script de verificación:
lsof +L1 -F s | awk '/^s/ && substr($0,2)+0 > 104857600 {count++} END {print count+0}'
Devuelve el conteo de archivos eliminados >100MB.
Integración con Prometheus:
node_filefd_allocated— crecimiento de fd abiertos.- Comparar df/disk_used vs suma de du.
Cron cada 5 min: alertar si discrepancia >10%.
Conclusiones clave
- df muestra bloques asignados desde el superbloque, du muestra archivos visibles en el directorio.
lsof +L1encuentra procesos con archivos (eliminados) por NLINK=0.- Liberar espacio:
> /proc/PID/fd/FD— poner a cero sin reiniciar. - copytruncate de logrotate previene el problema sin HUP.
- Monitoreo: scripts con lsof o métricas de node_exporter.
— Editorial Team
Aún no hay comentarios.