Cada punto de esta checklist termina en algo que puedes comprobar: una lista, el resultado de una consulta, un paso del runbook con un responsable. Los ejemplos asumen hosts Linux y PostgreSQL, y un destino con uno o varios proveedores cloud.
Fase 1: descubrimiento — lo que de verdad se ejecuta
Conexiones: quién habla con quién
Las conexiones establecidas con el proceso que las abre y, después, los puertos en escucha:
sudo ss -tnp state established
sudo ss -tlnpUna instantánea no ve el job que se conecta a las 03:00. Toma una muestra cada cinco minutos durante una semana que incluya un cierre de mes:
# /etc/cron.d/conn-sample (delete after discovery)
PATH=/usr/sbin:/usr/bin:/sbin:/bin
*/5 * * * * root ss -tnp state established >> /var/tmp/conn-$(hostname).logDespués cuenta los pares de proceso y peer:
grep -h 'users:' /var/tmp/conn-*.log |
awk -F'"' '{n = split($1, a, " "); sub(/:[0-9]+$/, "", a[n-1]); print $2, "<->", a[n-1]}' |
sort | uniq -c | sort -rn | head -50Cada peer que no sepas identificar es una dependencia sin documentar. El muestreo no ve las conexiones cortas, así que activa también log_connections en PostgreSQL durante esa semana.
Tareas programadas
# as root
for u in $(cut -d: -f1 /etc/passwd); do crontab -u "$u" -l 2>/dev/null | sed "s/^/$u: /"; done
cat /etc/crontab; ls /etc/cron.d /etc/cron.hourly /etc/cron.daily /etc/cron.weekly /etc/cron.monthly
systemctl list-timers --allCada job necesita un responsable y un destino; los que escriben en una base de datos siguen escribiendo en la copia antigua después del cutover si nadie los detiene.
Direcciones hardcodeadas, montajes, entradas de hosts
sudo grep -rIEno '([0-9]{1,3}[.]){3}[0-9]{1,3}' /etc /opt /srv 2>/dev/null |
grep -vE ':(127[.]0[.]0[.]1|0[.]0[.]0[.]0)$' | sort -u
findmnt -t nfs,nfs4,cifs
grep -v '^#' /etc/hostsAñade los directorios de tus aplicaciones y repite la búsqueda con tu nombre de dominio interno. Cada resultado es algo que cambia con la migración.
Registros DNS y sus TTL
dig +short NS example.com
dig +noall +answer app.example.com @ns1.example.com
dig +short TXT example.comConsulta al servidor autoritativo: un resolver recursivo muestra el tiempo que le queda en caché, no el TTL configurado. El TTL de cada registro que vaya a cambiar marca con cuánta antelación tienes que bajarlo. En la respuesta TXT, busca entradas SPF ip4: de los relays de correo del centro de datos.
Licencias ligadas al hardware
lscpu | grep -E '^ *(Socket|Core|Thread)'
sudo dmidecode -s system-uuid
ip -br linkEl software licenciado por socket o por core, o vinculado a una dirección MAC o a un host ID, no pasa tal cual a una VM nueva. Lee esas condiciones antes de elegir estrategia.
Volumen de datos y crecimiento
df -hT -x tmpfs -x devtmpfsSELECT datname, pg_size_pretty(pg_database_size(datname)) AS size
FROM pg_database ORDER BY pg_database_size(datname) DESC;Compara con hace tres meses para ver el crecimiento y haz la cuenta de la transferencia. Supón 2 TB por un enlace de 1 Gbit/s del que puedes usar la mitad: 16.000 gigabits a 0,5 Gbit/s son 32.000 segundos, casi nueve horas antes de que empiece ninguna restauración. Ese número decide la opción de migración de datos de la fase 4.
Resultado: una línea por carga de trabajo: responsable, hosts, peers, jobs, nombres DNS, volumen de datos, licencias.
Fase 2: decide por carga de trabajo
Asigna a cada carga de trabajo un único destino:
- Retire: sin responsable y sin usuarios reales en una semana de registros de conexiones. Apágala antes de migrar.
- Retain: latencia hacia equipos en las instalaciones, una licencia que prohíbe la nube o un fin de vida poco después de la migración.
- Rehost: la opción por defecto con una fecha de salida fija. Mismo sistema operativo y configuración, VM nueva.
- Replatform: cambia el componente que más tiempo de operación te cuesta por uno gestionado, como PostgreSQL gestionado, y no toques nada más.
- Refactor: solo cuando la carga de trabajo va a cambiar de todos modos, nunca como condición previa para salir.
- Repurchase: funciones commodity como el correo, el ticketing o una wiki. Comprueba antes la exportación de datos y dónde se guardan.
- Relocate: mueve la capa de virtualización entera a una versión alojada del mismo hipervisor cuando hay demasiadas VMs ante una fecha de salida fija. Compra tiempo, no ahorro.
En un destino multi-cloud, añade a cada línea el proveedor, la región y el motivo: un proveedor por carga de trabajo, con una razón explícita. Nuestra guía de estrategia multi-cloud analiza si un segundo proveedor compensa. Las cargas que más hablan entre sí en el registro de la fase 1 comparten oleada y proveedor.
Una evaluación gratuita de migración a la nube empieza justo aquí: hacemos el inventario de conexiones y de cron en tus hosts, asignamos a cada carga de trabajo un destino y una oleada, y dimensionamos la migración de datos frente a tu enlace. Nuestro servicio de migración a la nube se encarga del resto.
Fase 3: la landing zone, antes de la primera carga de trabajo
Nada se migra hasta que esto exista en código. En un destino multi-cloud, constrúyela por proveedor, con un único proveedor de identidad y un único conjunto de claves de etiquetas.
- Cuentas o proyectos: uno por entorno, más uno para los logs de auditoría y otro para la red compartida. Nada se ejecuta en la cuenta de gestión.
- Red: rangos de direcciones que no se solapen con el centro de datos, una VPN site-to-site de vuelta a él, un enlace privado dedicado solo si el tráfico de la fase 1 lo justifica, y reenvío DNS en ambos sentidos.
- Identidad: federación desde tu proveedor de identidad por SAML u OIDC, MFA, ninguna clave de larga duración para personas, roles para los servicios y una cuenta break-glass que genera una alerta cada vez que se usa.
- Infraestructura como código: todo en Terraform desde el primer día, estado remoto con bloqueo, estado separado por entorno y por proveedor, cambios mediante merge requests revisadas. Cuando falta esta capa, la consultoría de Terraform es por donde empezamos.
- Guardrails: políticas a nivel de organización que limitan las regiones a la UE (residencia de datos según el RGPD, impuesta y no solo prometida), deniegan los buckets públicos, exigen cifrado en reposo y protegen el registro de auditoría.
- Logging: logs de auditoría de todas las cuentas en la cuenta de logs, con una retención explícita; métricas y logs (Prometheus, OpenTelemetry) funcionando antes de la primera carga de trabajo, como línea base.
- Etiquetas de coste:
owner,cost-center,environment,app,migration-wave, impuestas por política. En minúsculas y con guiones: al menos un gran proveedor solo acepta claves de etiqueta en minúsculas.
La comprobación de solapamientos, incluidos los pools de clientes VPN y las redes de partners:
import ipaddress
on_prem = ["10.0.0.0/16", "172.16.8.0/22", "192.168.50.0/24"]
cloud = ["10.0.128.0/17", "10.40.0.0/16"]
for a in on_prem:
for b in cloud:
if ipaddress.ip_network(a).overlaps(ipaddress.ip_network(b)):
print("overlap:", a, b)La landing zone está terminada cuando una merge request crea una cuenta con red, identidad, guardrails y logging, y nadie abre una consola.
Fase 4: mueve los datos
Dump y restore
El tiempo de inactividad es dump, transferencia, restore y verificación. Ensáyalo sobre una copia y cronométralo; si cabe en la ventana con margen, no necesitas más.
pg_dump -Fd -j 4 -f /backup/appdb appdb
rsync -a /backup/appdb/ migrate@TARGET_HOST:/restore/appdb/
pg_restore -h NEW_DB_HOST -U app -d appdb -j 4 --no-owner --no-privileges /restore/appdbLas bases de datos gestionadas no suelen darte superusuario; de ahí los flags de propiedad.
Replicación y un cutover corto
Cuando la copia dura más que la ventana, replica con antelación y haz el cutover en minutos. La replicación lógica de PostgreSQL funciona entre versiones mayores:
-- Source: needs wal_level = logical (changing it requires a restart)
CREATE PUBLICATION migration FOR ALL TABLES;
-- Target: load the schema first with pg_dump --schema-only, then
CREATE SUBSCRIPTION migration
CONNECTION 'host=10.0.2.7 dbname=appdb user=replicator password=REPLACE_ME'
PUBLICATION migration;Los cambios de esquema no se replican, así que la congelación tiene que cubrir el DDL. Los valores de las secuencias se fijan en el cutover (paso 5 del runbook). Y en cuanto se publica una tabla sin clave primaria ni replica identity, los UPDATE y DELETE sobre ella fallan en el origen, y eso rompe producción. Localiza antes esas tablas:
SELECT n.nspname, c.relname FROM pg_class c
JOIN pg_namespace n ON n.oid = c.relnamespace
WHERE c.relkind = 'r' AND n.nspname NOT IN ('pg_catalog', 'information_schema')
AND NOT EXISTS (SELECT 1 FROM pg_constraint p WHERE p.conrelid = c.oid AND p.contype = 'p');Los archivos van en dos pasadas: una copia masiva días antes y un delta final durante la congelación.
rsync -aHAX --numeric-ids /srv/data/ migrate@TARGET_HOST:/srv/data/
rsync -aHAX --numeric-ids --delete /srv/data/ migrate@TARGET_HOST:/srv/data/Verifica la copia
Cuenta las filas de todas las tablas en ambos lados. En las tablas que contienen dinero o identidades, compara un hash:
SET TimeZone = 'UTC';
SELECT count(*), md5(string_agg(o::text, '|' ORDER BY o.id)) FROM orders o;Si las dos sesiones no usan la misma zona horaria, las marcas de tiempo se imprimen distinto y los hashes nunca coinciden. Las tablas grandes, por rangos de id. Para los archivos, un dry run con checksums lista todas las diferencias:
rsync -aHAXnci --delete /srv/data/ migrate@TARGET_HOST:/srv/data/Cuánto tiempo sigue abierta la vía de rollback
Mientras no se reabran las escrituras en el lado nuevo, el rollback es un cambio de DNS sin pérdida de datos. A partir de ahí, el lado antiguo se queda atrás. O configuras replicación inversa en el cutover (eliminas la suscripción de ida, publicas desde la base de datos nueva y suscribes la antigua con copy_data = false), o acuerdas por escrito que un rollback tardío implica volver a introducir datos a mano. Cierra la ventana con un evento de negocio, como que la primera facturación de fin de mes salga bien en el lado nuevo.
Fase 5: el runbook de cutover
Antes del día
- T-14 días: ensayo completo sobre un clon, rollback incluido, con cada paso cronometrado.
- Un TTL antiguo más margen antes: baja a 300 segundos todos los registros que cambian y confírmalo con
digcontra el servidor autoritativo. Un registro a 86400 necesita al menos un día; una semana es más seguro. - T-3 días: congelación de cambios en ambos lados: ni deploys, ni cambios de esquema ni de configuración.
- T-1 día: reunión de go/no-go. Ensayo superado, lag casi a cero, último backup probado con una restauración, rollback por escrito y una persona con nombre que decide el rollback.
El día, en orden
- 1Activa la página de mantenimiento y detén las escrituras de la aplicación.
- 2Detén cron y los timers en los hosts antiguos:
systemctl stop cron(ocrond, según la distribución) ysystemctl stoppara cada unidad.timerde tu inventario de la fase 1. - 3En el origen, espera a que el lag llegue a cero:
SELECT application_name, pg_wal_lsn_diff(pg_current_wal_lsn(), replay_lsn) FROM pg_stat_replication; - 4Ejecuta el delta final de rsync.
- 5Ajusta cada secuencia, por ejemplo
SELECT setval(pg_get_serial_sequence('orders', 'id'), (SELECT max(id) FROM orders));, y después ejecuta las consultas de verificación. - 6Haz los smoke tests del lado nuevo con su hostname real antes de tocar el DNS (comando abajo).
- 7Cambia el DNS o el load balancer y repite los smoke tests desde fuera.
- 8Decide go o no-go a la hora fijada de antemano: el go reabre las escrituras, el no-go ejecuta el rollback.
curl -sS -o /dev/null -w '%{http_code} %{time_total}' --resolve app.example.com:443:203.0.113.10 https://app.example.com/healthzCriterios de go/no-go
- Los recuentos y los hashes coinciden, y todos los smoke tests pasan.
- La tasa de errores y la latencia p95 están dentro del rango medido en el lado antiguo la semana anterior.
- Ningún error de replicación ni de aplicación desde el cambio.
Smoke tests
- Inicia sesión, lee un registro conocido, crea y borra un registro de prueba.
- Ejecuta un job en segundo plano; envía un correo y confirma que pasa SPF.
- Una llamada por integración externa: sandbox de pagos, SFTP de partners, webhooks.
El trigger de rollback
Una frase, acordada antes del día. Por ejemplo: "Si algún criterio de go/no-go falla y no está resuelto a las 02:00, o la tasa de 5xx se mantiene por encima de la línea base antigua durante 15 minutos en las primeras 24 horas, hacemos rollback. Decide el responsable de la migración; no se debate en la llamada."
Mantén el lado antiguo respondiendo o redirigiendo durante unos días; algunos resolvers y clientes guardan en caché más allá del TTL.
Fase 6: después de la migración
- Desmantela con método. En cada host antiguo,
sudo ss -tn state established '( sport = :5432 )'lista los clientes que siguen llegando a la base de datos antigua; cada uno es una dependencia que se escapó. Mantén el host apagado dos semanas antes de borrarlo, revoca sus credenciales, túneles, reglas de firewall y registros DNS, y cancela sus licencias. Un host olvidado deja de recibir parches pero sigue accesible, y sus cron jobs siguen ejecutándose. - Ajusta el tamaño con datos reales. Tras unas semanas que incluyan un cierre de mes, dimensiona al p95 observado más un margen, no a la asignación on-prem. Los descuentos por compromiso, después del rightsizing.
- Presupuestos y etiquetas. Alertas de presupuesto por cuenta y una lista semanal de recursos sin etiquetar.
- Prueba el backup y la recuperación ante desastres en el nuevo entorno. Un job de backup en verde no es una prueba. Restaura una base de datos en una instancia nueva y cronométralo, guarda copias en otra cuenta y otra región, y compara el tiempo de recuperación y la pérdida de datos medidos con lo que acordó el negocio. Nuestro trabajo de backup y recuperación ante desastres empieza por esa restauración.
Errores comunes y cómo detectarlos pronto
- Rangos de direcciones solapados: ejecuta la comprobación de solapamientos antes de que exista la primera red.
- El consumidor olvidado: una semana de muestreo con
ss, los logs de conexiones de la base de datos y la comprobación conssdespués del cutover. - Allowlists ligadas a las IP antiguas: firewalls de partners, proveedores de pago, peers SFTP, tu propio registro SPF. Haz la lista de tus IP de salida en la fase 1 y pregunta entonces a cada responsable de integración.
- Un par muy conversador partido por la VPN: la aplicación en la nube y la base de datos todavía on-prem. Multiplica el round trip por las consultas de cada página: supón 200 consultas con un round trip de 15 ms, lo que añade 3 segundos.
- MTU del túnel:
ping -M do -s 1472 CLOUD_HOSTdesde el centro de datos envía un paquete de 1500 bytes que no se puede fragmentar. Si falla, baja el tamaño hasta que pase y ajusta el MTU o el MSS clamping en consecuencia; si no, las respuestas grandes se cuelgan mientras las peticiones pequeñas funcionan. - Un rollback sin ensayar: si el ensayo se lo saltó, no tienes rollback.
Antes de la primera oleada, la evaluación gratuita de migración a la nube cubre lo que falla en silencio: el mapa de dependencias sacado de tus propios hosts, las comprobaciones de rangos de direcciones y de TTL, la cuenta de la migración de datos para tu base de datos más grande, y un borrador del runbook de la primera oleada con criterios de go/no-go y un trigger de rollback.