OpenSIPs 3.1 - Alta Disponibilidad con Keepalived

En este extenso articulo, veremos como configurar la alta disponibilidad en OpenSIPs 3.1 a través de Keepalived y el modulo CLUSTERER de OpenSIPs. El documento es parte del curso de OpenSIPs Cluster y no incluye la parte dedicada a la configuración del alta disponibilidad de la base de datos que se puede realizar utilizando el servicio RDS de Amazon o una configuración Primario/Primario de MariaDB. En el mismo documento es presente también una configuración de alta disponibilidad con corosync, pacemaker, redis, para que las llamadas activas pasen de un servidor a otro en unos 5 segundos.

------------------------------

Keepalived es un programa de enrutamiento cuya finalidad es brindar la posibilidad de configurar entornos de balanceo de carga y/o alta disponibilidad de una manera relativamente sencilla y al mismo tiempo eficaz. Para el balanceo de carga se apoya en un modulo del Kernel de Linux llamado Linux Virtual Server (IPVS), para la alta disponibilidad utiliza el protocolo VRRP (RFC5798) que permite a los servidores conocer el estado de los demás utilizando el protocolo Multicast o Unicast. En nuestro caso, con Linode, utilizaremos Unicast. Empezamos con la descarga de las fuentes de la ultima versión disponible y su compilación:

cd /usr/src/

wget https://www.keepalived.org/software/keepalived-2.1.5.tar.gz

tar -xf keepalived-2.1.5.tar.gz

cd keepalived-2.1.5

./configure --prefix=/

make

make install

Una vez compilado e instalado empezamos con su configuración. El archivo principal es keepalived.conf. Vamos a trabajar un servidor a la vez.

Servidor 1

Primero movemos el archivo de configuración predefinido:

mv /etc/keepalived/keepalived.conf /etc/keepalived/keepalived.conf.old

Luego creamos uno nuevo:

nano /etc/keepalived/keepalived.conf

Donde copiamos todas las lineas que siguen:

vrrp_script check_sip {

script "/etc/keepalived/node01.sh"

interval 3

fall 2

rise 2

}

vrrp_instance VI_OS {

state BACKUP

interface eth0

virtual_router_id 1

advert_int 1

notify /etc/keepalived/opensips.sh

priority 250

nopreempt

unicast_src_ip 192.168.190.191

unicast_peer {

192.168.177.49

}

virtual_ipaddress {

173.255.195.128 dev eth0

}

track_script {

check_sip

}

}

En el primer bloque se configura uno script que se ejecutará cada 3 segundos para averiguar el estado de OpenSIPs. Si el resultado será negativo por 2 veces consecutivas, la IP virtual pasará al segundo servidor, con dos resultados positivos la IP virtual volverá a pasar a este servidor. En el segundo bloque se configura la instancia y se le asigna un nombre. Ambos servidores se iniciarán como BACKUP y si los dos funcionan correctamente, el que tenga la prioridad más alta, será el servidor que pasará a Primario, la IP virtual se configurará sobre la interfaz eth0; virtual_router_id y advert_int tendrán que ser iguales en ambos servidores porque identifican los servidores que son parte de la instancia. Cada vez que se presente un cambio de estado, se ejecutará el script opensips.sh. El parámetro nopreempt nos permite el siguiente comportamiento: si el OpenSIPs principal se cae y entra en función el servidor de respaldo, cuando el OpenSIPs principal vuelva a funcionar, el servidor de respaldo seguirá siendo el Primario; esta configuración se realiza para evitar que haya continuos cambios cuando el funcionamiento del servidor principal se vuelve intermitente. Como se va a trabajar en UNICAST, en el bloque que inicia con unicast_src_ip se indica la configuración unicast: la primera IP es la IP local del Servidor 1 y la segunda del Servidor 2. Al final del bloque aparece la IP Virtual compartida entre los dos servidores. En el ultimo bloque se define el script que se utilizará para monitorear el estado de la instancia cuyo nombre es el mismo definido en la primera linea del primer bloque. Se continua con el script node01.sh:

nano /etc/keepalived/node01.sh

donde se copian las siguientes lineas:

#!/bin/bash

node01=198.58.124.115

node02=198.58.103.43

return_code=0

timeout 2 sipsak -s sip:$node01:5060

exit_status=$?

if [[ $exit_status -eq 0 ]]; then

echo "sip ping successful to node01 [$node01]"

exit $return_code

fi

timeout 2 sipsak -s sip:$node02:5060

exit_status=$?

if [[ $exit_status -eq 0 ]]; then

echo "sip ping successful to node02 [$node02]"

return_code=1

fi

echo "return code [$return_code]"

exit $return_code

Con este script, gracias al uso del programa SIPSAK, que luego se instalará, se controla el estado de los dos servidores OpenSIPs. Modificar la primera y la segunda linea con la IP publica del Servidor 1 y la IP publica del Servidor 2 respectivamente. El script, cada vez que se ejecute, devolverá un valor que se utilizará para conocer el estado del servicio: 0 OK, 1 NOOK. La lógica sería: si la IP 198.58.124.115 contesta correctamente el OPTIONS enviado a través de SIPSAK, se devuelve a KeepAlived el valor 0 que es considerado un valor positivo; si la primera IP no contesta correctamente, se envía el SIP OPTIONS a la segunda IP 198.58.103.43; si la segunda IP contesta correctamente, se devuelve a KeepAlived el valor 1 que es considerada una respuesta negativa pues el sistema pasará a estado FAULT y la IP Virtual pasará al segundo servidor y ahí se quedará aunque el Servidor 1 o servidor principal vuelva a funcionar correctamente. Se vuelve ejecutable

chmod +x /etc/keepalived/node01.sh

Se termina con el ultimo script:

nano /etc/keepalived/opensips.sh

donde se copian las lineas que siguen:

#!/bin/bash

TYPE=$1

NAME=$2

STATE=$3

case $STATE in

"MASTER") /usr/local/bin/opensips-cli -x mi clusterer_shtag_set_active vip/3

/usr/local/bin/opensips-cli -x mi nh_enable_ping 1

exit 0

;;

"BACKUP") /usr/local/bin/opensips-cli -x mi clusterer_list_shtags

/usr/local/bin/opensips-cli -x mi nh_enable_ping 0

exit 0

;;

"FAULT") /usr/local/bin/opensips-cli -x mi clusterer_list_shtags

/usr/local/bin/opensips-cli -x mi nh_enable_ping 0

exit 0

;;

*) echo "unknown state"

exit 1

;;

esac

Cada vez que haya un cambio de estado detectado por KeepAlived, se ejecutará este script. Los tres distintos estados son:

  • MASTER

  • BACKUP

  • FAULT

Si el servidor se vuelve Primario tendremos que modificar a nivel de OpenSIPs dos cosas. El tag que luego configuraremos como parámetro en el modulo CLUSTERER y que define quien es, en ese momento, el nodo activo en un escenario de dos nodos Primario/Replica; el servidor que se hará cargo de enviar las solicitudes OPTIONS a los dispositivos registrados. En el caso de estado BACKUP o FAULT diremos a OpenSIPs que deje de enviar los OPTIONS a los dispositivos registrados porque de eso se hará cargo el servidor Primario. Se vuelve ejecutable el archivo opensips.sh

chmod +x /etc/keepalived/opensips.sh

Ya se puede pasar al Servidor 2; primero movemos el archivo de configuración predefinido:

mv /etc/keepalived/keepalived.conf /etc/keepalived/keepalived.conf.old

Luego creamos uno nuevo:

nano /etc/keepalived/keepalived.conf

Donde copiamos todas las lineas que siguen:

vrrp_script check_sip {

script "/etc/keepalived/node02.sh"

interval 3

fall 2

rise 2

}

vrrp_instance VI_OS {

state BACKUP

interface eth0

virtual_router_id 1

advert_int 1

notify /etc/keepalived/opensips.sh

priority 200

nopreempt

unicast_src_ip 192.168.177.49

unicast_peer {

192.168.190.191

}

virtual_ipaddress {

173.255.195.128 dev eth0

}

track_script {

check_sip

}

}

¿Qué cambia? El nombre del script presente en el primer bloque, la prioridad que será más baja para que el Servidor 1 sea el Primario cuando se inicien los servicios; el bloque del unicast donde las IP locales están invertidas, es decir, primero la IP local del Servidor 2 y luego la IP local del Servidor 1. Se guardan los cambios y se pasa a:

nano /etc/keepalived/node02.sh

donde se copian las lineas que siguen:

#!/bin/bash

node01=198.58.103.43

node02=198.58.124.115

return_code=0

timeout 2 sipsak -s sip:$node01:5060

exit_status=$?

if [[ $exit_status -eq 0 ]]; then

echo "sip ping successful to node01 [$node01]"

exit $return_code

fi

timeout 2 sipsak -s sip:$node02:5060

exit_status=$?

if [[ $exit_status -eq 0 ]]; then

echo "sip ping successful to node02 [$node02]"

return_code=1

fi

echo "return code [$return_code]"

exit $return_code

donde lo único que cambia es el orden de las dos IP Publicas; primero la IP publica del Servidor 2 y luego la IP publica del Servidor 1. Se guardan los cambios se vuelve ejecutable el script:

chmod +x /etc/keepalived/node02.sh

Luego se termina con:

nano /etc/keepalived/opensips.sh

donde se copian las siguientes lineas:

#!/bin/bash

TYPE=$1

NAME=$2

STATE=$3

case $STATE in

"MASTER") /usr/local/bin/opensips-cli -x mi clusterer_shtag_set_active vip/3

/usr/local/bin/opensips-cli-x mi nh_enable_ping 1

exit 0

;;

"BACKUP") /usr/local/bin/opensips-cli -x mi clusterer_list_shtags

/usr/local/bin/opensips-cli-x mi nh_enable_ping 0

exit 0

;;

"FAULT") /usr/local/bin/opensips-cli -x mi clusterer_list_shtags

/usr/local/bin/opensips-cli -x mi nh_enable_ping 0

exit 0

;;

*) echo "unknown state"

exit 1

;;

esac

Este archivo es exactamente igual al archivo del Servidor 1. Se vuelve ejecutable el archivo opensips.sh:

chmod +x /etc/keepalived/opensips.sh

Se termina con algunos comandos que hay que ejecutar en ambos servidores; se añade el usuario keepalived_script requerido por KeepAlived:

useradd -g users -M keepalived_script

Se modifica la configuración de las opciones de KeepAlived para tener un archivo de LOG dedicado y para indicar donde se encuentra el archivo de configuración del programa:

nano /etc/sysconfig/keepalived

Cambiando:

KEEPALIVED_OPTIONS="-D"

con:

KEEPALIVED_OPTIONS="-f /etc/keepalived/keepalived.conf -D --log-facility=3"

Se añade un archivo de configuración para el LOG de Keepalived en Rsyslog:

nano /etc/rsyslog.d/keepalived.conf

Se añaden al final del archivo las siguientes lineas:

:programname, startswith, "Keepalived" -/var/log/keepalived.log

& ~

Se guardan los cambios y se reinicia Rsyslog:

systemctl restart rsyslog

y se habilita el arranque de KeepAlived cada vez que se reinicie el sistema:

systemctl enable keepalived

Ahora hay que instalar SIPSAK que es el programa que se utilizará en los script node01.sh y node02.sh para monitorear los dos servidores OpenSIPs.

 

SIPSAK

Como SIPSAK es uno de los programas que se bloquean por defecto en el cortafuegos, para que los dos servidores puedan comunicarse utilizando ese programa, hay que realizar un cambio en la configuración de IPTables de ambos:

Servidor 1:

nano /etc/sysconfig/iptables

antes de esta linea:

-A INPUT -p udp -m udp --dport 5060 -m string --string "sipsak" --algo bm --to 65535 -j DROP

se añade:

-A INPUT -p udp -m udp --dport 5060 -m string --string "sipsak" --algo bm --to 65535 -s 198.58.103.43 -j ACCEPT

Cambiar la IP publica que aparece con la IP Publica de su Servidor 2. Se guardan los cambios y se reinicia IPtables:

systemctl restart iptables

Servidor 2:

nano /etc/sysconfig/iptables

antes de esta linea:

-A INPUT -p udp -m udp --dport 5060 -m string --string "sipsak" --algo bm --to 65535 -j DROP

se añade:

-A INPUT -p udp -m udp --dport 5060 -m string --string "sipsak" --algo bm --to 65535 -s 198.58.124.115 -j ACCEPT

Cambiar la IP publica que aparece con la IP Publica de su Servidor 1. Se guardan los cambios y se reinicia IPtables:

systemctl restart iptables

Ya se puede instalar SIPSAK (en ambos servidores):

cd /usr/src/

git clone https://github.com/nils-ohlmeier/sipsak.git

cd sipsak

autoreconf --install

./configure

make

cp sipsak /usr/local/bin/

Ahora que hemos terminado con la configuración de KeepAlived y de los programas utilizados por éste, podemos pasar a la configuración de OpenSIPs. Lo primero que tenemos que hacer es añadir dos nuevas entradas a la tabla clusterer; esto porque para compartir los diálogos entre los dos servidores OpenSIPs, necesitamos que en la columna flags el Servidor 1 tenga el valor seed, que indica que es el servidor que se encargará de propagar los diálogos en el Cluster, y el Servidor 2 NULL, che indica que al iniciarse buscará un servidor seed para sincronizarse en el Cluster. Asignaremos estas dos nuevas entradas a un cluster_id con numero 3. Modificar la primera IP y la segunda IP con las IP publicas de su primer y de su segundo servidor respectivamente:

mysql

MariaDB [(none)]> use opensips

MariaDB [opensips]> INSERT INTO clusterer(id, cluster_id, node_id, url, state, no_ping_retries, priority, flags,description) VALUES \

(NULL, 3,1,'bin:198.58.124.115',1,3,1,'seed','dialog1'), \

(NULL, 3,2,'bin:198.58.103.43',1,3,1,'NULL','dialog2');

MariaDB [opensips]> quit

Bye

En el archivo de configuración del Proxy SIP se añadirá la IP compartida, se añadirá el modulo OPTIONS, para que openSIPs pueda contestar correctamente los OPTIONS recibidos desde SIPSAK, se modificará la configuración de los módulos CLUSTERER, USRLOC y DIALOG como del script de enrutamiento.

oscfg

Antes de esta linea (37):

socket=udp:IPPublica:5060

se añade:

socket=udp:173.255.195.128:5060

173.255.195.128 es la IP Virtual que tengo asociada a mis dos servidores. Después de este bloque (desde la linea 47):

#set module path

mpath="//lib64/opensips/modules/"

Se añade:

#### OPTIONS module

loadmodule "options.so"

En el modulo CLUSTERER tenemos que añadir una bandera que se utiliza para definir de manera predefinida el servidor Primario y el servidor Replica; de hecho el valor de este parámetro cambia en cada servidor. Al final de este bloque (desde la linea 95):

#### CLUSTERER

loadmodule "clusterer.so"

modparam("clusterer", "my_node_id", 2)

modparam("clusterer", "db_mode", 1)

modparam("clusterer", "ping_interval", 2)

modparam("clusterer", "ping_timeout", 1000)

modparam("clusterer", "node_timeout", 10)

modparam("clusterer", "db_url","mysql://opensips:594udcqPHdPmvQGK@localhost/opensips")

añadimos, Servidor 1:

modparam("clusterer", "sharing_tag", "vip/3=active")

Servidor 2:

modparam("clusterer", "sharing_tag", "vip/3=backup")

el servidor con la bandera active se hará cargo de gestionar toda la señalización de los módulos que necesiten conocer el estado del Proxy SIP. Si ese servidor se cae, el servidor de respaldo, a través del comando presente en el archivo opensips.sh, tomará la bandera active y sustituirá el servidor que se ha caído. La sintaxis del valor que acepta el parámetro es: etiqueta/IDcluster=active o backup.

Como ya no necesitamos utilizar el Federated User Location Cluster, modificamos la configuración del modulo USRLOC (desde la linea 104):

#### USRLOC module

loadmodule "usrloc.so"

modparam("usrloc", "nat_bflag", "NAT")

modparam("usrloc", "location_cluster", 1)

modparam("usrloc", "working_mode_preset","federation-cachedb-cluster")

modparam("usrloc", "cachedb_url", "mongodb://45.77.199.46:27017/opensipsDB.userlocation")

modparam("usrloc", "db_url", "mysql://opensips:594udcqPHdPmvQGK@localhost/opensips")

para que quede:

#### USRLOC module

loadmodule "usrloc.so"

modparam("usrloc", "nat_bflag", "NAT")

modparam("usrloc", "working_mode_preset","sql-only")

modparam("usrloc", "db_url", "mysql://opensips:594udcqPHdPmvQGK@localhost/opensips")

En el modulo DIALOG añadimos dos lineas relacionadas con la configuración Cluster, modificando el bloque (desde la linea 169):

#### DIALOG module

loadmodule "dialog.so"

modparam("dialog", "dlg_match_mode", 1)

modparam("dialog", "default_timeout", 21600)

modparam("dialog", "db_mode", 2)

modparam("dialog", "db_url","mysql://opensips:594udcqPHdPmvQGK@localhost/opensips")

para que quede:

#### DIALOG module

loadmodule "dialog.so"

modparam("dialog", "dlg_match_mode", 1)

modparam("dialog", "default_timeout", 21600)

modparam("dialog", "db_mode", 0)

modparam("dialog", "profile_replication_cluster", 2)

modparam("dialog", "dialog_replication_cluster", 3)

modparam("dialog", "db_url","mysql://opensips:594udcqPHdPmvQGK@localhost/opensips")

Cada servidor OpenSIPs conocerá en cualquier momento los diálogos activos gracias a la configuración del modulo CLUSTERER y del modulo DIALOG.

Se continua cambiando en la lineas 138 y 195, la IP Publica que aparece con la IP Virtual asignada. Se continua con el script de enrutamiento. Después de este bloque (Desde la linea 235):

if (!mf_process_maxfwd_header("10")) {

send_reply("483","Too Many Hops");

exit;

}

se añade:

if (is_method("OPTIONS") && ($si == "198.58.124.115" || $si == "198.58.103.43")) {

options_reply();

}

CAMBIAR las dos IP con la IP Publica del Servidor 1 y la IP Publica del Servidor 2 respectivamente. Esto para que los OPTIONS enviados desde SIPSAK sean contestados correctamente. Luego se modifica este bloque (Desde la linea 326):

# account only INVITEs

if (is_method("INVITE")) {

$acc_extra(llamante)=$fU;

$acc_extra(llamado)=$rU;

do_accounting("db","cdr");

route(AUTH);

if (isbflagset("NAT")) setflag("NAT");

}

para que quede:

# account only INVITEs

if (is_method("INVITE")) {

$acc_extra(llamante)=$fU;

$acc_extra(llamado)=$rU;

route(AUTH);

create_dialog();

set_dlg_sharing_tag("vip");

do_accounting("db","cdr");

if (isbflagset("NAT")) setflag("NAT");

}

Se han añadido estas dos lineas:

create_dialog();

set_dlg_sharing_tag("vip");

la primera función, activada por el modulo DIALOG, se utiliza para crear un dialogo por cada INVITE de forma que luego, ese dialogo podrá ser compartido entre los dos servidores. Con la segunda función, siempre activada por el modulo DIALOG, se añade la bandera vip a cada dialogo creado de forma que quien está gestionando las llamadas, mantenga el control sobre las mismas. Como ya en el modulo USRLOC no se va a utilizar el modulo CLUSTERER, se elimina el siguiente bloque (Desde la linea 460):

if (cluster_check_addr(1, $si)) {

xlog("L_NOTICE","$rm procedente del cluster, buscando localmente\n");

} else {

xlog("L_NOTICE","$rm externo, buscando globalmente\n");

$var(lookup_flags) = $var(lookup_flags) + "g";

}

y tambièn el siguiente bloque (Desde la linea 486):

if (cluster_check_addr("1", "$si")) {

return;

}

Se guardan los cambios. Para que OpenSIPs se pueda poner a la escucha sobre la IP Virtual sin que esté asignada realmente al servidor, hay que realizar este cambio en la configuración de Linux:

echo 1 > /proc/sys/net/ipv4/ip_nonlocal_bind

nano /etc/sysctl.d/99-sysctl.conf

al final del archivo, se añade la siguiente linea:

net.ipv4.ip_nonlocal_bind = 1

Los registros de los usuarios se realizarán sobre la IP VIRTUAL pues, como trabajamos en un escenario multidominio, hay que añadir esa IP a la lista de dominios; claramente se puede asociar un nuevo subdominio a la IP Virtual y utilizar el subdominio en lugar de la IP; MODIFICAR IPVirtual con la IP Virtual que tienen asignada:

mysql

MariaDB [(none)]> use opensips

MariaDB [opensips]> insert into domain (domain) values ('IPVirtual');

MariaDB [opensips]> quit

Se recarga la configuración del modulo DOMAIN:

opensips-cli -x mi domain_reload

"OK"

Se añaden tres nuevos usuarios (CAMBIAR IPVirtual con la IP Virtual asignada a cada uno de ustedes):

opensips-cli -x user add 1000@IPVirtual sesamo

opensips-cli -x user add 1001@IPVirtual sesamo

opensips-cli -x user add 1002@IPVirtual sesamo

Ya podemos reiniciar OpenSIPS:

systemctl restart opensips

revisar el LOG para ver si se ha presentado algún tipo de error. A nivel de Asterisk, el único cambio que tenemos que realizar es modificar la configuración del buzón de voz para añadir la nueva IP Virtual:

nano /etc/asterisk/voicemail.conf

al final del archivo añadimos (CAMBIAR IPVirtual con la IP Virtual que tienen asignada):

[IPVirtual]

1000 => 1000,Fulano,fulano@gmail.com

1001 => 1001,Fulano,fulano@gmail.com

1002 => 1002,Fulano,fulano@gmail.com

Guardamos los cambios y reiniciamos Asterisk:

systemctl restart asterisk

Terminada toda la configuración ya podemos iniciar KeepAlived en ambos servidores:

systemctl start keepalived

Si miran el LOG:

tail -50f /var/log/keepalived.log

verán que ambos servidores arrancarán en BACKUP STATE y el servidor con prioridad más alta, después de unos segundos, pasará a MASTER STATE mientras el segundo se quedará en BACKUP STATE. A partir de este momento es posible registrar los Softphone utilizando la IP Virtual:

Si registran dos, ambos se conectarán al Servidor 1 ya que ese servidor tiene la IP Virtual asignada:

ip a

Si realizan una llamada en ambos servidores, con el comando:

opensips-cli -x mi dlg_list_ctx

verán los datos del dialogo activo y entre tantas lineas:

que indica que la bandera se ha creado correctamente. Se ejecutamos este comando:

opensips-cli -x mi clusterer_list_shtags

en ambos servidores. En el Primario aparecerá:

en el BACKUP:

Si ejecutamos este comando en ambos servidores:

opensips-cli -x mi nh_enable_ping

en el Primario:

en el BACKUP:

IMPORTANTE: si por cualquier motivo los datos no corresponden, pueden configurarlos manualmente. Esto solamente la primera vez que ejecuten KeepAlived

Si a lo largo de una llamada paramos el OpenSIPs Primario, el audio de la llamada seguirá funcionando ya que RTPENGINE estará todavía activo; al mismo tiempo la gestión de la señalización de la llamada, pasará al nuevo Primario y una vez terminada podrán averiguar que en la tabla acc de la base de datos opensips, el CDR se habrá creado correctamente. Parar OpenSIPs permite realizar, por ejemplo, cualquier tipo de mantenimiento antes de volver a iniciarlo nuevamente. Cuando se reinicie OpenSIPs, el respaldo quedará siendo el Primario porque así se ha configurado en KeepAlived gracias a la opción nopreempt. Ahora que vimos como funciona la alta disponibilidad con KeepAlived, vamos a analizar las limitaciones del sistema:

  • Si se cae el servidor principal, todas las llamadas terminarán ya que se caerá también RTPEngine

  • La configuración se basa en distintos programas, scripts que vuelve la configuración un poco compleja

-----------------------------------------

Se aceptan comentarios y consejos para mejorar la documentación.

Vota el Articulo: 

Sin votos (todavía)
Evalúa la calidad del articulo
Suscribirse a Comentarios de "OpenSIPs 3.1 - Alta Disponibilidad con Keepalived" Suscribirse a VozToVoice - Todos los comentarios