Tabla de Contenidos

PLAN DE MIGRACIÓN FARAMIR → GIMLI

INFORMACIÓN GENERAL

Servidores Involucrados

Situación de Partida (IMPORTANTE)

Datos a Migrar

Dominios

Notas Importantes


FASE 0: PREPARACIÓN Y BACKUP

0.1 Verificar Espacio en Gimli

# En gimli
df -h /var/www
df -h /tmp
df -h /home
 
# Espacio requerido: mínimo 3GB libres (datos + backups)

0.2 Crear Directorios en Gimli

# En gimli
sudo mkdir -p /var/www/dokuwiki
sudo mkdir -p /var/www/pruebaewelinapmu
sudo chown -R www-data:www-data /var/www/dokuwiki /var/www/pruebaewelinapmu
sudo mkdir -p /tmp/migracion_backup

0.3 Backup Completo en Faramir

# En faramir
mkdir -p ~/backup_migracion
cd ~/backup_migracion
# 1. Identificar y volcar SOLO las bases de datos relevantes (WordPress + Dokuwiki si usa BD)
sudo mysql -e "SHOW DATABASES;"
grep -i "DB_NAME" /var/www/pruebaewelinapmu/wp-config.php
 
sudo mysqldump --single-transaction --routines --triggers wordpress_db > wordpress_db.sql
gzip wordpress_db.sql
# 2. Backup de Dokuwiki
sudo tar -czf dokuwiki_backup.tar.gz -C /var/www dokuwiki
# 3. Backup de WordPress (completo)
sudo tar -czf wordpress_backup.tar.gz -C /var/www pruebaewelinapmu
# 4. Backup de configuración Nginx (solo referencia, no se copiará entera a gimli)
sudo tar -czf nginx_config_backup.tar.gz -C /etc nginx
# 5. Backup de crontabs
crontab -l > crontab_usuario.txt
sudo crontab -l > crontab_root.txt
# 6. Backup de listas de paquetes
dpkg -l | grep '^ii' | awk '{print $2}' > paquetes_instalados.txt
dpkg --get-selections > paquetes_selecciones.txt
apt-mark showmanual > paquetes_manuales.txt
# 7. Verificar backups
ls -lh
tar -tzf dokuwiki_backup.tar.gz | head -20
tar -tzf wordpress_backup.tar.gz | head -20
zcat wordpress_db.sql.gz | head -50
# 8. Copiar backups a gimli
scp *.tar.gz *.txt ubuntu@gimli:/tmp/migracion_backup/

0.4 Verificar Backups en Gimli

# En gimli
cd /tmp/migracion_backup
ls -lh
md5sum *.tar.gz *.txt  # Guardar checksums para verificar integridad

FASE 1: SINCRONIZACIÓN DE PAQUETES UBUNTU

1.1 Instalar en Gimli Todos los Paquetes de Faramir

No hay repositorios adicionales que sincronizar. Gimli parte sin base de datos (no tiene MariaDB instalado) y sin varios paquetes que sí están en faramir — hay que instalarlos todos antes de empezar el resto de la migración.

# En gimli
cd /tmp/migracion_backup
 
sudo apt update
sudo apt install -y $(cat paquetes_instalados.txt) 2>&1 | tee instalacion_paquetes.log
 
grep "Unable to locate package" instalacion_paquetes.log
grep "dependency problems" instalacion_paquetes.log
 
sudo systemctl restart php8.4-fpm mariadb

1.2 Verificar Servicios

# En gimli
sudo systemctl status nginx
sudo systemctl status php8.4-fpm
sudo systemctl status mariadb
sudo systemctl status fail2ban

FASE 2: CONFIGURACIÓN DE NGINX (fusión de configs, sin tocar otros sitios de gimli)

2.0 Cómo Funciona Hoy el Proxy

2.1 Backup de los Vhosts Actuales en Gimli

# En gimli
sudo cp /etc/nginx/sites-available/pruebaewelinapmu /etc/nginx/sites-available/pruebaewelinapmu.bak.$(date +%Y%m%d)
sudo cp /etc/nginx/sites-available/arocha /etc/nginx/sites-available/arocha.bak.$(date +%Y%m%d)

2.2 Nueva Configuración Fusionada: arocha.my.to (Dokuwiki)

Combina la base de gimli (SSL, 10-restrict-methods.inc, protección anti-scraping de /doku.php) con la lógica real de Dokuwiki de faramir (rewrites, bloqueo de /conf/ y /data/, fastcgi local).

# En gimli
sudo tee /etc/nginx/sites-available/arocha.new > /dev/null <<'EOF'
#==============================================================================#
# arocha.my.to (Dokuwiki)                                                      #
#==============================================================================#
server {
    include /etc/nginx/conf.d/10-restrict-methods.inc;
    listen [::]:443 ssl ipv6only=on; # managed by Certbot
    listen 443 ssl; # managed by Certbot
    server_name arocha.my.to luis-arocha.duckdns.org;
 
    root /var/www/dokuwiki;
    index index.php doku.php;
 
    client_max_body_size 4M;
    client_body_buffer_size 128K;
 
    location /robots.txt {
        alias /var/www/dokuwiki/robots.txt;
    }
    location /.well-known/acme-challenge {
        root /var/tmp/acme-challenges;
        try_files $uri =404;
    }
 
    location ^~ /conf/ { return 403; }
    location ^~ /data/ { return 403; }
    location ~ /\.ht { deny all; }
 
    location / {
        try_files $uri $uri/ @dokuwiki;
    }
 
    location @dokuwiki {
        rewrite ^/_media/(.*) /lib/exe/fetch.php?media=$1 last;
        rewrite ^/_detail/(.*) /lib/exe/detail.php?media=$1 last;
        rewrite ^/_export/([^/]+)/(.*) /doku.php?do=export_$1&id=$2 last;
        rewrite ^/(.*) /doku.php?id=$1 last;
    }
 
    # doku.php con la protección anti-scraping que antes filtraba el proxy_pass
    # (Referer + $is_deep_query + limit_req), ahora aplicada al fastcgi local.
    # location = /doku.php tiene prioridad sobre el bloque genérico ~ \.php$
    location = /doku.php {
        limit_except GET HEAD POST OPTIONS { deny all; }
        expires -1;
 
        set $suspect "";
        if ($is_deep_query) {
            set $suspect "D";
        }
        if ($http_referer !~* "^https?://(arocha\.my\.to|luis-arocha\.duckdns\.org)/") {
            set $suspect "${suspect}R";
        }
        if ($suspect = "DR") {
            return 403;
        }
        limit_req zone=deep_query_limit burst=10 nodelay;
 
        try_files $uri =404;
        fastcgi_pass unix:/run/php/php8.4-fpm.sock;
        fastcgi_index index.php;
        include fastcgi_params;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
    }
 
    location ~ \.php$ {
        try_files $uri =404;
        fastcgi_pass unix:/run/php/php8.4-fpm.sock;
        fastcgi_index index.php;
        include fastcgi_params;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
    }
 
    ssl_certificate /etc/letsencrypt/live/arocha.my.to/fullchain.pem; # managed by Certbot
    ssl_certificate_key /etc/letsencrypt/live/arocha.my.to/privkey.pem; # managed by Certbot
    include /etc/letsencrypt/options-ssl-nginx.conf; # managed by Certbot
    ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem; # managed by Certbot
}
server {
    if ($host = arocha.my.to) {
        return 301 https://$host$request_uri;
    } # managed by Certbot
    if ($host = luis-arocha.duckdns.org) {
        return 301 https://$host$request_uri;
    } # managed by Certbot
    listen 80;
    listen [::]:80;
    server_name arocha.my.to luis-arocha.duckdns.org;
    return 404; # managed by Certbot
}
EOF

2.3 Nueva Configuración Fusionada: pruebaewelinapmu.my.to (WordPress)

⚠️ Requiere certificado nuevo (ver 2.4) antes de poder activar el bloque SSL tal cual está escrito abajo.

# En gimli
sudo tee /etc/nginx/sites-available/pruebaewelinapmu.new > /dev/null <<'EOF'
#==============================================================================#
# pruebaewelinapmu.my.to (WordPress)                                           #
#==============================================================================#
server {
    listen 80;
    listen [::]:80;
 
    include /etc/nginx/conf.d/10-restrict-methods.inc;
    server_name pruebaewelinapmu.my.to;
 
    root /var/www/pruebaewelinapmu;
    index index.php index.html;
 
    access_log /var/log/nginx/pruebaewelinapmu.access.log;
    error_log  /var/log/nginx/pruebaewelinapmu.error.log;
 
    client_max_body_size 10M;
 
    location /robots.txt {
        alias /var/www/pruebaewelinapmu/robots.txt;
    }
    location /.well-known/acme-challenge {
        root /var/tmp/acme-challenges;
        try_files $uri =404;
    }
 
    location / {
        try_files $uri $uri/ /index.php?$args;
    }
 
    location ~ \.php$ {
        include snippets/fastcgi-php.conf;
        fastcgi_pass unix:/run/php/php8.4-fpm.sock;
        # Este server block solo se alcanza por HTTPS (443), así que HTTPS
        # siempre es "on" aquí. El truco de faramir con X-Forwarded-Proto
        # ya no hace falta al servir localmente con SSL terminado en el
        # mismo nginx.
        fastcgi_param HTTPS on;
    }
 
    location ~ /\.ht {
        deny all;
    }
    location ~* /(?:xmlrpc\.php|wp-config\.php|readme\.html|license\.txt) {
        deny all;
    }
}
EOF

2.4 Emitir Certificado SSL para pruebaewelinapmu (nuevo)

⚠️ Certbot con el plugin –nginx edita el vhost que esté activo en sites-enabled.

# En gimli - confirmar primero que efectivamente no existe:
sudo certbot certificates | grep -A5 pruebaewelinapmu
 
# Emitir por webroot (no toca el vhost activo en producción)
sudo certbot -d pruebaewelinapmu.my.to
 
# Verificar
sudo certbot certificates | grep -A5 pruebaewelinapmu

2.5 Validar Sintaxis de los Ficheros Nuevos

# En gimli
sudo nginx -t

2.6 Prueba en Paralelo (sin afectar producción)

Como los .new todavía no están en sites-enabled, para probarlos hay que apuntar php-fpm directamente o activar un puerto alternativo temporal. La forma más simple: comprobar a nivel de archivo y de PHP-FPM antes de que existan datos (esto se completa de verdad en la Fase 6, una vez migrados los ficheros y la BD):

# En gimli - comprobaciones básicas ahora; las pruebas HTTP completas se
# hacen en la Fase 6 cuando los .new ya estén activados en un vhost de prueba
php -v
ls -la /etc/nginx/sites-available/*.new

⚠️ Nota de secuencia: los .new sustituyen por completo la lógica de proxy_pass. No se activan (no se copian sobre el fichero real ni se tocan symlinks de sites-enabled) hasta la Fase 7, una vez migrados y verificados los datos (Fases 3-6). Esto evita romper el sitio en producción antes de tiempo.


FASE 3: MIGRACIÓN DE BASE DE DATOS

3.1 Preparar MariaDB en Gimli

# En gimli
sudo systemctl start mariadb
sudo systemctl enable mariadb

3.2 Restaurar Base de Datos de WordPress (solo esta BD, no todas)

# En gimli
cd /tmp/migracion_backup
sudo gunzip wordpress_db.sql.gz
 
# Crear la BD si no existe (nombre real obtenido en 0.4)
sudo mysql -e "CREATE DATABASE IF NOT EXISTS wordpress_db CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;"
sudo mysql wordpress_db < wordpress_db.sql
 
sudo mysql -e "SHOW DATABASES;"

3.3 Crear Usuario para WordPress (si es necesario)

# En gimli - obtener credenciales exactas de faramir
ssh ubuntu@faramir "grep -E 'DB_NAME|DB_USER|DB_PASSWORD|DB_HOST' /var/www/pruebaewelinapmu/wp-config.php"
 
# Crear usuario en gimli SOLO si no existe ya (gimli aloja otras BD/usuarios)
sudo mysql -e "CREATE USER IF NOT EXISTS 'wordpress_user'@'localhost' IDENTIFIED BY 'contraseña_segura';"
sudo mysql -e "GRANT ALL PRIVILEGES ON wordpress_db.* TO 'wordpress_user'@'localhost';"
sudo mysql -e "FLUSH PRIVILEGES;"

FASE 4: MIGRACIÓN DE ARCHIVOS

4.1 Migrar Dokuwiki

# En gimli
cd /tmp/migracion_backup
sudo tar -xzf dokuwiki_backup.tar.gz -C /var/www/
ls -la /var/www/dokuwiki

4.2 Migrar WordPress (copia completa)

# En gimli - copia completa tal cual estaba en faramir
cd /tmp/migracion_backup
sudo rm -rf /var/www/pruebaewelinapmu
sudo tar -xzf wordpress_backup.tar.gz -C /var/www/
 
ls -la /var/www/pruebaewelinapmu

4.3 Ajustar wp-config.php

# En gimli - el wp-config.php ya viene incluido en la copia completa.
# Solo hay que revisar/actualizar credenciales de BD si difieren:
sudo vim /var/www/pruebaewelinapmu/wp-config.php
# Confirmar DB_HOST, DB_NAME, DB_USER, DB_PASSWORD

4.4 Ajustar Permisos

# En gimli
sudo chown -R www-data:www-data /var/www/pruebaewelinapmu /var/www/dokuwiki
 
sudo find /var/www/pruebaewelinapmu -type d -exec chmod 755 {} \;
sudo find /var/www/pruebaewelinapmu -type f -exec chmod 644 {} \;
sudo chmod 600 /var/www/pruebaewelinapmu/wp-config.php

4.5 Verificar/Crear robots.txt Propio de Cada Sitio

# En gimli - cada sitio sirve su propio robots.txt (ya no comparten el de /var/www/html)
ls -la /var/www/dokuwiki/robots.txt /var/www/pruebaewelinapmu/robots.txt 2>&1
 
# Si falta alguno, crear uno básico:
[ -f /var/www/dokuwiki/robots.txt ] || echo -e "User-agent: *\nDisallow:" | sudo tee /var/www/dokuwiki/robots.txt
[ -f /var/www/pruebaewelinapmu/robots.txt ] || echo -e "User-agent: *\nDisallow:" | sudo tee /var/www/pruebaewelinapmu/robots.txt
 
sudo chown www-data:www-data /var/www/dokuwiki/robots.txt /var/www/pruebaewelinapmu/robots.txt

4.6 Actualizar URLs en Base de Datos (si aplica)

Sin WP-CLI instalado, se hace vía SQL directo. El prefijo de tablas (normalmente wp_, pero puede ser otro) está en $table_prefix dentro de wp-config.php.

# En gimli - comprobar prefijo de tablas
grep table_prefix /var/www/pruebaewelinapmu/wp-config.php
 
# Verificar URLs almacenadas (ajustar wp_ si el prefijo es distinto)
sudo mysql wordpress_db -e "SELECT option_name, option_value FROM wp_options WHERE option_name IN ('siteurl','home');"
 
# Si hace falta corregir (solo la URL exacta, no cambia rutas dentro de contenido):
# sudo mysql wordpress_db -e "UPDATE wp_options SET option_value='https://pruebaewelinapmu.my.to' WHERE option_name IN ('siteurl','home');"

⚠️ Un simple UPDATE en wp_options no reescribe URLs que puedan estar serializadas dentro de otros campos (widgets, algunos ajustes de plugins). Si tras la migración se detectan URLs rotas en contenido serializado, ese caso puntual sí justificaría instalar WP-CLI temporalmente para usar wp search-replace (que gestiona la serialización correctamente) — evaluarlo solo si aparece el problema, no de forma preventiva.


FASE 5: CONFIGURACIÓN DE CRON JOBS

5.1 Crontab de Usuario (ubuntu) en Gimli

Solo hace falta añadir check_sites y check_sites Diario (confirmado por Luis) más los ajustes de permisos propios de Dokuwiki. El resto de tareas que aparecían en el crontab original de gandalf/faramir (report_fail2ban, apto update, check_reboot, notify, copias/backup) ya existen en gimli — vienen de un repositorio común a todas las máquinas y no requieren ninguna acción.

# En gimli
crontab -e
 
# ========== DE GANDALF (a migrar) ==========
12  * * * * check_sites
15 09 * * * check_sites Diario
 
# ========== NUEVAS, DE FARAMIR (permisos Dokuwiki) ==========
01 00 * * * sudo find /var/www/dokuwiki -type d -exec chmod 775 {} \;
02 00 * * * sudo find /var/www/dokuwiki -type f -exec chmod 664 {} \;
08 01 * * * sudo chown www-data:www-data -R /var/www/dokuwiki

5.2 Crontab de Root en Gimli

# En gimli
sudo crontab -e
 
# ========== NUEVAS, DE FARAMIR (permisos WordPress) ==========
*/15 * * * * find /var/www/pruebaewelinapmu -type d -exec chmod g+rwx {} \;
*/15 * * * * find /var/www/pruebaewelinapmu -type f -exec chmod g+rw {} \;

5.3 Verificar que las Nuevas Entradas se Guardaron

# En gimli
crontab -l
sudo crontab -l

FASE 6: VERIFICACIÓN Y PRUEBAS (antes del corte)

6.0 Activar los .new en Modo de Prueba

Este es el único momento en que los vhosts .new entran en juego antes del corte real. Se activan ya con la lógica final (local, sin proxy_pass), pero solo si Fases 3-5 (BD, archivos, permisos) ya están completas — si no, esta prueba fallará por falta de datos, no por la configuración de nginx.

# En gimli
sudo cp /etc/nginx/sites-available/arocha.new /etc/nginx/sites-available/arocha
sudo cp /etc/nginx/sites-available/pruebaewelinapmu.new /etc/nginx/sites-available/pruebaewelinapmu
sudo nginx -t
sudo systemctl reload nginx

⚠️ A partir de aquí, ambos dominios ya sirven en local (dejan de usar proxy_pass a faramir). Si algo falla en las pruebas siguientes, usar el rollback de 7.3 inmediatamente — no es necesario esperar a completar toda la Fase 6 para revertir.

6.1 Verificar Servicios y Puertos

# En gimli
sudo systemctl status nginx php8.4-fpm mariadb fail2ban
sudo netstat -tlnp | grep -E ':(80|443|3306)'

6.2 Probar Dokuwiki (ya sirviendo en local tras 6.0)

# En gimli
sudo -u www-data ls -la /var/www/dokuwiki/data/
curl -I https://arocha.my.to
curl -I https://luis-arocha.duckdns.org
 
# Navegar manualmente y verificar:
# - Página de inicio, edición de página, archivos multimedia
# - Que la protección de /doku.php (Referer, limit_req) no bloquee uso normal

6.3 Probar WordPress (ya sirviendo en local tras 6.0)

Sin WP-CLI, la verificación es a nivel de archivos, PHP-FPM y navegación real, en vez de comandos wp.

# En gimli
curl -I https://pruebaewelinapmu.my.to
curl -I https://pruebaewelinapmu.my.to/wp-admin
 
# Confirmar que los plugins y temas migrados están presentes como archivos
ls /var/www/pruebaewelinapmu/wp-content/plugins/
ls /var/www/pruebaewelinapmu/wp-content/themes/
 
# Verificar manualmente en el navegador: frontend, admin (login), PDFs,
# imágenes, y que los plugins críticos funcionan correctamente

6.4 Verificar Logs

# En gimli
sudo tail -f /var/log/nginx/error.log
sudo tail -f /var/log/php8.4-fpm.log
sudo tail -f /var/log/mysql/error.log
 
sudo grep -i error /var/log/nginx/error.log | tail -20
sudo grep -i error /var/log/php8.4-fpm.log | tail -20

6.5 Probar check_sites

# En gimli - copias/backup y demás scripts comunes no requieren prueba
# (ya funcionan igual que en el resto de máquinas del grupo)
check_sites

FASE 7: CONFIRMACIÓN DEL CORTE

Con la Fase 6.0, gimli ya sirve ambos dominios en local — el corte “real” ya ha ocurrido en el momento en que las pruebas de la Fase 6 se completan con éxito. Esta fase es solo para asegurar que no queda ningún dato desactualizado de última hora y dejar constancia formal del corte.

7.1 Resincronización de Última Hora (si hubo cambios desde el backup inicial)

# En gimli - solo si ha pasado tiempo entre el backup (Fase 0) y este punto
# y podría haber contenido nuevo en faramir (páginas de wiki editadas,
# posts publicados, etc. mientras faramir seguía siendo el backend activo)
rsync -avz --delete ubuntu@faramir:/var/www/dokuwiki/ /var/www/dokuwiki/
 
rsync -avz --delete --exclude='wp-config.php' \
  ubuntu@faramir:/var/www/pruebaewelinapmu/ /var/www/pruebaewelinapmu/
 
ssh ubuntu@faramir "sudo mysqldump --single-transaction wordpress_db" > /tmp/wordpress_final.sql
sudo mysql wordpress_db < /tmp/wordpress_final.sql
 
sudo systemctl reload nginx

⚠️ Si se ejecuta este paso, hay que repetir las pruebas clave de la Fase 6 (frontend, admin, multimedia) tras la resincronización.

7.2 Rollback (si algo falla en cualquier momento tras 6.0)

# En gimli - basta con restaurar el vhost con proxy_pass a faramir
sudo cp /etc/nginx/sites-available/pruebaewelinapmu.bak.$(date +%Y%m%d) /etc/nginx/sites-available/pruebaewelinapmu
sudo cp /etc/nginx/sites-available/arocha.bak.$(date +%Y%m%d) /etc/nginx/sites-available/arocha
sudo nginx -t
sudo systemctl reload nginx
# Faramir no ha sido tocado en ningún momento, por lo que el tráfico
# vuelve a funcionar de inmediato en cuanto se recarga nginx

Modificar scripts de copia de seguridad

Modificar el script backup_data.


FASE 8: SEGUIMIENTO Y ELIMINACIÓN DE FARAMIR

8.1 Periodo de Observación (24–48h)

# En gimli - monitoreo, faramir se mantiene encendido e intacto como red de seguridad
watch -n 30 'curl -s -o /dev/null -w "%{http_code}" https://pruebaewelinapmu.my.to && echo " - WordPress OK"'
watch -n 30 'curl -s -o /dev/null -w "%{http_code}" https://arocha.my.to && echo " - Dokuwiki OK"'

8.2 Limpieza de /etc/hosts en Gimli

# En gimli - eliminar las referencias a gandalf y faramir (ya no se necesitan:
# gandalf porque los scripts se obtienen del repositorio común, no por IP;
# faramir porque gimli ya sirve ambos sitios en local). NO tocar boromir.
sudo vim /etc/hosts
# Eliminar las líneas:
#   10.0.0.201  gandalf
#   10.0.0.10   pruebaewelinapmu.my.to
#   10.0.0.10   faramir
#   10.0.0.10   arocha.my.to
#   10.0.0.10   luis-arocha.duckdns.org
 
cat /etc/hosts

⚠️ Revisar el resultado a mano antes de guardar — no usar sed automático aquí, mejor edición manual para no arriesgar el resto del fichero (compartido con boromir y otros sitios del grupo).

8.3 Eliminar el Servidor Faramir y su Disco

Tras confirmar que gimli funciona correctamente sin incidencias durante el periodo de observación, eliminar faramir y gandalf por completo (instancia y volumen de disco) desde la consola de Oracle Cloud.

Verificar que la instancia y el volumen han desaparecido.

⚠️ Este paso es irreversible: no hay backup posterior de faramir ni desmantelamiento gradual — se destruye la máquina y su disco directamente. Confirmar que el periodo de observación (8.1) ha sido satisfactorio antes de ejecutar.


FIN DEL PLAN DE MIGRACIÓN