La actualización tomó 1 hora. La preparación tomó 2 meses. Lecciones aprendidas de una migración.
Una migración Open edX de nueve releases mayores: MongoDB de 500 GB, migraciones de datos comprimidas, videos en GridFS, charsets, Meilisearch y una ventana de corte de una hora.
Open edX es un buen ejemplo de un sistema de complejidad media a alta: front end, back end, base de datos relacional, base de datos no relacional, base de datos de búsqueda, base de datos en memoria, plataforma en vivo con miles de usuarios y gigabytes de contenido. Esta es la historia y las lecciones aprendidas de cómo migramos a nuestro servicio a uno de nuestros últimos clientes, que estaba nueve releases atrás, y lo actualizamos a la última versión.
Contexto
Nuestro cliente tenía una instalación local y autogestionada de Open edX, en un único servidor virtual en AWS. Estaban en Lilac, es decir, nueve releases mayores por detrás de Ulmo, la última en ese momento. Querían pasar a la última versión para resolver algunos problemas técnicos, y los convencimos de migrar también a nuestra oferta SaaS, para que no tuvieran que preocuparse más por las actualizaciones.
Versiones de Open edX
Open edX nombra sus releases mayores con nombres de árboles en orden alfabético. Lilac es el 12.º release mayor y, al momento de la migración, Ulmo era el más reciente, el 21.º. Lilac se lanzó a mediados de 2021, y Ulmo a fines de 2025. Desde entonces, Open edX publica dos versiones mayores por año, y tres o cuatro actualizaciones menores entre medio. Mantenerse al día es importante por muchas razones:
- Actualizaciones de seguridad: las versiones antiguas pueden tener vulnerabilidades conocidas que los hackers pueden usar para vulnerar tu sitio.
- Fin de soporte: no solo la plataforma principal da soporte únicamente a la última versión, sino que sus dependencias también.
- Obsolescencia de la infraestructura: las bases de datos también evolucionan con el tiempo. Las versiones antiguas pueden dejar de estar disponibles, o los proveedores de nube pueden cobrar costos adicionales por usar versiones obsoletas.
- Corrección de errores: los bugs se corrigen en las versiones nuevas; no vas a tener tu código parcheado sin una actualización
- Nuevas funcionalidades: por último, pero no menos importante, las versiones más nuevas incorporan funcionalidades que te van a encantar.
Por qué actualizar es difícil
Hemos visto muchos sitios que quedaron estancados en versiones antiguas. Así que quizás pienses “¿por qué no simplemente actualizan?”. Hay muchas razones que hacen que actualizar tu sistema a la última versión sea difícil.
- Miedo a arruinar un entorno de producción. Especialmente cuando autogestionas un despliegue, tocar el núcleo de tu sistema de producción puede ser riesgoso. El miedo a dejar un servicio inutilizable con cientos de clientes quejándose puede desalentar a cualquier operador de sistemas a la hora de hacer cambios grandes.
- Complejidad de la actualización. Migrar un sitio en producción requiere planificación. Respaldar todos los datos, preparar un script de migración, tener un plan de rollback y definir una ventana de servicio son solo algunas de las actividades obligatorias que deberías considerar antes de migrar. Y durante la migración pasan cosas malas. Si no tienes experiencia migrando sitios, puedes sentirte abrumado por las implicancias.
- Actualizaciones de infraestructura. Normalmente los releases mayores requieren actualizar componentes de infraestructura, especialmente bases de datos. Esta puede ser una parte delicada y debe hacerse en coordinación con la actualización del sistema.
- Integraciones. Si tu sistema está conectado a otros, como un CRM, una pasarela de pagos, un ERP, etc., las APIs pueden cambiar. Tienes que considerar cómo adaptar las integraciones a las nuevas versiones.
- Personalizaciones de código. Especialmente al adoptar software open source como este, es tentador personalizarlo modificando el código base. Cuando actualizas desde los repositorios oficiales, todos tus cambios se van a perder a menos que los rebasees sobre el nuevo código. A veces es una tarea simple, en general es una tarea difícil (porque requiere retrabajo y nuevas pruebas), y en muchos casos es imposible sin reescribir toda la personalización.
- Migraciones de datos. Las definiciones de datos cambian de versión a versión. Cuando tienes datos en producción no puedes simplemente crear la base de datos nueva: tienes que mover tus datos a través de esos cambios. Los frameworks de programación avanzados (como Django en Open edX) lo gestionan con un proceso llamado migrations. Por la naturaleza de este proceso, y por el hecho de que en cada release mayor las migraciones de datos se comprimen (squash), no puedes saltear actualizaciones de versiones mayores. Esto significa que si estás nueve versiones atrás, tienes que hacer nueve actualizaciones para llegar a la última.
Autogestionado vs. SaaS
Cuando autogestionas tu sistema, tienes que ocuparte de todo. Mira mi artículo sobre cómo elegir entre self-hosting y SaaS (https://medium.com/@andres_81160/how-to-choose-between-saas-and-self-hosted-lms-078c2798ab31 ).
Cómo planifico las actualizaciones
Creo que una buena planificación es la clave de una migración exitosa. Por eso prefiero dedicar todo el tiempo que sea necesario a planificar cuidadosamente cómo será la migración, en lugar de fallar en las ventanas de migración o tener el sitio caído durante días.
Este caso es especialmente completo, ya que incluyó tanto mover el sitio autogestionado a nuestro SaaS como, al mismo tiempo, actualizar nueve releases mayores. Así que dividamos el proceso.
Plan de migración
Pasar de autogestionado a SaaS significa cambiar todo. Planificar con cuidado es la clave de una migración exitosa. Estos son los pasos principales para lograrlo.
Fase de planificación
Sin detener el sitio, con suficiente antelación respecto de la fecha objetivo de migración, hacemos todos los pasos de preparación.
- Crear el sitio nuevo desde cero, en un dominio temporal, en la última versión
- Mover los datos no relacionales. Esto significa hacer un dump de la base de datos original, transferir los datos y luego cargarlos en la base de datos destino. Esta base de datos puede ser enorme.
- Mover los datos relacionales. Lo mismo, pero para la base de datos relacional.
- Mover los archivos multimedia (esto incluye imágenes de perfil, paquetes SCORM y entregas de tareas de los estudiantes)
- Ejecutar las migraciones de datos
- Adaptar las personalizaciones y el tema
- Probar el sitio
Cada uno de los pasos anteriores expone desafíos. Por eso tomo nota de todos los problemas y me aseguro de que estén todos resueltos antes de la fecha de corte. Aquí es importante que el cliente pruebe el sitio temporal en todas las funcionalidades que son críticas para su negocio.
Ejecutar este proceso también sirve para medir el tiempo que toma cada paso y planificar alternativas. Mover datos y ejecutar migraciones puede llevar tiempo, y hay que medirlo.
En la ventana de migración, solo hacemos esto:
- Detener el sitio original, para que no haya usuarios modificando las bases de datos hasta que terminemos el traslado. Aquí se espera algo de downtime porque estamos moviendo infraestructura. Una actualización simple sin mover infraestructura puede hacerse sin downtime. Si te preguntas si es posible hacer el traslado sin downtime, la respuesta es sí, pero es mucho más complejo y solo se justifica en sitios muy críticos, lo cual queda fuera del alcance de este artículo.
- Mover de nuevo los datos relacionales y no relacionales (ya que pueden haber cambiado desde la fase de planificación)
- Mover de nuevo los archivos multimedia si cambiaron
- Corregir los problemas de datos identificados en la fase de planificación
- Ejecutar todas las migraciones de datos intermedias (excepto la última)
- Inicializar la última versión (lo que implica la última migración de datos)
- Apuntar el DNS a la nueva infraestructura
Si la fase de planificación se siguió cuidadosamente, la migración no debería tomar más de una o dos horas. Por supuesto que pasan cosas, pero cuando los problemas críticos están gestionados, cualquier imprevisto puede manejarse fácilmente durante la ventana de migración.
Actualización
La actualización es parte del plan de migración. Pero hay muchas cosas para tener en cuenta.
Migraciones de datos
Los datos no relacionales no son un problema. Como este tipo de bases de datos no tienen un esquema fijo (definiciones de tablas, tipos de datos, etc.), MongoDB puede sobrevivir a las actualizaciones sin cambios estructurales. Así que no tienes que preocuparte por eso. El problema es MySQL.
El proceso de data migrations gestiona los cambios en la estructura de la base de datos relacional de manera que pueda hacerse con una base de datos en vivo sin perder los datos almacenados. Esto incluye agregar o eliminar tablas, agregar o eliminar columnas, o cambiar tipos de datos. Estas migraciones se guardan en archivos con instrucciones SQL complejas, que tardan mucho en ejecutarse. Se acumulan con el tiempo con cada release.
Como cada release mayor puede tener cientos de archivos de migración, después de unos pocos releases puede tomar demasiado tiempo ejecutarlos todos. A veces un release crea una tabla que va a ser eliminada unos releases después, así que muchos cambios son incluso innecesarios. Por eso los desarrolladores comprimen (squash) las migraciones en algunos releases mayores. Esto significa que cuando instalas Ulmo (el 21.º release) por primera vez, solo ejecutas las últimas migraciones de datos, y no todo el historial de migraciones desde el primer release.
Instalar desde cero está bien. Actualizar un release mayor está bien. Pero no puedes saltearte la actualización de un release, porque las migraciones comprimidas en el release del medio se van a perder y eso va a romper la secuencia de migración. Por eso tienes que migrar los releases mayores uno por uno.
El desafío es que tendrías que levantar versiones intermedias que nunca vas a usar después de la migración. Y hay algunas versiones antiguas que directamente son imposibles de levantar, sobre todo porque la imagen no compila ya que algunas dependencias pueden no estar disponibles.
Cómo lo resolví
No es necesario levantar un Open edX completo y funcional para ejecutar las migraciones. Por ejemplo, no necesitas los componentes de front end ni las tareas en segundo plano corriendo. Solo necesitas una imagen mínima para ejecutar el comando de migración de dos servicios: LMS y CMS. Así que creé una imagen Docker para cada release, desde Koa hasta la última, y resolví todos los problemas de dependencias para poder simplemente ejecutar el proceso de migración.
Así que el proceso es bastante simple:
- Cargar los datos en la base de datos (solo MySQL)
- Levantar el contenedor Docker de la versión siguiente (si el cliente estaba en Lilac, ejecuto Maple)
- Conectarlo a la base de datos
- Ejecutar el comando de migración para el LMS y el CMS
- Detener el contenedor
- Repetir con la versión siguiente
Versión de MySQL
En Palm, Open edX pasó de MySQL 5.7 a 8. Suena abrumador, pero no lo es. Si el sitio destino ya está en MySQL 8, el dump de 5.7 va a funcionar sin problemas.
Juegos de caracteres y collation de datos
Una pequeña advertencia: la collation y el charset de los datos. El juego de caracteres define cómo se representa internamente cada carácter dentro de la base de datos. En Redwood, Open edX pasó de utf8mb3 a utf8mb4. El “mb” significa multibyte, e indica cuántos bytes se usan para representar un carácter. “utf8mb4”, con cuatro bytes por carácter, permite caracteres especiales como emojis dentro de los campos de datos.
La collation es cómo se ordenan los caracteres al ordenar datos (por ejemplo, podríamos querer las letras ordenadas como a, A, b, B, c, C en lugar de a, b, c, A, B, C). No suena como algo importante, pero tiene que estar alineada con el juego de caracteres. Un juego de caracteres o una collation distintos en campos vinculados entre tablas pueden romper una migración de datos.
La solución es actualizar el charset y la collation como primer paso. Por suerte puedes migrar el charset hacia arriba (de mb3 a mb4) sin perder datos, así que es seguro y recomendable hacerlo primero.
No subestimes este paso. Actualizar el charset de todas las tablas puede ser la tarea que más tiempo consuma en el proceso de migración.
MongoDB: el elefante en el ascensor
En Open edX, MongoDB almacena las estructuras y el contenido de los cursos, y puede crecer mucho en sitios grandes.
Cuando empecé a planificar la migración y le pedí al cliente que me enviara el dump de sus datos, tardaron días en hacerlo. Cuando recibí el dump, entendí por qué: la MongoDB pesaba más de 500 GB. Sí, más de medio terabyte.
Bien, esto puede ser normal si hay muchos cursos, y había más de mil. Pero una vez que tuve los datos, revisé por qué era tan grande, y la respuesta llegó de inmediato: videos.
Lo que no te cuentan sobre el almacenamiento de archivos
Cada curso en Open edX tiene una sección llamada File uploads. Básicamente puedes subir cualquier archivo y te da un link que puedes usar dentro de la estructura del curso para lo que quieras.
Cualquier operador de un sitio asumiría que estos archivos van a un almacenamiento típico en un sistema de archivos o algo similar. No es así. Se guardan en MongoDB GridFS como datos binarios, en una colección llamada fs.chunks.
Este enfoque tiene el beneficio de que los archivos del curso se mueven junto con el curso cuando exportas e importas, o cuando haces un re-run de ediciones del curso. Pero tiene algunas desventajas.
- Cuando usas el link absoluto al archivo, va a apuntar a la versión original del curso, no a la importada ni a la del re-run. Si cambias el archivo en el curso original, puedes perder el acceso en el nuevo.
- El archivo se duplica en cada importación o re-run.
- Ver o descargar el archivo implica cargar la base de datos.
- Puede hacer que la MongoDB sea enorme, lo que impacta en los backups y en los movimientos de datos.
Por eso recomiendo usar file uploads para archivos pequeños, o imágenes para incluir como parte del contenido. Para archivos grandes o contenido reutilizable, es mejor tener un almacenamiento dedicado fuera de la plataforma e insertar los links.
Y definitivamente no para videos. Los videos son archivos grandes y requieren alto throughput. MongoDB no está diseñada para servir videos.
Entonces, ¿dónde alojar los videos de los cursos?
Es sorprendente que, siendo el video un componente central de los MOOCs, no haya una guía definitiva para almacenar y gestionar videos. Hay dos estrategias principales para almacenar y servir videos: como objeto de archivo, o streaming. Cuando almacenas los videos como archivo, puedes guardarlos en un almacenamiento común como S3 y opcionalmente poner un CDN adelante. El streaming es mucho mejor, ya que puede ofrecer funcionalidades dedicadas como la calidad adaptativa.
El video merece un análisis dedicado. Así que, para resumir:
- Nunca uses la funcionalidad de archivos de Open edX para almacenar videos
- Usa un servicio de video como YouTube, Vimeo o VdoCipher para transmitir los videos
- O guárdalos en S3 o un almacenamiento similar con un CDN opcional adelante.
Cómo lo resolvimos
Esto es lo que ocupó la mayor parte de los dos meses de preparación. Hicimos un trabajo conjunto en el que el cliente creó un script para extraer los links de video de todos los cursos, eliminó duplicados y subió todos los videos a la plataforma de streaming. Cuando terminaron, me dieron la lista de videos con la nueva URL.
Después creé un script para modificar la URL del video en cada curso y eliminar el video de la MongoDB.
Aun así tuvimos que hacer mucho trabajo manual. Había muchos videos duplicados, bloques huérfanos y archivos sin uso en muchos cursos. Todos tuvieron que resolverse manualmente.
Después de todo este trabajo, no solo la base de datos es mucho más chica, sino que además los videos se ven mucho mejor.
Foro
En el release Sumac, la aplicación de Foro fue (afortunadamente) completamente refactorizada de Ruby + MongoDB a Python + MySQL.
Existe un proceso para migrar los datos de MongoDB a MySQL. El desafío es que después de Sumac, las configuraciones para conectar ese proceso con los datos antiguos ya no están presentes y tienen que parchearse manualmente.
Como bonus, aquí va un pequeño plugin que te permite ejecutar el comando de migración después de Sumac:
from tutor import hooks as tutor_hooks
tutor_hooks.Filters.ENV_PATCHES.add_item(
(
"openedx-common-settings", """
FORUM_MONGODB_DATABASE = 'cs_comments_services'
"""
)
)
Meilisearch
A partir del release Teak, Open edX reemplazó ElasticSearch por Meilisearch. La migración es bastante directa y la gestiona el paso de inicialización.
El desafío que enfrentamos fue que nuestro servidor de Meilisearch era chico. Era suficientemente grande para producción, pero se saturó cuando tuvo que reindexar más de mil cursos. Como el proceso de reindexado es parte del pipeline de inicialización, se quedó trabado. Para sortear esto en la ventana de mantenimiento, tuve que inicializar los plugins manualmente y después retomar el reindexado de los cursos en una instancia de Meilisearch más grande.
Integración de e-commerce
El cliente tiene un sitio de e-commerce que usaba código personalizado dentro de Open edX para obtener información adicional del usuario y procesar la orden. Revisamos el proceso y los ayudamos a refactorizar la integración para usar endpoints estándar y obtener la información que necesitaban sin personalizar el código base de Open edX.
Certificados PDF
Sorprendentemente, el código de generación de certificados PDF fue completamente descontinuado en 2021, a pesar de la preferencia de muchas instituciones por emitir credenciales en este formato.
Nuestro cliente había desarrollado una generación personalizada de certificados PDF, que dependía de un componente personalizado dentro de Open edX para disparar la emisión del certificado.
Recomendamos usar webfilters en su lugar, y pudieron refactorizar el microservicio que tenían para conservar la funcionalidad.
Los webfilters pueden habilitarse usando nuestro plugin tutor-contrib-webhooks, que es abierto y de uso gratuito. De esta manera eliminamos la última personalización de código de la plataforma.
Tema
Gracias a nuestra aplicación Branding (https://branding.aulasneo.com ), el cliente puede aplicar sus propios estilos (logos, tipografías, CSS personalizado) por su cuenta, sin tener que pedir soporte. Los cambios pueden aplicarse de inmediato sin necesidad de reconstruir imágenes Docker ni reiniciar el servicio.
Branding es de uso gratuito en cualquier sitio Open edX, autogestionado o alojado por cualquier proveedor.
Como pequeño bonus, si quieres cambiar la leyenda de la pantalla de login, aquí va un plugin de tutor que lo logra fácilmente (vas a necesitar reconstruir la imagen del MFE después de habilitarlo):
version: 1.0.0
name: change_authn_legend
patches:
mfe-dockerfile-pre-npm-build-authn: |
RUN sed -i \
-e "s/defaultMessage: 'Start learning'/defaultMessage: '<nueva primera línea>'/" \
-e "s/defaultMessage: 'with {siteName}'/defaultMessage: '<nueva segunda línea>'/" \
src/base-container/components/default-layout/messages.js
Otro consejo: si quieres ocultar el link de “Ayuda” en el dashboard del estudiante, agrega este plugin que aprovecha los slots de los MFE:
version: 1.0.0
name: hide_learning_help
patches:
mfe-env-config-runtime-definitions: |
addPlugins(config, 'org.openedx.frontend.layout.header_learning_help.v1', [{
op: PLUGIN_OPERATIONS.Hide,
widgetId: 'default_contents',
}]);
SCORM
SCORM es una tecnología antigua, pero aún ampliamente usada en entornos de aprendizaje. Depende fuertemente de JavaScript y de iframes, lo que presenta muchos desafíos, especialmente cuando se implementa en infraestructuras escalables con almacenamiento de objetos dedicado.
Gracias a nuestro plugin de Tutor para SCORM en S3, que es abierto y de uso gratuito, esto no es un problema. Incluso resolvieron un problema de larga data que tenían con algunos módulos SCORM que no funcionaban en el setup viejo y ahora sí funcionan en el nuevo.
Conclusión
Gracias a la planificación cuidadosa y a la experiencia migrando y dando soporte a Open edX, nuestro cliente pudo migrar y actualizar a la última versión con un downtime mínimo. El nuevo despliegue ofrece una serie de beneficios.
Ahora tienen un Open edX totalmente gestionado como servicio, con soporte experto dedicado. La plataforma está siempre en la última versión estable, con los parches de seguridad aplicados, las nuevas funcionalidades habilitadas y componentes soportados. Ahora los videos no sobrecargan la base de datos y ofrecen streaming adaptativo sin demoras ni fallas. El código base se mantiene como el estándar oficial: sin código personalizado que haya que rebasear en futuras actualizaciones. Optimizamos el setup en base a las mejores prácticas de nuestros diez años de experiencia. El despliegue está sobre una infraestructura robusta, resiliente y escalable. En lugar de un sitio monolítico, la aplicación corre en Kubernetes y está conectada a bases de datos redundantes. Todos los costos de infraestructura están incluidos en la tarifa del servicio.