Cuando trabajaba instalando y configurando Asterisk de mis clientes, más tarde o temprano te encuentras con el mismo problema: un solo servidor de Asterisk no escala bien. Esto es algo que llevamos viendo desde siempre. Las llamadas se acumulan, la CPU se dispara con las llamadas (más aún si hace grabaciones o peor aún si hace trascoding), y cualquier reinicio para aplicar un cambio de configuración implica cortar el servicio. La solución clásica es poner varios Asterisk en paralelo y poner algo delante que reparta la carga. Ese «algo» es un SIP proxy: Kamailio, y la herramienta que lo hace posible es el módulo dispatcher.
Llevo varios años trabajando con este módulo en producción para repartir llamadas entre varios Asterisk, o entre varios proveedores (así sean llamadas entrantes o salientes) y este módulo es uno de los que hacen esa «magia» que tanto nos gusta a los que trabajamos a diario con Kamailio. Así que he optado por hacer un tutorial que explique cómo instalar Kamailio en Debian/Ubuntu, configurar el módulo dispatcher y enrutar las llamadas SIP hacia distintos servidores Asterisk en función del usuario que origina la llamada. No es magia, es lógica de enrutamiento SIP bien configurada.
La arquitectura que vamos a montar
El escenario es simple y funcional:
- Kamailio actúa como proxy SIP. Los terminales (softphones, teléfonos IP, troncales SIP) se registran contra Kamailio y Kamailio decide a qué Asterisk mandar cada llamada.
- Asterisk A (192.168.1.10) y Asterisk B (192.168.1.20) son los servidores de media. Aquí vive la lógica de negocio: IVRs, colas, extensiones, etc.
- Importante!: Kamailio no toca el audio. Es un proxy de señalización pura. El RTP va directo entre el terminal y Asterisk.
Esto puede dar problemas con terminales detrás de un router con CG-NAT ya que estos routers suelen cerrar puertos muy rápidamente y nos encontraríamos con problemas de audio en un único sentido, pero por lo general, con este escenario debería funcionar todo correctamente.
El módulo dispatcher de Kamailio mantiene un conjunto de destinos (los Asterisk) y los distribuye según distintos algoritmos: round-robin, hash del usuario, carga activa… En nuestro caso usaremos hash del usuario (algoritmo «3»), lo que garantiza que un mismo usuario siempre va al mismo Asterisk mientras ambos estén disponibles. Esto es importante para que el estado de los canales, los parqueos de llamada y los grupos de captura sean coherentes.
Requisitos previos
- Un servidor con Debian 12 o Ubuntu 22.04/24.04 limpio para Kamailio
- Dos servidores Asterisk funcionando (o al menos accesibles por red)
- Acceso root o sudo en todos los servidores
- Los puertos UDP 5060 abiertos entre Kamailio y los Asterisk
1. Instalación de Kamailio
Usamos los repositorios oficiales de Kamailio. No instales el paquete del repositorio base de Debian/Ubuntu: suele ir muy atrasado de versión.
# Añadimos el repositorio oficial de Kamailio (versión 5.8)
curl -fsSL https://deb.kamailio.org/kamailiodebkey.gpg | gpg --dearmor -o /usr/share/keyrings/kamailio.gpg
echo "deb [signed-by=/usr/share/keyrings/kamailio.gpg] http://deb.kamailio.org/kamailio58 $(lsb_release -cs) main" \
> /etc/apt/sources.list.d/kamailio.list
apt update
apt install -y kamailio kamailio-mysql-modules kamailio-utils-modulesAdemás de los módulos base, instalamos kamailio-utils-modules porque incluye el módulo dispatcher que es el protagonista de este tutorial.
Comprobamos que Kamailio arranca:
systemctl enable kamailio
systemctl start kamailio
systemctl status kamailio2. Configuración básica de Kamailio
El fichero de configuración principal es /etc/kamailio/kamailio.cfg. Es un fichero largo e intimidante si no lo has visto antes, pero no te preocupes: vamos a construirlo desde cero con lo mínimo necesario para que funcione el balanceo.
Antes de editar el cfg, ajustamos los parámetros globales en /etc/default/kamailio:
MEMORY=128
SHM_MEMORY=256
CFGFILE=/etc/kamailio/kamailio.cfg
USER=kamailio
GROUP=kamailio
DUMP_CORE=noAhora el fichero principal. Reemplaza el contenido de /etc/kamailio/kamailio.cfg con lo siguiente:
#!KAMAILIO
##############################################
# Parámetros globales
##############################################
debug=2
log_stderror=no
log_facility=LOG_LOCAL0
fork=yes
children=4
port=5060
listen=udp:0.0.0.0:5060
##############################################
# Módulos a cargar
##############################################
loadmodule "tm.so"
loadmodule "sl.so"
loadmodule "rr.so"
loadmodule "pv.so"
loadmodule "maxfwd.so"
loadmodule "textops.so"
loadmodule "siputils.so"
loadmodule "xlog.so"
loadmodule "sanity.so"
loadmodule "dispatcher.so"
##############################################
# Parámetros de módulos
##############################################
# tm - Transaction Manager
modparam("tm", "failure_reply_mode", 3)
modparam("tm", "fr_timer", 30000)
modparam("tm", "fr_inv_timer", 120000)
# rr - Record-Route
modparam("rr", "enable_full_lr", 1)
# dispatcher - el módulo clave
modparam("dispatcher", "list_file", "/etc/kamailio/dispatcher.list")
modparam("dispatcher", "flags", 2)
modparam("dispatcher", "dst_avp", "$avp(ds_dst)")
modparam("dispatcher", "grp_avp", "$avp(ds_grp)")
modparam("dispatcher", "cnt_avp", "$avp(ds_cnt)")
modparam("dispatcher", "sock_avp", "$avp(ds_sock)")
# Comprobación de disponibilidad de los Asterisk cada 30 segundos
modparam("dispatcher", "ds_ping_interval", 30)
modparam("dispatcher", "ds_probing_mode", 1)
modparam("dispatcher", "ds_ping_from", "sip:kamailio@tudominio.com")
##############################################
# Lógica de enrutamiento
##############################################
request_route {
# Comprobaciones básicas de sanidad SIP
if (!mf_process_maxfwd_header(10)) {
sl_send_reply("483", "Too Many Hops");
exit;
}
if (!sanity_check()) {
exit;
}
# Gestionamos los ACK de transacciones existentes
if (is_method("ACK")) {
if (t_check_trans()) {
t_relay();
}
exit;
}
# Gestionamos los CANCEL
if (is_method("CANCEL")) {
if (t_check_trans()) {
t_relay();
}
exit;
}
# Añadimos Record-Route para mantener el diálogo
if (is_method("INVITE|SUBSCRIBE")) {
record_route();
}
# Si ya tiene Route header, seguimos la ruta existente
if (loose_route()) {
route(RELAY);
exit;
}
# Enrutamiento de INVITE hacia los Asterisk
if (is_method("INVITE")) {
route(DISPATCH);
exit;
}
# El resto de métodos: OPTIONS, REGISTER, etc.
route(RELAY);
}
route[DISPATCH] {
# Seleccionamos destino usando hash del usuario (algoritmo 4)
# El grupo 1 es el que definimos en dispatcher.list
if (!ds_select_dst(1, 4)) {
xlog("L_ERR", "No hay destinos disponibles en el grupo 1\n");
sl_send_reply("503", "Service Unavailable");
exit;
}
xlog("L_INFO", "Enviando llamada de $fu hacia $du (dispatcher)\n");
# Configuramos failover: si falla el primer Asterisk, intenta con el siguiente
t_on_failure("MANAGE_FAILURE");
route(RELAY);
}
route[RELAY] {
if (!t_relay()) {
sl_reply_error();
}
exit;
}
failure_route[MANAGE_FAILURE] {
if (t_is_canceled()) {
exit;
}
# Si el destino falla (408, 5xx, 6xx), marcamos como inactivo e intentamos otro
if (t_check_status("408|5[0-9][0-9]|6[0-9][0-9]")) {
ds_mark_dst("ip"); # marca como inactivo y prueba el siguiente
if (!ds_next_dst()) {
xlog("L_ERR", "No hay más destinos disponibles. Fallando la llamada.\n");
t_reply("503", "All destinations failed");
exit;
}
route(RELAY);
}
}
3. El fichero dispatcher.list: aquí defines tus Asterisk
Este es el fichero más importante para el balanceo. Cada línea define un destino con el formato:
GRUPO URI FLAGS PRIORIDAD ATRIBUTOS DESCRIPCIONCrea el fichero /etc/kamailio/dispatcher.list con tus servidores Asterisk:
# /etc/kamailio/dispatcher.list
# Formato: setid uri flags priority attrs description
# Grupo 1: Asterisk de producción
1 sip:192.168.1.10:5060 0 0 weight=50 Asterisk-A
1 sip:192.168.1.20:5060 0 0 weight=50 Asterisk-BEl número de grupo (1 en este caso) es el que usamos en la función ds_select_dst(1, 3) del routing. Puedes tener varios grupos para distintos propósitos: un grupo para llamadas entrantes, otro para llamadas salientes, otro para usuarios premium, etc.
El algoritmo 3 hace hash del Request-URI (el usuario al que se llama). Así, todas las llamadas hacia el mismo destino van siempre al mismo Asterisk. Si prefieres hacer hash del From (el usuario que llama), usa el algoritmo 1, que hashea el Call-ID y es más uniforme. Para round-robin simple usa el algoritmo 4.
Después de modificar el fichero, recarga el dispatcher sin reiniciar Kamailio:
kamctl dispatcher reloadO bien usando el RPC de Kamailio:
kamcmd dispatcher.reload4. Configuración de Asterisk para aceptar conexiones de Kamailio
Los Asterisk tienen que aceptar las llamadas que vienen de la IP de Kamailio. En PJSIP (/etc/asterisk/pjsip.conf), crea un endpoint para Kamailio:
[transport-udp]
type=transport
protocol=udp
bind=0.0.0.0:5060
[kamailio]
type=endpoint
context=desde-kamailio
disallow=all
allow=alaw,ulaw
direct_media=no
trust_id_inbound=yes
from_domain=tudominio.com
[kamailio]
type=identify
endpoint=kamailio
match=192.168.1.1 ; IP de tu Kamailio
[kamailio]
type=aor
max_contacts=1El parámetro direct_media=no es crítico. Le dice a Asterisk que no intente negociar media directo entre terminales, porque Kamailio no va a estar en medio del RTP. Si lo pones a yes y los terminales están detrás de NAT, el audio no funcionará.
En el dialplan (/etc/asterisk/extensions.conf), crea el contexto que recibe las llamadas:
[desde-kamailio]
exten => _X.,1,NoOp(Llamada recibida desde Kamailio para EXTEN_VAR)
same => n,Dial(PJSIP/EXTEN_VAR,30)
same => n,Hangup()(Nota: sustituye EXTEN_VAR por ${EXTEN} en tu fichero real.)
5. Verificar que el dispatcher funciona
Una vez que todo está arrancado, puedes ver el estado de los destinos del dispatcher con:
kamcmd dispatcher.listLa salida te muestra algo así:
DISPATCHER SETS[1]:
SET[0]:
ID: 1
DESTS[2]:
DEST[0]: URI=sip:192.168.1.10:5060 FLAGS=AP PRIORITY=0
DEST[1]: URI=sip:192.168.1.20:5060 FLAGS=AP PRIORITY=0El campo FLAGS es el que más información te da:
- A (Active): el destino está activo y recibiendo llamadas
- P (Probing): Kamailio está enviando OPTIONS periódicos para verificar disponibilidad
- I (Inactive): el destino no responde y ha sido marcado como caído
- D (Disabled): el destino está deshabilitado manualmente
Si quieres poner un Asterisk en mantenimiento sin cortar las llamadas en curso, puedes deshabilitarlo manualmente:
# Deshabilitar el destino sip:192.168.1.20:5060 del grupo 1
kamcmd dispatcher.set_state ip 1 sip:192.168.1.20:5060Y para volver a habilitarlo:
kamcmd dispatcher.set_state ap 1 sip:192.168.1.20:50606. Depuración y logs
Cuando algo no funciona, el primer sitio donde mirar es el syslog de Kamailio:
tail -f /var/log/syslog | grep kamailioSi quieres ver el SIP en crudo para entender qué está pasando con la señalización, usa sngrep:
apt install -y sngrep
sngrep -d eth0 port 5060sngrep es una herramienta imprescindible para depurar SIP. Muestra los diálogos SIP de forma visual en la terminal, con colores y filtros, y es infinitamente más cómodo que hacer un tcpdump y analizar el pcap a mano.
Para activar el debug del dispatcher específicamente, puedes bajar el nivel de log de Kamailio temporalmente:
# Bajar el nivel de debug en caliente (sin reiniciar)
kamcmd cfg.set_now_int core debug 4Y volver al nivel normal cuando termines:
kamcmd cfg.set_now_int core debug 2Y con esto y un bizcocho…
Con esta configuración tienes un proxy SIP funcional que balancea llamadas entre varios Asterisk usando hash del usuario, con detección automática de caídas y failover transparente. Es la base sobre la que construir una arquitectura de telefonía seria.
Hay varias cosas que quedan fuera del alcance de este tutorial pero que son el siguiente paso natural:
- Registro de usuarios: para que los terminales se registren contra Kamailio y este sepa dónde está cada usuario, necesitas el módulo
registrary una base de datos (normalmente MySQL conkamailio-mysql-modules). - Autenticación: el módulo
authyauth_dbpara que los terminales autentiquen contra Kamailio. - NAT traversal: el módulo
nathelpery un servidor STUN o un mediaproxy (RTPengine, rtpproxy) para los clientes detrás de NAT. - TLS y SRTP: para cifrar la señalización y el audio.
El dispatcher es solo uno de los cientos de módulos que viene con Kamailio, pero es uno de los que más partido te da en un entorno que trabaje con varios Asterisk. Si lo manejas bien, tendrás la base para construir lo que necesites encima.


¡Inicia la conversación!
Sé el primero en compartir tu opinión. Tu comentario puede ayudar a otros.