Asterisk con Alta-Disponibilidad + MySQL

10 Dic

Este tutorial fue escrito por uno de los participantes de nuestro foro: navaismo.
El artículo original lo pueden encontrar aquí.

En algunas ocasiones nos vemos en la necesidad de crear un Cluster de alta disponibilidad para nuestros servicios de Asterisk. A continuación se describen los pasos necesarios para llevar esto acabo en nuestros servidores usando Asterisk y Mysql(por si queremos usar Asterisk Realtime Architechture).

Algunas indicaciones iniciales:

  • Estos pasos están basados en las instrucciones que brinda Digium y los tutoriales de DRBD para Mysql.
  • Este tutorial no esta hecho para hacer copy&paste.
  • El color verde indica que son pasos para realizar en ambos servidores.
  • El color Naranja indica que son pasos para realizar en el servidor primario.
  • El color Rojo indica que son pasos para realizar en el servidor secundario.
  • El Hostname del Servidor primario es «node1«.
  • El Hostname del Servidor secundario es «node2«.
  • La dirección IP del servidor primario es 10.0.1.51
  • La dirección IP del servidor secundario es 10.0.1.52
  • La dirección IP compartida del cluster y a la que deberán apuntar los servicios(como el registro de teléfonos, MySQL o apache) es 10.0.1.50.
  • La dirección IP del Gateway es 10.0.1.1.
  • Se puede adaptar fácilmente el Hardware Failover que provee Digium(rseries) y los servicios de Apache.

DRBD Cluster

La Imagen anterior describe el funcionamiento del Cluster:

  • Escenario 1: El servidor primario esta activo y el secundario esta en modo pasivo esperando.
  • Escenario 2: El servidor Primario ha entrado en estado de falla(por conexión de RED o por reinicio o falla en el kernel), el servidor secundario entonces, se convierte en el servidor primario y es marcado como activo.
  • Escenario 3: El servidor secundario(antes primario) se ha recuperado de la falla y ha entrado en modo pasivo.
  • Escenario 4: El servidor primario(antes secundario)  ha presentado falla  y el servidor secundario(antes primario) es marcado como servidor primario nuevamente y entra en modo activo.

 

Paso 1 —- Realizar en Ambos Servidores:

Instala CentOS 5.X(Los agentes de recursos «ocf»de Digium no son compatibles con las versiones 6.X de CentOS). Escoger el modo de partición manual y dejar un espacio libre sin formateo ni nada, en este tutorial yo he dejado 5GB sin particionar. Este espacio será donde guardemos nuestros datos a replicar en el Cluster, así que deberán considerar cuanto espacio necesitaran para sus archivos y logs.

 

Paso 2 —- Realizar en Ambos Servidores:

Instala las dependencias para nuestros servidores. Primero añadiré el repositorio de rpmforge:

rpm -ihv http://pkgs.repoforge.org/rpmforge-release/rpmforge-release-0.5.2-2.el5.rf.[ARCH].rpm

Cambia [ARCH] por la arquitectura del servidor: i386 o x86_64.

Ahora instala las dependencias:

yum -y install kernel kernel-devel libxml2  libxml2-devel  libtiff  libtiff-devel  lame httpd  mysql mysql-devel mysql-server php  php-pear  php-mysql  php-gd openssl openssl-devel perl  bison ncurses-devel audiofile-devel curl sox gcc newt-devel libusb-devel glibc-devel  zlib-devel svn gcc-c++ subversion make nano wget cfdisk

 

Paso 3 —- Realizar en Ambos Servidores:

Ejecuta el comando:

cfdisk

Obtendrás una ventana como esta:

Selecciona el espacio libre y crea una nueva partición con todo el espacio libre usando los botones: New, Primary, Write. Al finalizar te preguntara si quieres efectuar los cambios, escribe la palabra: yes y da enter.

Cuando el proceso termine reinicia el servidor:

reboot

Una vez que el servidor haya arrancado de nuevo hay que limpiar la nueva partición usando el siguiente comando:

dd if=/dev/zero of=/dev/[hs]da[#] bs=1M; sync

Cambia /dev/[hs]da[#] por tu dispositivo que puede ser SDA3,HDA3 o SDB3 HDB3 etc. En este tutorial es HDA3.

Veras una salida similar cuando termine el proceso, dependiendo del tamaño de tu partición y de la velocidad de tus discos duros puede tomar minutos u horas.

Crea un nuevo directorio en /usr/src:

cd /usr/src/
mkdir asterisk
cd asterisk/

En el nuevo directorio descarga Asterisk y sus componentes:

wget http://downloads.asterisk.org/pub/telephony/dahdi-linux-complete/dahdi-linux-complete-2.6.1+2.6.1.tar.gz
wget http://downloads.asterisk.org/pub/telephony/libpri/libpri-1.4.13.tar.gz
wget http://downloads.asterisk.org/pub/telephony/certified-asterisk/certified-asterisk-1.8.11-current.tar.gz
wget http://downloads.digium.com/pub/telephony/rseries/rseries-current.tar.gz

Descomprime los archivos fuente:

for i in `ls *gz`; do tar zxvf $i; done

Compilamos DAHDI:

cd dahdi-linux-complete-2.6.1+2.6.1
make && make all && make install && make config

Compilamos LibrPRI y configuramos DAHDI:

cd ../libpri-1.4.13
make && make install
service dahdi start
dahdi_genconf
dahdi_cfg -vvvvv

Instalamos los agentes de Digium para el cluster:

cd ../rseries-1.0.0/
make && make install

 

Paso 4 —- Realizar solo en  Servidor Primario..

Instalar Asterisk en el servidor primario:

cd ../certified-asterisk-1.8.11-cert9
contrib/scripts/get_mp3_source.sh
./configure && make menuselect

En el menú selecciona las aplicaciones que quieras disponibles en tu asterisk:

Guarda los cambios y compila Asterisk:

make && make install && make config && make samples

Inicia el servicio de Mysql y detenlo esto solo para crear las bases de datos por default.

service mysqld start
service mysqld stop

 

Paso 5 —- Realizar en Ambos Servidores.

Cambiate al directorio de rseries:

cd /usr/src/asterisk/rseries-1.0.0/

Instala los servicios de DRBD:

yum -y install drbd83 kmod-drbd83

Instala Libesmtp, dependencia necesaria para Pacemaker:

wget http://dl.fedoraproject.org/pub/epel/5/[ARCH]/libesmtp-1.0.4-5.el5.[ARCH].rpm #64bits

Cambia [ARCH] por la arquitectura del servidor: i386 o x86_64.

Instala PaceMaker y Corosync:

wget -O /etc/yum.repos.d/pacemaker.repo http://clusterlabs.org/rpm/epel-5/clusterlabs.repo
yum -y install -x heartbeat-stonith* pacemaker corosync

Estamos exluyendo el paquete heratbeat-stonith ya que cre un conflicto con pacemaker.

Instala los archivos de configuración que provee Digium:

make samples

Agrega estas lineas al archivo /etc/drbd.conf:

echo «include «drbd.d/global_common.conf»;» >> /etc/drbd.conf

echo «include «drbd.d/*.res»;» >> /etc/drbd.conf

Edita el archivo /etc/drbd.d/asterisk.res. Cambia astnode1 por el nombre el hostname del servidor primario, cambia astnode2 por el hostname del servidor secundario, cambia /dev/sda3 por la partición que creamos con el espacio libre(en este tutorial hda3). Cambia las IPs por las de tus servidores primarios y secundarios.Cambia el correo electrónico por el tuyo o el administrador del sistema en este tutorial el archivo quedo así:

resource asterisk {
  handlers {
    split-brain "/usr/lib/drbd/notify-split-brain.sh clusteradmin@example.com";
  }

  net {
    after-sb-0pri discard-younger-primary;
    after-sb-1pri discard-secondary;
    after-sb-2pri disconnect;
  }

  on node1 {
    device    /dev/drbd0;
    disk      /dev/hda3;
    address   10.0.1.51:7789;    
    meta-disk internal;
  }
  on node2 {
    device    /dev/drbd0;
    disk      /dev/hda3;
    address   10.0.1.52:7789;
    meta-disk internal;
  }
}

Crea el Recurso llamado asterisk e inicia el servicio de DRDB:

drbdadm create-md asterisk
service drbd start

 

Paso 6 —- Realizar solo en  Servidor Primario.

A continuación crea el UUID llamado Asterisk, y formatea la partición que será usada por el cluster tipo: EXT3

drbdadm disconnect asterisk
drbdadm -- --clear-bitmap new-current-uuid asterisk
drbdadm -- --overwrite-data-of-peer primary asterisk
mkfs.ext3 -m0 /dev/drbd0
drbdadm secondary asterisk
drbdadm detach asterisk
drbdadm up asterisk

Crea el directorio que será usado para el cluster, pero antes marca como nodo primario para poder montarlo:

drbdadm primary asterisk
mkdir /mnt/asterisk

Monta la partición:

mount -t ext3 /dev/drbd0 /mnt/asterisk

Ahora crea el directorio donde estará Mysql:

mkdir /mnt/asterisk/mysql
mkdir /mnt/asterisk/mysql/data
cd /mnt/asterisk/mysql

 

Copia el contenido del directorio de Mysql al nuevo directorio:

cp -aR /var/lib/mysql/* /mnt/asterisk/mysql/data
ls data/

 

Mueve el archivo /etc/my.cnf al nuevo directorio de Mysql:

mv /etc/my.cnf .

Crea un enlace simbólico al nuevo directorio:

ln -s /mnt/asterisk/mysql/my.cnf /etc/

Edita el archivo my.cnf y cambia la directiva de DATADIR: datadir=/mnt/asterisk/mysql/data.


Crea un archivo dummy solo para verificar la replicación en el nodo secundario.

touch test11

Desmonta la partición y marca el nodo como secundario:

cd --
umount /mnt/asterisk
drbdadm secondary asterisk

 

Paso 7 —- Realizar solo en Servidor Secundario

Elimina el archivo /etc/my.cnf

rm -rf /etc/my.cnf

 

Vamos a verificar que los archivos de nuestra partición en el cluster se repliquen. Marca el servidor como primario y crea el mismo directorio:

drbdadm primary asterisk
mkdir /mnt/asterisk

 

Monta la partición y cambiate al directorio:

mount -t ext3 /dev/drbd0 /mnt/asterisk
cd /mnt/asterisk

Haz un enlace simbolico del archo my.cnf al directorio de la partición del cluster:

ln -s /mnt/asterisk/mysql/my.cnf /etc/

Verifica que el archivo my.cnf sea el mismo que editamos anteriormente, es decir, que contenga:  datadir=/mnt/asterisk/mysql/data

Verifica que los archivos de Mysql y el archivo test11 existan en el directorio:

ls

Si los archivos existen la replicación va de maravilla.

Desmonta la partición y marca el nodo como secundario:

cd --
umount /mnt/asterisk
drbdadm secondary asterisk

 

Paso 8 —- Realizar solo en  Servidor Primario.

Cambiate al directorio de rseries, mara el servdiro como primario y monta la partición:

cd /usr/src/asterisk/rseries-1.0.0
drbdadm primary asterisk
mount -t ext3 /dev/drbd0 /mnt/asterisk/

 

Ejecuta el script cretalinks.sh:

./createlinks.sh

Veras una salida como la siguiente:

Elimina el script de asterisk para que no arranque automáticamente cuando inicie el sistema, desmonta la partición y marca el nodo como secundario:

chkconfig --del asterisk
umount /mnt/asterisk
drbdadm secondary asterisk

 

Paso 9 —- Realizar solo en Servidor Secundario

Cambiate al directorio de rseries, marca el nodo como primario, monta la partición y ejecuta el script createlinks.sh:

cd /usr/src/asterisk/rseries-1.0.0
drbdadm primary asterisk
mount -t ext3 /dev/drbd0 /mnt/asterisk/
./createlinks.sh

Veras una salida como en la imagen anterior.

Cambiate al directorio de Asterisk, configura las mismas opciones que se configuraron en el servidor primario. Compilalo solo ejecutando make && make install. Desmonta la partición y marca el nodo como secundario.

cd ../certified-asterisk-1.8.11-cert9
contrib/scripts/get_mp3_source.sh
./configure && make menuselect
make && make install
umount /mnt/asterisk/
drbdadm secondary asterisk


Paso 10 —- Realizar en Ambos Servidores.

Edita el archivo /etc/corosync/corosync.conf. Cambia la opción bindnetaddr, y las opciones memberaddr. Para este tutorial el archivo quedo de la siguiente manera:

totem {
        version: 2
        token: 3000
        token_retransmits_before_loss_const: 10
        join: 60
        consensus: 5000
        vsftype: none
        max_messages: 20
        clear_node_high_bit: yes
        secauth: off
        threads: 0
        rrp_mode: none

        interface {
                ringnumber: 0
                bindnetaddr: 10.0.1.0
                broadcast: yes
                mcastport: 5405
                member {
                        memberaddr: 10.0.1.51
                }
                member {
                        memberaddr: 10.0.1.52
                }
        }
}

aisexec {
        user:   root
        group:  root
}

logging {
        fileline: off
        to_stderr: yes
        to_logfile: no
        to_syslog: yes
        syslog_facility: daemon
        debug: off
        timestamp: on
        logger_subsys {
                subsys: AMF
                debug: off
                tags: enter|leave|trace1|trace2|trace3|trace4|trace6
        }
}

amf {
        mode: disabled
}

Inicia el servicio de Corosync y añade drbd y corosync al startup:

service corosync start
chkconfig drdb on
chkconfig corosync on

Verifica el estado del Cluster con el siguiente comando:

cat /proc/drbd

Veras una imagen como la siguiente:

Veras que se esta sincronizando la particion del cluster, también verás como Secondary/Secondary(no como en la imagen).

Si el proceso de sincronización reporta que tardará mucho tiempo puedes usar este comando para acelerar la velocidad de sincronización:

drbdsetup /dev/drbd0 syncer -r 250M

La velocidad máxima de sincronización dependerá de la velocidad de tus tarjetas de red así como la velocidad de escritura de tus discos duros. Para más información de como calcular la velocidad ve a este enlace.

Una vez que el estado sea UpToDate/UpToDate reinicia los servidores:

reboot

 

Paso 11 —- Realizar solo en  Servidor Primario.

Edita el siguiente codigo para que:

— node1 y node2. Sean los hostnames de tus servidores. En este ejemplo node1 y node2

ip bajo ClusterIP. Sea la Ip de tu Cluster, la IP flotante. En este ejemplo 10.0.1.51

cidr_mask bajo ClusterIP. Sea la mascara de tu red. en este ejemplo de 24bits(255.255.255.0)

– –host_list bajo GatewayStatus. Sea el gateway de tu red. en este ejemplo 10.0.1.1

node node1
node node2
primitive ClusterIP ocf:heartbeat:IPaddr2 
        params ip="10.0.1.50" cidr_netmask="24" 
        op monitor interval="5"
primitive drbd ocf:linbit:drbd 
      params drbd_resource="asterisk" 
      op monitor start-delay="10" interval="5"  
primitive drbd_fs ocf:heartbeat:Filesystem 
      params device="/dev/drbd0" directory="/mnt/asterisk/" fstype="ext3"
primitive mysqld lsb:mysqld
primitive Asterisk ocf:Digium:asterisk 
        op monitor interval="5"
primitive GatewayStatus ocf:pacemaker:ping 
        params host_list="10.0.1.1" multiplier="100" 
        op monitor interval="5" timeout="10"
ms drbd_ms drbd 
        meta master-max="1" master-node-max="1" clone-max="2" clone-node-max="1" notify="true"
clone GatewayStatusClone GatewayStatus
location Asterisk-with-ping Asterisk 
        rule $id="Asterisk-with-ping-rule" -inf: not_defined pingd or pingd lte 0
group mysql drbd_fs ClusterIP mysqld
colocation mysql_on_drbd inf: mysql drbd_ms:Master
order mysql_after_drbd inf: drbd_ms:promote mysql:start
colocation Everything-with-Asterisk inf: ( drbd_ms:Master )  ( ClusterIP drbd_fs )  Asterisk
order  Asterisk-after-Everything inf:   ( drbd_ms:promote ) ( ClusterIP drbd_fs )  Asterisk:start
property $id="cib-bootstrap-options" 
        cluster-infrastructure="openais" 
        expected-quorum-votes="2" 
        stonith-enabled="false" 
        no-quorum-policy="ignore"
rsc_defaults $id="rsc-options" 
        resource-stickiness="99"

Una vez que has editado el archivo Cluster_Mysql_Asterisk.cfg, actualiza la configuración de pacemaker:

crm configure load update Cluster_Mysql_Asterisk.cfg

Si todo sale bien veras una salida como la siguiente y los servicios de MySQL y Asterisk se iniciaran en el nodo primario:

Para verificar que los servicios estén corriendo puedes revisar el estado del cluster con:

crm_mon

Y verás algo así:

Demostración:


El siguiente video muestra las pruebas del cluster. Asterisk esta configurado con «static realtime» obteniendo los datos de una base de datos de MySQL. Este tutorial no cubre la cofiguración de ARA(Asterisk Realtime Architechture).

Herramientas para diagnosticar fallas:

Puedes ejecutar el siguiente comando en el nodo secundario para verificar que la configuración de pacemaker se replico:

crm configure show

Para detener los servicios del cluster:

crm configure property stop-all-resources=true

Para borrar la configuracion del cluster:

crm configure erase

Verificar el estado del cluster:

crm_mon

Verificar el estado de la sincronización y los roles de los servidores:

cat /prco/drbd

Aumentar la velocidad de sincronización:

drbdsetup /dev/drbd0 syncer -r 250M

Reparar todas las tablas de MySQL en un sistema Vicidial

28 Nov

El domingo pasado recibí el correo de un cliente: su sistema de marcación predictiva con vicidial había tenido fallas eléctricas durante el fin de semana, ocasionando que las tablas de MySQL se corrompieran y que el sistema quedara inservible (al menos lo relacionado con la marcación predictiva).

Tras cerca de una hora reparé todas las tablas usando unos comandos simples como los que siguen:

/etc/init.d/mysql stop
cd /var/lib/mysql/asterisk
myisamchk -r *.MYI
/etc/init.d/mysql start

Sin embargo, me percaté que al hacer esto, estaba solo reparando las tablas una por una. El sistema tenía 4 núcleos, por lo que era posible hacer mucho más trabajo al mismo tiempo (4 tareas a la vez) en vez de ir una por una.

El sistema terminó de manera habitual tras el paso de 1 1/2 hrs. Para evitar que tanto tiempo se perdiera en nuevas ocasiones, decidí crear un script en bash que reparara todas las tablas de manera simultánea (limitándose a 1 proceso por cada núcleo del sistema), y me quedó el código así:

#!/bin/bash

# Editar estos valores:
## CORES 		debe ser igual al número de núcleos en el sistema 
##				(revisar el comando "cat /proc/cpuinfo")
## MYSQLDBDIR	debe ser igual a la carpeta de MySQL que contiene la BD
##				que debamos reparar (default: "/var/lib/mysql/asterisk")

CORES=4
MYSQLDBDIR=/var/lib/mysql/asterisk

# Enlistar los archivos
FILES=`ls $MYSQLDBDIR/*.MYI`
RUNS=0
for i in $FILES
do
	SALIR=0
	while [ $SALIR -eq 0 ]
	do
		# Obtener la cantidad de procesos en el sistema
		PROCS=`ps -eF|grep myisamchk|egrep -v "grep"|wc -l`

		# Ejecutamos el while los procesos activos sean menor a CORES
		if [ $CORES -gt $PROCS ]
		then
			echo `date +"%X"` Revisando/Reparando tabla $i
			myisamchk -r $i &
			# Descansamos 1 segundo
			sleep 1
			SALIR=1
		else
			# Esperamos 3 segundos
			sleep 3
		fi
	done

done

# Ya se terminaron de correr todas las instrucciones de reparación,
# pero existe la posibilidad de que algunas de ellas aún estén ejecutándose.
# Revisamos para ver si hay procesos corriendo y avisar al usuario
while [ $SALIR -eq 0 ]
do
	# Obtener la cantidad de procesos en el sistema
	PROCS=`ps -eF|grep myisamchk|egrep -v "grep"|wc -l`
	if [ $PROCS -gt 0 ]
	then
		echo `date +"%X"` Hay $PROCS procesos corriendo. Esperando 10s...
		sleep 10
	else
		echo "No hay procesos corriendo"
		SALIR=1
	fi
done
echo `date +"%X"` Fin. Ya puedes reiniciar MySQL

Los siguientes pasos son algo obvios:

  1. Hay que guardar este script como un archivo checar.sh
  2. Editamos el archivo y cambiamos el número de cores de nuestro sistema, así como la ubicación de la carpeta de MySQL que contiene los archivos de la BD
  3. Darle permisos de ejecución (chmod 755 checar.sh)
  4. Ejecutarlo: ./checar.sh
El script correrá varios procesos en paralelo para revisar cada uno una tabla. Mientras más núcleos tenga nuestro procesador, más rápido el script aprovechará los recursos para crear la reparación.
Espero les sirva.
¡Suerte!

 

Advertencia: Aunque este script puede ayudarnos a reparar nuestras tablas en caso de algún problema (falla eléctrica, corrupción del sistema de archivos, fallo en el disco duro, etc) no hay nada más seguro que respaldar nuestra información periódicamente. Siempre ten a la mano copias de seguridad de aquella información crítica, ya que no podemos hacernos responsables por la pérdida de información causada por el mal uso de las directivas que se muestran en este artículo. Úsalo bajo tu propio riesgo.

Resetear/recuperar la contraseña de root de MySQL

11 Jul

Este post realmente no tiene que ver con Asterisk, pero resolviendo una consulta que me hicieron, consideré que podría ser útil para aquel que se ha visto en la necesidad de acceder a una BD de MySQL de la cual no se tiene la contraseña de root.

Los pasos a seguir son bastante sencillos (hay que ejecutar los comandos con permisos de root de Linux):

  1. Detenemos el servicio de MySQL
    [codesyntax lang=»bash»]

    /etc/init.d/mysql stop

    [/codesyntax]

  2. Iniciamos MySQL pero en modo sin privilegios
    [codesyntax lang=»bash»]mysqld_safe –skip-grant-tables &[/codesyntax]
  3. Hacemos un login a MySQL sin password
    [codesyntax lang=»bash»]mysql -u root[/codesyntax]
  4. Cambia el password (este query se ejecuta desde adentro de MySQL, al cual accedimos ya porque arrancamos sin contraseña).
    [codesyntax lang=»sql»]UPDATE mysql.user set Password = PASSWORD(‘tunuevopass‘) WHERE User=’root’;[/codesyntax]
  5. Salimos de SQL
    [codesyntax lang=»sql»]exit;[/codesyntax]
  6. Detenemos la sesión corriendo de MySQL
    [codesyntax lang=»bash»]mysqladmin shutdown[/codesyntax]
  7. Reiniciamos el servicio de MySQL
    [codesyntax lang=»bash»]/etc/init.d/mysql restart[/codesyntax]

Al re-arrancar, ya debemos poder acceder a nuestro servicio MySQL con la nueva contraseña que definimos.

¡Suerte!

Respaldando la base de datos de configuración de Elastix/FreePBX por SSH

27 Jun

Una gran ventaja que tenemos en Linux es la facilidad de crear procesos automatizados que nos ayuden a ejecutar tareas cotidianas. Para nuestros usos comunes como administrador de equipos basados en Asterisk/Elastix/FreePBX, puede ser una labor cotidiana respaldar la configuración alojada en bases de datos de MySQL.

SSH nos permite ejecutar comandos en servidores Linux remotos y traernos el resultado al mismo tiempo, por lo que resulta ideal para realizar un respaldo en un equpo distante y almacenarlo en nuestro equipo Linux loca. El comando para hacerlo sería el siguiente (asumiendo que usamos la contraseña default de MySQL en nuestro equipo remoto):

[codesyntax lang=»bash»]

ssh 192.168.1.100 "mysqldump -peLaStIx.2oo7 --databases asterisk | gzip -9" > respaldo.sql.gz

[/codesyntax]

El comando de arriba se encargará de hacer un dump de la BD asterisk. Si quisiéramos traernos también el CDR, hariamos lo siguiente:

[codesyntax lang=»bash»]

ssh 192.168.1.100 "mysqldump -peLaStIx.2oo7 --databases asterisk cdr | gzip -9" > respaldo.sql.gz

[/codesyntax]

O si quisiéramos traernos TODAS las bases de datos:

[codesyntax lang=»bash»]

ssh 192.168.1.100 "mysqldump -peLaStIx.2oo7 --all-databases | gzip -9" > respaldo.sql.gz

[/codesyntax]

Hay que tomar en cuenta que estamos asumiendo lo siguiente:

  • La IP de nuestro servidor remoto es 192.168.1.100. Hay que reemplazar esta por la IP real de nuestro equipo del que queramos copiar el respaldo
  • El password default del usuario root del MySQL remoto es eLaStIx.2oo7. Nuevamente, hay que reemplazar este por el correspondiente al servidor

Adaptando este comando podemos prácticamente hacer cualquier tipo de respaldo remoto. Si agregamos la conexión mediante llaves públicas y privadas, podemos dejar estas actividades programadas en el cron para no tener que estar proporcionando la contraseña de SSH cada vez que nos conectamos.

¡Suerte!

Una posible solución al «No service» de los teléfonos Aastra (y como mejorar el rendimiento de los reportes de llamadas)

17 Jun

Para los que nunca han hecho uso de, Aastra es una marca de telefonía con base en Ontario, Canadá. Últimamente, su crecimiento se ha dado fuertemente gracias a la prevalencia de sistemas como Elastix que se integran muy bien con sus teléfonos. Mi percepción personal de la marca desde el punto de vista del valor del producto es intermedio: no es una marca tan barata como Grandstream o Yealink, pero tampoco es una tan cara como Polycom o Cisco. Es una marca que está a muy buenos medios términos en cuanto a calidad y funcionalidades se refiere.

Un «inconveniente» que tienen sus teléfonos es que son extremadamente sensibles al retraso de paquetes cuando están en modo de stand by. Esto quiere decir que constantemente los teléfonos están enviando paquetes a Asterisk para medir el estado del servicio, y si el servidor por un momento se retrasa con la respuesta, inevitablemente veremos el mensaje de «No service» en la pantalla de los teléfonos, que es como si no estuviéramos registrados.

La solución no siempre está bien establecida porque tenemos que encontrar (y solucionar) lo que sea que esté causando este retraso. Puede ser un problema de la red (no es muy común, pero puede ser) o puede ser que el servidor de Asterisk se encuentre en un proceso que consuma muchos procesos/CPU y ocasione que se retrase para emitir una respuesta. En este caso, vamos a analizar una posible cause de este segundo escenario.

El cliente que hoy me ha reportado que sus teléfonos están perdiendo el registro frecuentemente es una agencia automotriz, que tiene alrededor de unos 140 teléfonos conectados entre Linksys 921 y diversos modelos de la serie Aastra 675x (aunque el problema solo es con los Aastra, por la razón arriba mencionada).

Indagando un poco más, observe que de pronto sus procesos de MySQL subían alredor del 80% del CPU por unos segundos y luego, bajaban a 0%. Esto quiere decir que algo se come el procesador por un instante y luego lo suelta, pudiendo ser la causa del problema que tenemos. Indagué un poco más y me doy cuenta que por default Elastix no utiliza índices en las tablas de CDR, lo cual hace que todos los reportes del detalle de llamadas tengan que «barrer» todos los registros de la tabla para encontrar los que necesitamos. Para un PBX pequeño no hay mucho problema, pero para una tabla que tiene 3.6 millones de registros, buscar en todos ellos resulta algo de uso intensivo de CPU. Para corroborar mi teoria, activé el log de consultas lentas de MySQL, y al hacer un mysqldumpslow desde el Linux CLI, obtuve lo siguiente (no se fijen en el query, fíjense en el tiempo total para ejecutarlo):

[codesyntax lang=»bash»]

[root@100 asterisk]# mysqldumpslow -t 5 /var/log/mysql/mysql-slow.log

Reading mysql slow query log from /var/log/mysql/mysql-slow.log
Count: 14  Time=40.50s (567s)  Lock=0.00s (0s)  Rows=58.6 (820), asteriskuser[asteriskuser]@[XXX.XXX.XXX.XXX]
  SELECT  * from (SELECT  billsec as billsec ,calldate as calldate,clid as clid,src as src,dst as dst,dcontext as dcontext,channel as channel,dstchannel as dstchannel,lastapp as lastapp,lastdataas lastdata,duration as duration,disposition as disposition,amaflags as amaflags,accountcode as accountcode,uniqueid as uniqueid,userfield as userfield, calldate + INTERVAL duration SECOND as fecha_termino from cdr where calldate >= 'S') as resultado where fecha_termino >='S'order by calldate limit N, N

[/codesyntax]

Como se observa, cada consulta toma 40 segundos. ¡Esto es un mar de tiempo solo para ver las llamadas que ha hecho una extensión el día de hoy! El problema se confirma con un EXPLAIN del mismo query:

[codesyntax lang=»sql»]

mysql> EXPLAIN SELECT  * FROM ( SELECT  billsec AS billsec ,calldate AS calldate,clid AS clid,src AS src,dst AS dst,dcontext AS dcontext,channel  AS channel,dstchannel AS dstchannel,lastapp AS lastapp,lastdata AS lastdata,duration AS duration, disposition AS disposition,amaflags AS amaflags,accountcode AS accountcode,uniqueid AS uniqueid,userfield AS userfield,  calldate + INTERVAL duration SECOND AS fecha_termino FROM cdr  WHERE calldate >= ‘2011-01-01 00:00:01’)  AS resultado  WHERE fecha_termino >=’2011/06/17 16:44:23 ‘ORDER BY calldate LIMIT 0, 10000;

+----+-------------+------------+------+---------------+------+---------+------+---------+-----------------------------+
| id | select_type | table      | type | possible_keys | key  | key_len | ref  | rows    | Extra
                   |
+----+-------------+------------+------+---------------+------+---------+------+---------+-----------------------------+
|  1 | PRIMARY     |            | ALL  | NULL          | NULL | NULL    | NULL |  592484 | Using where; Using filesort |
|  2 | DERIVED     | cdr        | ALL  | NULL          | NULL | NULL    | NULL | 3617505 | Using where                 |
+----+-------------+------------+------+---------------+------+---------+------+---------+-----------------------------+
2 rows in set (27.90 sec)

[/codesyntax]

Allí se nota como la consulta NO usa índices y tiene que recorrer los 3.6M de registros. ¿La solución? Agregar un índice al campo calldate que es donde se hacen las consultas de manera principal. Esto se hace con el siquiente comando en MySQL (ojo: dependiendo del tamaño de la tabla de CDR este proceso puede demorar desde segundos hasta horas, por lo que planea muy bien que tu equipo esté disponible para ejecutar esta tarea)

[codesyntax lang=»sql»]

ALTER TABLE `asteriskcdrdb`.`cdr` ADD INDEX `calldate` (`calldate`);

[/codesyntax]

Y al terminar el proceso, podemos validar ejecutando el mismo EXPLAIN:

[codesyntax lang=»sql»]

+----+-------------+------------+-------+---------------+----------+---------+------+--------+-----------------------------+
| id | select_type | table      | type  | possible_keys | key      | key_len | ref  | rows   | Extra|
+----+-------------+------------+-------+---------------+----------+---------+------+--------+-----------------------------+
|  1 | PRIMARY     |            | ALL   | NULL          | NULL     | NULL    | NULL | 592516 | Using where; Using filesort |
|  2 | DERIVED     | cdr        | range | calldate      | calldate | 8       | NULL | 652745 | Using where                 |
+----+-------------+------------+-------+---------------+----------+---------+------+--------+-----------------------------+
2 rows in set (8.84 sec)

[/codesyntax]

Como se observa, la consulta ahora está reducida a 650K registros o bien un 82% de ahorro en cuanto al número de registros que se tienen que consultar.

Aunque esta puede bien no ser la única cause del problema, es un excelente primer paso. Ya ahorramos procesamiento en este ejercicio, ahora hay que indagar en que otros procesos del sistema podemos ahorrarnos unos ciclos para así dejar más recursos disponibles para nuestras llamadas.

Suerte,