Mover una app de Rails fuera de Heroku
La razón por la que la mayoría de los equipos se quedan en una plataforma administrada más tiempo del que querían no es el precio. Es que la migración se ve como un proyecto sin un final claro. Este post es el camino más corto que conocemos, en el orden que mantiene la app corriendo en cada paso.
Paso 1: despliégala sin cambios, con buildpacks
No empieces escribiendo un Dockerfile. Empieza demostrando que la aplicación corre en algún otro lado, punto.
Los Cloud Native Buildpacks leen tu repositorio de la misma forma que lo hace el build de Heroku, usando capas de runtime preconstruidas en vez de compilar un lenguaje desde el código fuente. En la práctica, una app estándar de Rails o Django se construye sin cambiar un solo archivo.
Si arranca, ya demostraste la parte difícil: nada en tu aplicación dependía en realidad de la plataforma.
Paso 2: mueve los servicios de datos
Tu app casi seguro necesita Postgres y Redis. Aprovisiónalos como complementos, y Railyard inyecta los connection strings en el entorno de la misma forma a la que ya estás acostumbrado.
Dos cosas que revisar:
- Extensiones. Si usas
pgvector, PostGIS o similares, asegúrate de que el Postgres aprovisionado las tenga. El nuestro las tiene para pgvector. - El dump. Restaura un dump real de producción en la base de datos nueva antes del cutover, y mide cuánto tarda. Este es el paso que los equipos subestiman.
Paso 3: declara tus procesos
Si tu app tiene un Procfile, Railyard lo lee — proceso web, proceso worker, y el
hook release: que corre las migraciones antes de que nada reciba tráfico. Ese último punto importa
más de lo que parece: es lo que evita que una migración corra contra una versión del código que ya
empezó a recibir requests.
Paso 4: cambia a un Dockerfile, si quieres
Una vez que está corriendo, un Dockerfile te da reproducibilidad y builds en caliente más rápidos. No es obligatorio, y es mucho más fácil de escribir para una app que ya puedes desplegar que para una que no puedes.
Con un caché de capas respaldado por un registro, los rebuilds en caliente de una app grande de Rails aterrizan en alrededor de minuto y medio.
Qué es lo que de verdad se rompe
De hacer esto repetidamente, los problemas recurrentes son:
- Apps que traen su propia infraestructura. Algunas aplicaciones arrancan todo su entorno durante
assets:precompiley necesitan que exista un clúster de búsqueda. Eso falla en cualquier plataforma sin el complemento correspondiente — no es un problema de migración, es un requisito de la app que se te había olvidado. - Memoria de build en máquinas pequeñas. Un servidor de 2 GB compilando extensiones nativas puede quedarse sin memoria. O construye en un builder dedicado, o limita la memoria del build para que falle limpiamente en vez de llevarse la máquina con él.
- Disco efímero asumido. Cualquier cosa que escriba a disco local necesita un volumen persistente, o el siguiente despliegue lo descarta en silencio.
Qué obtienes al final
Tu app corre en servidores en una región que tú elegiste, al precio de tu proveedor sin sobreprecio, y si algún día dejas de pagar por el control plane, los contenedores siguen sirviendo tráfico. El flujo de trabajo se queda igual al que dejaste: push, mira el build, haz rollback si algo sale mal.