Cómo armar un script de despliegue continuo (CI/CD) para servidor de MU Online
Construye un pipeline de despliegue continuo para tu servidor de MU Online, automatizando el build del emulador, la migración de base de datos, la distribución de archivos de cliente y el reinicio seguro del GameServer/ConnectServer.
Mantener un servidor de MU Online privado actualizado manualmente —copiando DLLs, editando configuraciones y reiniciando servicios a mano— funciona mientras el proyecto es pequeño, pero se convierte en fuente de error humano en cuanto el equipo crece o la frecuencia de actualizaciones aumenta. Un sc
Mantener un servidor de MU Online privado actualizado manualmente —copiando DLLs, editando configuraciones y reiniciando servicios a mano— funciona mientras el proyecto es pequeño, pero se convierte en fuente de error humano en cuanto el equipo crece o la frecuencia de actualizaciones aumenta. Un script de despliegue continuo (CI/CD) resuelve esto: cada cambio en el código del emulador, en los archivos de configuración o en la base de datos pasa por un pipeline repetible, probado y reversible. Este tutorial muestra cómo estructurar ese pipeline desde cero, desde los requisitos de infraestructura hasta el script de rollback, cubriendo GameServer, ConnectServer, base de datos y archivos de configuración.
Por qué automatizar el despliegue de un servidor de MU
Un servidor de MU típico tiene múltiples componentes que deben actualizarse en conjunto: el binario del GameServer (compilado a partir del código del emulador, ej. IGCN/MuEMU/OpenMU), el ConnectServer, los archivos .txt/.xml de configuración de ítems y monstruos, y el esquema de la base de datos (MSSQL o MySQL, según el emulador). Actualizar estos componentes fuera de orden —por ejemplo, subir un ItemList.txt nuevo sin el stored procedure correspondiente— es la causa más común de caídas tras una actualización. Un pipeline de despliegue continuo garantiza que todo suba en el orden correcto, con validación en cada etapa.
Requisitos previos de infraestructura
- Repositorio de código versionado (Git) con el código fuente del emulador y los archivos de configuración.
- Un ambiente de staging —réplica del servidor de producción, aunque más pequeña, para validar builds antes de liberarlas.
- Acceso vía SSH/WinRM al/los servidor(es) de producción, con una cuenta de servicio dedicada (no la cuenta personal del administrador).
- Un runner de CI (GitHub Actions, GitLab CI, Jenkins o incluso un script programado vía Task Scheduler/cron) que ejecute el pipeline.
- Backup automatizado de la base de datos y de los archivos de configuración antes de cualquier despliegue.
Estructura del repositorio
Organiza el repositorio separando claramente lo que es código fuente compilable de lo que es configuración de datos de juego:
mu-server/
├── src/ # código fuente del emulador (GameServer, ConnectServer)
├── config/ # archivos .ini/.txt de configuración de ambiente
├── db/
│ ├── migrations/ # scripts .sql numerados secuencialmente
│ └── seed/ # datos iniciales (ítems, monstruos, mapas)
├── deploy/
│ ├── deploy.sh # script principal de despliegue
│ ├── rollback.sh # script de rollback
│ └── healthcheck.sh # validación post-despliegue
└── .github/workflows/deploy.yml
Esta separación permite versionar cambios de gameplay (un evento nuevo, un ítem nuevo) independientemente de cambios de infraestructura (una corrección de bug en el core del emulador).
Etapa 1 — Build del emulador
El pipeline comienza compilando el código fuente del GameServer/ConnectServer. Un ejemplo de etapa de build en bash, asumiendo un proyecto C++ con CMake:
#!/bin/bash
set -euo pipefail
echo "[deploy] Compilando GameServer..."
cd src/GameServer
cmake -B build -DCMAKE_BUILD_TYPE=Release
cmake --build build --config Release -j"$(nproc)"
if [ ! -f build/GameServer.exe ]; then
echo "[deploy] ERROR: el build falló, no se generó el binario."
exit 1
fi
echo "[deploy] Build completado con éxito."
Si tu emulador se distribuye como binario cerrado (sin recompilación), esta etapa se convierte solo en la validación de integridad del paquete descargado (checksum) antes de continuar.
Etapa 2 — Migración de base de datos
Todo cambio de esquema debe vivir en un archivo de migración numerado y versionado, nunca aplicado manualmente vía SSMS/HeidiSQL directo en producción. Un ejemplo de script de aplicación de migraciones incrementales:
#!/bin/bash
set -euo pipefail
DB_HOST="$1"
LAST_APPLIED=$(sqlcmd -S "$DB_HOST" -Q "SELECT MAX(version) FROM SchemaMigrations" -h -1)
for file in db/migrations/*.sql; do
version=$(basename "$file" | cut -d'_' -f1)
if [ "$version" -gt "$LAST_APPLIED" ]; then
echo "[deploy] Aplicando migración $file"
sqlcmd -S "$DB_HOST" -i "$file"
sqlcmd -S "$DB_HOST" -Q "INSERT INTO SchemaMigrations (version, applied_at) VALUES ($version, GETDATE())"
fi
done
Nunca uses DROP TABLE ni ALTER COLUMN destructivo en una migración sin antes confirmar que el backup previo al despliegue se completó con éxito.
Etapa 3 — Backup automático antes del despliegue
El paso de backup debe ser bloqueante: si falla, todo el pipeline se detiene y no se implanta nada. Un ejemplo mínimo:
#!/bin/bash
set -euo pipefail
TIMESTAMP=$(date +%Y%m%d_%H%M%S)
BACKUP_DIR="/backups/mu/$TIMESTAMP"
mkdir -p "$BACKUP_DIR"
sqlcmd -S "$DB_HOST" -Q "BACKUP DATABASE MuOnline TO DISK='$BACKUP_DIR/MuOnline.bak'"
cp -r /opt/mu-server/config "$BACKUP_DIR/config"
tar -czf "$BACKUP_DIR/binaries.tar.gz" /opt/mu-server/bin
echo "[deploy] Backup guardado en $BACKUP_DIR"
Mantén al menos los últimos 7 backups diarios y 4 semanales, con rotación automática para no llenar el disco.
Etapa 4 — Distribución de los archivos al servidor
Con la build validada y el backup completado, el pipeline copia los artefactos a el/los servidor(es) de producción. Usando rsync sobre SSH:
rsync -avz --delete \
--exclude 'config/local.ini' \
build/GameServer.exe deploy/ \
usuario@mu-prod:/opt/mu-server/bin/
El --exclude es importante: los archivos de configuración específicos del ambiente (contraseñas de base de datos, claves de licencia) no deben ser sobrescritos por el pipeline —viven fuera del control de versión, gestionados por variables de ambiente o un cofre de secretos (Vault, AWS Secrets Manager).
Etapa 5 — Reinicio seguro de los servicios
Reiniciar el GameServer desconecta a todos los jugadores conectados, así que esta etapa debe estar cronometrada y comunicada. Un script de reinicio controlado:
#!/bin/bash
set -euo pipefail
echo "[deploy] Avisando a los jugadores sobre el reinicio en 5 minutos..."
echo "notice El servidor se reiniciará en 5 minutos por mantenimiento" > /opt/mu-server/gs_console_pipe
sleep 300
systemctl stop mu-gameserver
systemctl stop mu-connectserver
cp /tmp/new_build/GameServer.exe /opt/mu-server/bin/
systemctl start mu-connectserver
systemctl start mu-gameserver
Siempre prefiere reiniciar el ConnectServer al final, para que solo acepte nuevas conexiones cuando el GameServer ya esté en pie.
Etapa 6 — Healthcheck post-despliegue
Después del reinicio, valida automáticamente que los servicios subieron correctamente antes de declarar el despliegue como concluido:
#!/bin/bash
RETRIES=10
for i in $(seq 1 $RETRIES); do
if nc -z localhost 44405; then
echo "[deploy] ConnectServer respondiendo en el puerto 44405."
exit 0
fi
echo "[deploy] Esperando a que el ConnectServer suba... ($i/$RETRIES)"
sleep 10
done
echo "[deploy] ERROR: el ConnectServer no respondió. Iniciando rollback."
exit 1
Si el healthcheck falla, el pipeline debe disparar automáticamente el script de rollback, sin esperar intervención manual.
Etapa 7 — Script de rollback
El rollback restaura el backup generado en la Etapa 3 y revierte los binarios:
#!/bin/bash
set -euo pipefail
BACKUP_DIR="$1"
systemctl stop mu-gameserver mu-connectserver
sqlcmd -S "$DB_HOST" -Q "RESTORE DATABASE MuOnline FROM DISK='$BACKUP_DIR/MuOnline.bak' WITH REPLACE"
tar -xzf "$BACKUP_DIR/binaries.tar.gz" -C /
systemctl start mu-connectserver mu-gameserver
echo "[deploy] Rollback completado a partir de $BACKUP_DIR"
Prueba este script periódicamente en staging —un rollback nunca probado tiene una gran probabilidad de fallar justo en el momento en que más lo necesitas.
Integrando con GitHub Actions
Un ejemplo de workflow que orquesta las etapas anteriores, disparado en push a la rama main tras la aprobación de un pull request:
name: Deploy MU Server
on:
push:
branches: [main]
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Build
run: ./deploy/build.sh
- name: Backup de producción
run: ./deploy/backup.sh
- name: Migrar base de datos
run: ./deploy/migrate.sh ${{ secrets.DB_HOST }}
- name: Distribuir archivos
run: ./deploy/rsync-deploy.sh
- name: Reiniciar servicios
run: ssh deploy@mu-prod './deploy/restart.sh'
- name: Healthcheck
run: ./deploy/healthcheck.sh || ./deploy/rollback.sh
Mantén los secretos (host de la base de datos, claves SSH) en Secrets del repositorio, nunca hardcodeados en el YAML.
Ambiente de staging como puerta obligatoria
Ninguna build debe ir directo a producción. Configura el pipeline para primero implantar en staging, correr un smoke test automatizado (login, creación de personaje, teletransporte, drop de ítem) y solo entonces liberar el despliegue a producción —manual o automáticamente, según la madurez del equipo. Esta puerta es lo que separa el despliegue continuo del "despliegue a ciegas".
Errores comunes y soluciones
| Síntoma | Causa probable | Solución |
|---|---|---|
| El GameServer no arranca tras el despliegue | DLL/binario incompatible con el esquema de base de datos actual | Corre las migraciones antes de cambiar el binario, nunca después |
| El rollback falla | Script de rollback nunca probado en staging | Prueba el rollback en cada release mayor, no solo lo documentes |
| Los jugadores se desconectan sin aviso | Reinicio no anunciado con anticipación | Agrega un mensaje in-game/Discord automatizado antes del reinicio |
| El pipeline se traba en el backup | Disco lleno en el servidor de base de datos | Configura rotación de backups antiguos y monitorea el espacio libre |
| Configuración local sobrescrita | rsync sin exclude de archivos de ambiente | Lista explícitamente los archivos de configuración que no vienen del repositorio |
Lista de verificación de implementación del pipeline
- Repositorio versionado con separación entre código, configuración y migraciones de base de datos.
- Ambiente de staging funcional para validar builds antes de producción.
- Script de backup automático bloqueante antes de cada despliegue.
- Migraciones de base de datos numeradas y aplicadas en orden.
- Script de reinicio con aviso previo a los jugadores.
- Healthcheck automatizado post-despliegue.
- Script de rollback probado periódicamente en staging.
- Secretos (contraseñas, claves) fuera del control de versión.
Con el pipeline de despliegue continuo en marcha, el siguiente paso es aplicar esa misma disciplina de automatización a la configuración inicial del ambiente —consulta el tutorial de creación de servidor de MU Online para entender la base sobre la cual se construyó este pipeline.
Preguntas frecuentes
¿Necesito un servidor de CI dedicado (Jenkins/GitLab) para esto?
No es obligatorio. Puedes empezar con un script bash/PowerShell disparado manualmente o por una tarea programada, y evolucionar hacia GitHub Actions/GitLab CI/Jenkins a medida que el equipo crezca. Lo importante es que el proceso sea repetible y esté versionado, no la herramienta en sí.
¿Es seguro automatizar el despliegue en un servidor de MU en producción?
Sí, siempre que el pipeline incluya backup automático antes de cada despliegue, un ambiente de staging para validar la build y un paso de rollback probado. El despliegue manual tiende a ser más riesgoso que un pipeline bien diseñado, porque depende de la memoria humana.
¿Cómo manejar el cliente del juego en el despliegue continuo?
El cliente (archivos que el jugador descarga) normalmente tiene un pipeline separado del servidor, publicando paquetes de actualización vía launcher. El despliegue del servidor debe verificar la compatibilidad de versión de protocolo antes de liberar la actualización del cliente.
¿Qué hacer si el despliegue rompe el GameServer en producción?
El pipeline debe tener un paso de rollback automático: restaurar el binario/DLL anterior y el dump de base de datos del backup previo al despliegue, y luego reiniciar los servicios. Ese rollback debe probarse periódicamente, no solo escribirse y olvidarse.
¿Se puede desplegar sin desconectar a los jugadores en línea?
Parcialmente. Cambiar las DLLs del GameServer normalmente exige reiniciar el proceso, así que lo ideal es programar los despliegues en ventanas de bajo tráfico y avisar a la comunidad con anticipación. Para el ConnectServer y los servicios web, sí es posible desplegar con cero downtime usando un balanceador simple.