Problema
En entornos de producción auto‑gestionados es frecuente que un contenedor o proceso genere archivos inesperados (logs, dumps, archivos temporales) que llenan rápidamente el disco. La reacción típica es ampliar el volumen subyacente (por ejemplo, un EBS de 250 GB a 1.5 TB) para evitar que el servicio se caiga. Una vez eliminado el exceso de datos, el uso real vuelve a ser mucho menor, pero el coste del volumen sigue basado en su tamaño máximo. El desafío es reducir el tamaño lógico del volumen sin perder datos ni interrumpir servicios críticos, algo que los sistemas de archivos ext4 y xfs no permiten de forma directa en producción.
Causa
- Crecimiento inesperado de datos – scripts con bucles infinitos, logs sin rotación o procesos de sincronización que generan archivos temporales pueden consumir rápidamente el espacio disponible.
- Política de “expandir primero, investigar después” – en AWS es trivial aumentar el tamaño de un EBS, por lo que muchos administradores optan por la solución rápida sin validar la causa raíz.
- Limitaciones del sistema de archivos – ext4 y xfs pueden crecer en línea, pero la reducción requiere desmontar el FS o recrearlo, lo que implica una migración de datos.
- Dependencia de contenedores – bases de datos y servicios de medios corren dentro de Docker Compose; detenerlos brevemente es aceptable, pero una migración completa a un nuevo host suele ser demasiado costosa en tiempo.
Solución
La forma más segura y reutilizable consiste en crear un nuevo volumen más pequeño y copiar los datos usando rsync. El proceso se puede ejecutar con un downtime mínimo si se planifica la parada de los contenedores críticos y se aprovechan snapshots para volver atrás en caso de error.
Paso a paso general
- Identificar el punto de montaje del volumen problemático (p. ej.
/var/lib/postgresql/datao/srv/media). - Detener los servicios que escriben en él (
docker compose downosystemctl stop …). - Crear un snapshot del EBS actual; sirve como respaldo rápido y permite volver a un estado conocido.
- Crear un nuevo EBS del tamaño deseado (p. ej. 300 GB).
- Adjuntar el nuevo volumen a la instancia, asignarle un dispositivo (p. ej.
/dev/xvdf). - Formatear el nuevo dispositivo con el mismo tipo de FS que el original (ext4 o xfs).
- Montar temporalmente el nuevo volumen en una ruta de staging (
/mnt/newvol). - Copiar los datos con
rsync -aHx --progress /mnt/oldvol/ /mnt/newvol/. - Verificar la integridad (comparar tamaños, ejecutar
diffo checksums). - Actualizar
/etc/fstabo los volúmenes declarados en Docker Compose para apuntar al nuevo dispositivo/UUID. - Desmontar el volumen antiguo, desvincularlo y, opcionalmente, eliminarlo para ahorrar costes.
- Reiniciar los servicios y validar que todo funciona.
Este flujo es aplicable tanto a volúmenes EBS como a discos locales en Proxmox, siempre que el FS sea ext4 o xfs.
Cuándo aplicar esta solución
- Uso real ≪ tamaño del volumen después de una expansión de emergencia.
- El FS es ext4 o xfs y no se dispone de herramientas de reducción en línea.
- Se puede detener temporalmente los contenedores o servicios que acceden al disco (pocos minutos a una hora).
- Se cuenta con permisos de AWS para crear snapshots y volúmenes.
No es apropiado cuando:
- El downtime debe ser cero (p. ej. bases de datos de alta disponibilidad sin replicación).
- El volumen está en uso por un clúster distribuido que no permite desmontar el FS.
- El costo del downtime supera con creces el coste del espacio extra.
Código
# 1. Detener servicios Docker (ajusta al nombre de tu stack)
docker compose down
# 2. Snapshot del volumen actual (asume /dev/xvdb)
aws ec2 create-snapshot --volume-id vol-0abcd1234ef567890 --description "pre‑shrink snapshot"
# 3. Crear nuevo volumen (300 GB en la misma zona)
NEW_VOL_ID=$(aws ec2 create-volume \
--size 300 \
--availability-zone $(curl -s http://169.254.169.254/latest/meta-data/placement/availability-zone) \
--volume-type gp3 \
--query VolumeId --output text)
# 4. Esperar a que el volumen esté disponible
aws ec2 wait volume-available --volume-ids $NEW_VOL_ID
# 5. Adjuntar nuevo volumen como /dev/xvdf
aws ec2 attach-volume --volume-id $NEW_VOL_ID --instance-id $(curl -s http://169.254.169.254/latest/meta-data/instance-id) --device /dev/xvdf
# 6. Formatear (ext4 en este ejemplo)
sudo mkfs.ext4 -F /dev/xvdf
# 7. Montar volúmenes
sudo mkdir -p /mnt/oldvol /mnt/newvol
sudo mount /dev/xvdb /mnt/oldvol # volumen original
sudo mount /dev/xvdf /mnt/newvol # nuevo volumen
# 8. Copiar datos con rsync (preserva atributos, ACL, xattrs)
sudo rsync -aHx --progress /mnt/oldvol/ /mnt/newvol/
# 9. Verificar tamaños
du -sh /mnt/oldvol /mnt/newvol
# 10. Actualizar fstab (usa UUID para mayor robustez)
OLD_UUID=$(blkid -s UUID -o value /dev/xvdb)
NEW_UUID=$(blkid -s UUID -o value /dev/xvdf)
sudo sed -i "s|$OLD_UUID|$NEW_UUID|g" /etc/fstab
# 11. Desmontar y limpiar
sudo umount /mnt/oldvol
sudo umount /mnt/newvol
sudo rmdir /mnt/oldvol /mnt/newvol
# 12. Detach y eliminar volumen antiguo (opcional)
aws ec2 detach-volume --volume-id vol-0abcd1234ef567890
aws ec2 delete-volume --volume-id vol-0abcd1234ef567890
# 13. Reiniciar stack
docker compose up -d
Verificación
- Comprobar montaje:
mount | grep $(basename $NEW_VOL_ID)debe mostrar el nuevo UUID en el punto de montaje esperado. - Espacio libre:
df -h /ruta/de/montajedebe reflejar el tamaño reducido (≈ 300 GB). - Integridad de la base de datos: para PostgreSQL, ejecutar
pg_ctl statusy luegoSELECT pg_database_size('nombre_db');para confirmar que los tamaños coinciden con lo esperado. - Logs de Docker:
docker logs <container>para asegurarse de que no hay errores de I/O. - Snapshot de respaldo: verifica que el snapshot creado en el paso 2 sigue disponible; en caso de fallo, puedes volver a crear el volumen a partir de él.
Notas adicionales
- Rsync vs. cp –
rsync -aHxconserva atributos extendidos y ACL, cruciales para bases de datos y sistemas de archivos con SELinux/AppArmor. - UUID vs. /dev/xvdf – cambiar
/etc/fstaba UUID evita problemas si AWS reasigna el nombre de dispositivo después de reinicios. - Tiempo de inactividad – la copia suele tardar tanto como el volumen original (≈ 150 GB → 5‑10 min en EBS gp3 con 125 MiB/s). Planifica una ventana de mantenimiento corta.
- Coste de snapshot – los snapshots son incrementales; el primer snapshot de un volumen de 250 GB cuesta pocos centavos al mes, una buena inversión para evitar migraciones riesgosas.
- Automatización – este proceso se puede encapsular en un script Bash o Ansible playbook para reutilizarlo en varios servidores.
- Evitar la expansión impulsiva – habilita rotación de logs (
logrotate) y alertas de CloudWatch para detectar uso > 80 % antes de que sea crítico.
Con este enfoque, puedes recuperar el espacio facturable en AWS sin una migración de fin de semana, manteniendo la integridad de tus datos y minimizando el downtime.