lunes, 28 de abril de 2008

* MOVIENDO SERVIDOR MYSQL A OTRA MAQUINA

El procedimiento que describo en el presente artículo, lo puse en práctica hace algún tiempo y ahora procedo a documentarlo a modo de compartir esta simple solución aplicada para aumentar el rendimiento de un servidor LAMP.

"Problema: Se tiene un servidor LAMP (Linux Apache Mysql Php) el cual se ha notado un poco lento y se desea liberarlo un poco de trabajo"

La solución adoptada consiste en simplemente instalar un servidor mysql en otra máquina la cual se dedicará única y exclusivamente a dar servicio de base de datos MYSQL, y detener el que estaba corriendo en la máquina en donde está instalado el apache liberando así más recursos de memoria y procesador.

Hasta aquí todo se ve simple, pero la idea es hacer el cambio de forma transparente para los clientes que tienen su servicio de web hosting, es decir, que estos no tengan que hacer ningun cambio en sus páginas php, perl u otro. Ellos deben seguir viendo un servidor MYSQL corriendo en el localhost y o en 127.0.0.1 pero el servidor en verdad estará corriendo en otra máquina sin que ellos se den cuenta.

Bueno, como ya se captó la idea, lo que haremos es engañar a los clientes los cuales harán sus conexiones mysql desde php a un servidor local que en realidad no es tal, pues en verdad es remoto, y para lograr esto haremos un tunel desde un puerto tcp local (3306) al puerto tcp remoto (3306) de manera que todas las peticiones mysql que lleguen a la ip 127.0.0.1 se vayan directo a la ip del servidor remoto. Para lograr nuestro cometido de forma muy simple usaremos un software llamado 'socat' el cual pese a su simplicidad de uso es muy poderoso, flexible, con muchas opciones, y se le puede dar muchísimas aplicaciones.

Pero no olvidemos que los sitios que se conectan a una base mysql desde php normalmente tienen configurado como servidor 'localhost', y en este caso las conexiones no son realizadas via tcp sino via socket unix, de manera que debemos buscar la forma de que este tipo de peticiones también sean reenviadas al servidor remoto. ¡ Ha pero no hay problema ! Con socat también podemos manejar las conexiones a sockets unix y transformarlas en conexiones tcp.

¡ Que maravilloso es socat !

Entonces manos a la obra:

1) Lo primero que debemos hacer es instalar el nuevo servidor MYSQL, ya saben
#apt-get install mysql-server
No olviden comentar o modificar la línea que dice "bind-address = 127.0.0.1" en el archivo /etc/mysql/my.cnf de lo contrario el servidor mysql sólo escuchará las peticiones provenientes del host local.
#/etc/init.d/mysql stop
#/etc/init.d/mysql start

2) Una vez que esté listo mysql-server, podemos traernos las bases de datos desde el servidor original usando mysqldump. Hay que tener en cuenta que si tenemos el servidor web arriba puede que se esté escribiendo en algunas de las bases de datos, por tanto debemos bajarlo, y también bajar cualquier script o programa que pudiese escribir en el servidor mysql, mientras realizamos todo el proceso que dejará funcionando el tunel.

#/etc/init.d/apache2 stop
#read -p "Ingresar Clave: " -s clave;mysqldump -A --add-drop-table --password=$clave > dump.sql;chmod go-r dump.sql

Movemos el archivo al host remoto usando scp u otro medio.
#scp dump.sql root@10.0.0.209:/root/dump.sql

Luego volcamos toda la información de la base de datos original en el nuevo servidor mysql:
#mysql -p < /root/dump.sql
Si después de esto hacemos un stop y luego un start del servidor, éste no partirá y arrojará un error como este:
ERROR 1045 (28000): Access denied for user 'debian-sys-maint'@'localhost' (using password: YES)
Para corregir esto se deben copiar la o las password que están en el archivo /etc/mysql/debian.cnf desde el servidor orginal al mismo archivo que esta en el nuevo servidor.Y luego:
#/etc/init.d/mysql stop #/etc/init.d/mysql start

3) Recordemos que la mayoría de los permisos de los usuarios mysql hacen referencia al localhost, ya que las consultas se hacían desde el equipo local, pero ahora las consultas que hagan nuestros usuarios serán redireccionadas desde el host original al host nuevo. Esto quiere decir que debemos agregar los mismos permisos que los usuarios tienen desde el localhost pero considerando que ahora viene desde la ip del host original. Podríamos hacer esta tarea manualmente ingresando los permisos uno por uno, pero en el caso que tengamos varias bases de datos podría ser un poco lento y tedioso, asi es que recomiendo hacer lo sgte.:
En el nuevo servidor:
#echo "select * from user;" | mysql -p mysql | grep "^localhost" | grep -v "debian-sys-maint" | sed "s/^localhost/10.0.0.126/" > /root/user.sql
(reemplace la ip 10.0.0.126 por la ip de su servidor original)

#echo "LOAD DATA INFILE '/root/user.sql' INTO TABLE user;" | mysql -p mysql

Luego haga algo similar en la tabla 'db', así:
#echo "select * from db;" | mysql -p mysql | grep "^localhost" | sed "s/^localhost/10.0.0.126/" > /root/db.sql
#echo "LOAD DATA INFILE '/root/db.sql' INTO TABLE db;" | mysql -p mysql
#echo "flush privileges;" | mysql -p

Ahora, hay que hacer pruebas de conexiones a las bases de datos desde el host original al nuevo. Ej.:
#echo "show tables;" | mysql -u usuario1 -h 10.0.0.209 -p base1
Aquí debe reemplazar usuario1 por el usuario de conexion a la base1 (donde base1 es el nombre de la base que está probando). Al probar todas las bases de la misma forma debería arrojar el listado de tablas que tiene cada base.

4) Bueno, y finalmente en el servidor original crearemos los tuneles mysql hacia el nuevo sevidor de base de datos mysql. Para mayor comodidad yo me hice un script con el cual puedo, comodamente, levantar y bajar los tuneles. El contenido del script es el sgte.:

#!/bin/sh
NAME="tunel-mysql"
REMOTE_HOST="10.0.0.209"
REMOTE_PORT="3306"
LOCAL_PORT="3306"

case "$1" in
start)
echo -n "Iniciando 'tunel-mysql' ..."
socat -d -lf/var/log/socat.log UNIX-LISTEN:/var/run/mysqld/mysqld.sock,reuseaddr,fork,unlink-early,mode=777,su=nobody,user=mysql,group=mysql TCP4:$REMOTE_HOST:$REMOTE_PORT &
socat -d -lf/var/log/socat.log TCP4-LISTEN:$LOCAL_PORT,setuid=nobody,fork,reuseaddr TCP4:$REMOTE_HOST:$REMOTE_PORT &
echo " HECHO"
;;

stop)
echo -n "Deteniendo 'tunel-mysql' ..."
killall socat
echo " HECHO"
;;

restart)
echo -n "Deteniendo 'tunel-mysql' ..."
killall socat
echo " HECHO"
echo -n "Iniciando 'tunel-mysql' ..."
sleep 3
socat -d -lf/var/log/socat.log UNIX-LISTEN:/var/run/mysqld/mysqld.sock,reuseaddr,fork,unlink-early,mode=777,su=nobody,user=mysql,group=mysql TCP4:$REMOTE_HOST:$REMOTE_PORT &
socat -d -lf/var/log/socat.log TCP4-LISTEN:$LOCAL_PORT TCP4:$REMOTE_HOST:$REMOTE_PORT &
echo " HECHO"
;;

*)
echo "Usage: /etc/init.d/$NAME {start|stop|restart}"
exit 1
;;

esac
#Fin del script

Reemplace el valor de la variable REMOTE_HOST por la dirección ip de su nuevo servidor mysql.
Este script debe quedar en /etc/init.d/tunel-mysql. Seteamos los permisos de ejecución correspondientes:
#chmod ugo+x /etc/init.d/tunel-mysql
No olvide que antes de subir el tunel se debe bajar el servidor mysql y desactivarlo para que no vuelva a subir automáticamente al reiniciar el servidor.
#/etc/init.d/mysql stop

Para desactivar el inicio automático puedes usar la utilidad rcconf y desmarcar mysql (usando tecla espaciadora)
o bien hacer: # rm /etc/rc*.d/S*mysql

Pero, antes que se nos olvide lo principal:
#apt-get install socat

Y listo, ahora subimos el tunel:
#/etc/init.d/tunel-mysql start

Haga algunas pruebas finales de conexión a algunas bases de datos de la stge. forma:

#echo "show tables;" | mysql -u usuario1 -h localhost -p base1

y también haga pruebas de la forma: #echo "show tables;" | mysql -u usuario1 -h 10.0.0.126 -p base1

El resultado de estas pruebas debería ser el listado de tablas de las bases correspondientes.
Si el tunel funciona correctamente vuelva a subir el servidor web:
#/etc/init.d/apache2 start

5) Nos queda un detalle, activar el inicio automático del tunel de manera que al reiniciar el servidor no tengamos que levantar nada manualmente.
#cd /etc/rc2.d
#ln -s ../init.d/tunel-mysql S19tunel-mysql
#cd /etc/rc3.d
#ln -s ../init.d/tunel-mysql S19tunel-mysql
#cd /etc/rc4.d
#ln -s ../init.d/tunel-mysql S19tunel-mysql
#cd /etc/rc5.d
#ln -s ../init.d/tunel-mysql S19tunel-mysql

Como prueba final, se podría reiniciar el servidor y probar si todo funciona correctamente. Acceder a las páginas con php o perl que escriban en las bases de datos y observar que no arrojen errores.

Puede que deseemos hacer una prueba preliminar usando un servidor LAMP que no esté en producción, de manera de saber como optimizar y acelerar todo el proceso cuando se haga en el servidor en producción.

Nota: Por seguridad, no olvide borrar todos los archivos .sql generados durante el proceso movimiento de datos de un servidor al otro.

Chaolin ...