📋 Contexto
En 2020, AWS lanzó GP3 (General Purpose SSD), la nueva generación de volúmenes EBS que ofrece mejor rendimiento a menor costo que GP2. Sin embargo, muchas organizaciones siguen usando GP2 por inercia o desconocimiento de los beneficios.
Recientemente enfrenté el desafío de migrar decenas de volúmenes GP2 a GP3 de forma masiva, minimizando downtime y riesgos. Este post documenta el proceso y comparte el script de automatización.
🎯 Objetivo
Crear un script bash que permita:
- Identificar todos los volúmenes GP2 en una cuenta AWS
- Migrar volumes de forma segura y controlada
- Validar el estado antes y después de la migración
- Generar logs detallados de cada operación
- Manejar errores y permitir rollback si es necesario
- Minimizar impacto en servicios productivos
📊 GP2 vs GP3: Comparación Técnica
| Característica | GP2 | GP3 |
|---|---|---|
| Precio (por GB/mes) | $0.10 | $0.08 (-20%) |
| IOPS Base | 3 IOPS por GB (mín 100) | 3,000 IOPS (fijos) |
| IOPS Máximo | 16,000 IOPS | 16,000 IOPS (gratis) Hasta 64,000 (pagando extra) |
| Throughput | Hasta 250 MB/s | 125 MB/s base Hasta 1,000 MB/s (pagando extra) |
| Burst Performance | Sí (con créditos) | No necesario (IOPS constantes) |
🛠️ Implementación del Script
Estructura del Script
El script sigue esta lógica:
- Validar permisos y perfil AWS
- Listar todos los volúmenes GP2
- Para cada volumen:
- Verificar si está attached (en uso)
- Obtener configuración actual (size, IOPS, AZ)
- Calcular configuración GP3 equivalente
- Ejecutar modificación
- Validar resultado
- Log detallado
- Generar reporte final (CSV)
Script Base (Simplificado)
#!/bin/bash
# migrate_gp2_to_gp3.sh
# Script para migración masiva de volúmenes EBS GP2 a GP3
set -euo pipefail
# Configuración
AWS_PROFILE="${AWS_PROFILE:-default}"
AWS_REGION="${AWS_REGION:-us-east-1}"
LOG_FILE="migration_$(date +%Y%m%d_%H%M%S).log"
REPORT_FILE="migration_report_$(date +%Y%m%d_%H%M%S).csv"
# Colores para output
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
NC='\033[0m' # No Color
log() {
echo "[$(date '+%Y-%m-%d %H:%M:%S')] $*" | tee -a "$LOG_FILE"
}
log_success() {
echo -e "${GREEN}✓${NC} $*" | tee -a "$LOG_FILE"
}
log_error() {
echo -e "${RED}✗${NC} $*" | tee -a "$LOG_FILE"
}
log_warning() {
echo -e "${YELLOW}⚠${NC} $*" | tee -a "$LOG_FILE"
}
# Verificar AWS CLI y permisos
check_prerequisites() {
log "Verificando prerequisitos..."
if ! command -v aws &> /dev/null; then
log_error "AWS CLI no encontrado"
exit 1
fi
if ! aws sts get-caller-identity --profile "$AWS_PROFILE" &> /dev/null; then
log_error "No se puede autenticar con AWS"
exit 1
fi
log_success "Prerequisites OK"
}
# Listar volúmenes GP2
list_gp2_volumes() {
log "Listando volúmenes GP2 en $AWS_REGION..."
aws ec2 describe-volumes \
--profile "$AWS_PROFILE" \
--region "$AWS_REGION" \
--filters "Name=volume-type,Values=gp2" \
--query 'Volumes[*].[VolumeId,Size,State,Attachments[0].InstanceId,AvailabilityZone]' \
--output text
}
# Migrar un volumen específico
migrate_volume() {
local volume_id=$1
local current_size=$2
log "Migrando volumen $volume_id ($current_size GB)..."
# Calcular IOPS para GP3 (mínimo 3000, equivalente a GP2)
local gp2_iops=$((current_size * 3))
local gp3_iops=3000
if [ $gp2_iops -gt 3000 ]; then
gp3_iops=$gp2_iops
fi
# Limitar a 16000 IOPS (máximo gratuito de GP3)
if [ $gp3_iops -gt 16000 ]; then
gp3_iops=16000
fi
# Ejecutar modificación
if aws ec2 modify-volume \
--profile "$AWS_PROFILE" \
--region "$AWS_REGION" \
--volume-id "$volume_id" \
--volume-type gp3 \
--iops "$gp3_iops" \
--output json > /dev/null; then
log_success "Volumen $volume_id migrado (IOPS: $gp3_iops)"
echo "$volume_id,$current_size,SUCCESS,$gp3_iops" >> "$REPORT_FILE"
return 0
else
log_error "Error migrando volumen $volume_id"
echo "$volume_id,$current_size,FAILED,N/A" >> "$REPORT_FILE"
return 1
fi
}
# Verificar estado de modificación
check_modification_status() {
local volume_id=$1
aws ec2 describe-volumes-modifications \
--profile "$AWS_PROFILE" \
--region "$AWS_REGION" \
--volume-ids "$volume_id" \
--query 'VolumesModifications[0].ModificationState' \
--output text
}
# Main
main() {
log "═══════════════════════════════════════"
log " Migración GP2 → GP3"
log " Región: $AWS_REGION"
log " Perfil: $AWS_PROFILE"
log "═══════════════════════════════════════"
check_prerequisites
# Crear header del reporte
echo "VolumeID,Size(GB),Status,GP3_IOPS" > "$REPORT_FILE"
# Listar y procesar volúmenes
local count=0
local success=0
local failed=0
while IFS=$'\t' read -r vol_id size state instance_id az; do
count=$((count + 1))
log ""
log "[$count] Volumen: $vol_id"
log " Tamaño: ${size}GB"
log " Estado: $state"
log " Instancia: ${instance_id:-N/A}"
log " AZ: $az"
# Advertencia si está attached
if [ -n "$instance_id" ]; then
log_warning "Volumen attached a instancia. La migración es online."
fi
# Confirmar antes de migrar
read -p "¿Migrar este volumen? (y/n): " -n 1 -r
echo
if [[ $REPLY =~ ^[Yy]$ ]]; then
if migrate_volume "$vol_id" "$size"; then
success=$((success + 1))
else
failed=$((failed + 1))
fi
else
log "Volumen $vol_id omitido"
echo "$vol_id,$size,SKIPPED,N/A" >> "$REPORT_FILE"
fi
done < <(list_gp2_volumes)
# Resumen final
log ""
log "═══════════════════════════════════════"
log " RESUMEN DE MIGRACIÓN"
log "═══════════════════════════════════════"
log "Total procesados: $count"
log_success "Exitosos: $success"
if [ $failed -gt 0 ]; then
log_error "Fallidos: $failed"
fi
log ""
log "Reporte guardado en: $REPORT_FILE"
log "Log completo en: $LOG_FILE"
}
main
🚀 Uso del Script
Prerequisitos
# Instalar AWS CLI
curl "https://awscli.amazonaws.com/awscli-exe-linux-x86_64.zip" -o "awscliv2.zip"
unzip awscliv2.zip
sudo ./aws/install
# Configurar credenciales
aws configure
# O usar AWS SSO
aws sso login --profile my-profile
Ejecución
# Hacer ejecutable
chmod +x migrate_gp2_to_gp3.sh
# Ejecutar en región específica
AWS_REGION=us-east-1 ./migrate_gp2_to_gp3.sh
# Con perfil específico
AWS_PROFILE=production AWS_REGION=eu-west-1 ./migrate_gp2_to_gp3.sh
# Modo dry-run (solo listar, sin modificar)
# Comentar la línea de migrate_volume en el script
🐛 Troubleshooting
Problema 1: "VolumeInModification" Error
Causa: El volumen ya tiene una modificación en progreso.
Solución: Esperar a que termine la modificación anterior (puede tomar horas).
# Ver modificaciones en progreso
aws ec2 describe-volumes-modifications \
--volume-ids vol-xxxxx \
--query 'VolumesModifications[0].[ModificationState,Progress]'
Problema 2: "InvalidParameterValue" con IOPS
Causa: IOPS solicitados exceden el máximo para el tamaño del volumen.
Solución: GP3 permite hasta 16,000 IOPS gratis. Para más, se paga extra.
Problema 3: Downtime Durante Migración
Realidad: La migración GP2 → GP3 es online. No hay downtime.
Nota: Puede haber ligera degradación de performance durante la migración (1-2 horas típicamente).
📊 Resultados Reales
En mi caso de uso, migré ~30 volúmenes con los siguientes resultados:
Tiempo total: ~2 horas (incluyendo validaciones)
Downtime: 0 minutos
Problemas: 0 (después de testing en staging)
Métricas de Performance
Monitoreando con CloudWatch después de la migración:
- ✅ Latencia: Sin cambios significativos
- ✅ IOPS: Mejora en volúmenes pequeños (ahora tienen 3000 IOPS mínimos)
- ✅ Throughput: Equivalente o mejor
- ✅ Burst performance: Ya no necesaria (GP3 tiene IOPS constantes)
✅ Mejores Prácticas
- Prueba primero en staging - Valida el script en entorno no productivo
- Migra fuera de horas pico - Aunque el downtime es cero, reduce riesgo
- Monitorea CloudWatch - Observa métricas antes y después
- Documenta todo - Guarda logs y reportes para auditoría
- Comunica al equipo - Avisa sobre la migración aunque sea transparente
- Snapshots previos - Crea snapshots antes de migrar (extra seguridad)
- Migración gradual - No migres todo a la vez, hazlo por lotes
💡 Lecciones Aprendidas
- GP3 no siempre es mejor - Para volúmenes muy grandes (>5TB) con pocos IOPS, GP2 podía ser más económico (esto cambió en 2023)
- Automatización es clave - Migrar manualmente 50+ volúmenes sería tedioso y propenso a errores
- Validación constante - Verificar el estado de cada modificación antes de proceder con la siguiente
- Logs detallados - Indispensables para troubleshooting y auditoría
- Comunicación - Aunque no haya downtime, el equipo debe estar informado
📚 Recursos Útiles
🚀 Próximos Pasos
Mejoras futuras para el script:
- Integración con Terraform/CloudFormation para IaC
- Rollback automático en caso de error
- Estimación de ahorro antes de ejecutar
- Notificaciones vía SNS/Slack
- Dashboard de progreso con Lambda + DynamoDB
💭 Conclusión
La migración de GP2 a GP3 es una de las optimizaciones de costo más fáciles y seguras que puedes hacer en AWS. Con un script bien diseñado, puedes automatizar el proceso y ahorrar significativamente sin afectar servicios productivos.
Si aún tienes volúmenes GP2, considera migrarlos. El beneficio es claro: menos costo, igual o mejor performance.
En el próximo post, profundizaré en el uso avanzado de JMESPath para queries complejas en AWS CLI, una habilidad esencial para cualquier DevOps Engineer.