Arquitectura y componentes

OMniLeads es una aplicación basada en múltiples componentes que residen en repositorios individuales de GitLab, donde se almacena el código fuente y/o de configuración, los scripts de build, deploy y pipelines CI/CD.

Si bien al momento de ejecutar una instancia de OMniLeads los componentes interactúan como una unidad a través de conexiones TCP/IP, la realidad es que cada uno es una entidad propia con su repositorio GitLab y ciclo DevOps.

A nivel de build, cada componente se distribuye a partir de RPMs (para instalaciones sobre Linux) y Docker-IMG (para instalaciones sobre Docker Engine). Con respecto al deploy, cada componente cuenta con un script first_boot_installer.tpl, con el cual se puede invocar como Provisioner y así desplegar fácilmente sobre un Linux host de manera automatizada, o bien ejecutar de manera manual editando variables sobre el script en cuestión.

Podemos pensar a cada componente como una pieza de un rompecabezas con sus atributos:

_images/arq_component.png

Descripción de cada componente

A continuación, se describe cada componente:

  • OMLApp (https://gitlab.com/omnileads/ominicontacto): La aplicación web (Python/Django) está contenida en OMLApp.

    Nginx es el webserver que recibe las peticiones HTTPS y redirecciona hacia OMLApp (Django/UWSGI) dichas solicitudes. OMLApp interactúa con varios componentes, ya sea para almacenar/aprovisionar configuración, así como también en la generación de llamadas, o a la hora de devolver vistas de reportes y supervisión de agentes/campañas.

    OMLApp utiliza PostgreSQL como motor SQL, Redis como caché y para aprovisionar la configuración de Asterisk, ya sea a través de archivos .conf así como también generando ciertas estructuras clave/valor que son consultadas por Asterisk en tiempo real a la hora de procesar llamadas sobre campañas. OMLApp se conecta a la interfaz AMI de Asterisk para generar llamadas y recargar alguna que otra configuración, también realiza conexiones hacia la API de WombatDialer cuando es necesario generar campañas con discado predictivo.

_images/arq_omlapp.png
  • Asterisk (https://gitlab.com/omnileads/omlacd): OMniLeads se basó en el framework Asterisk como base del ACD (Distribuidor Automático de Llamadas). Se encarga de la implementación de lógica de negocio (campañas telefónicas, grabaciones, reportes y métricas de la canalidad telefónica). A nivel de networking, Asterisk recibe peticiones AMI desde OMLApp y desde WombatDialer, mientras que necesita ejecutar conexiones hacia PostgresSQL para dejar logs, hacia Redis para consultar parámetros de campañas aprovisionados desde de OMLApp, y también necesita acceder a Nginx para el establecimiento del Websocket utilizado para traer el contenido de archivos de configuración contenidos en Asterisk (etc/asterisk) y generados desde OMLApp.
_images/arq_omlacd.png
  • Kamailio (https://gitlab.com/omnileads/omlkamailio): Éste componente es utilizado en conjunto con RTPEngine (WebRTC bridge) a la hora de gestionar comunicaciones WebRTC (SIP over WSS) contra los usuarios agentes, mientras mantiene sesiones (SIP over UDP) contra Asterisk. Kamailio recibe los REGISTERs generados por el webphone (JSSIP) desde los agentes, por lo tanto se encarga de la labor de registro y localización de usuarios utilizando Redis para almacenar la dirección de red de cada usuario.

    Para Asterisk, todos los agentes se encuentran disponibles en la URI de Kamailio, por lo que Kamailio recibe INVITEs (UDP 5060) desde Asterisk cuando éste requiere ubicar algún agente para conectar una llamada. Finalmente, cabe mencionar el hecho de que Kamailio genera conexiones hacia RTPEngine (TCP 22222) solicitando un SDP a la hora de establecer sesiones SIP entre Asterisk VoIP y los agentes WebRTC.

_images/arq_omlkamailio.png
  • RTPEngine (https://gitlab.com/omnileads/omlrtpengine): OMniLeads se apoya en RTPEngine a la hora del transcoding y bridge entre la tecnología WebRTC y la tecnología VoIP desde el punto de vista del audio. El componente mantiene canales de audio sRTP-WebRTC con los usuarios agentes por un lado, mientras que por el otro establece canales RTP-VoIP contra Asterisk. RTPEngine recibe conexiones desde Kamailio al puerto 22222.
_images/arq_omlrtpengine.png
  • Nginx (https://gitlab.com/omnileads/omlnginx): El web server del proyecto es Nginx, y tiene como tarea recibir la peticiones TCP 443 por parte de los usuarios, así como también desde algunos componentes como Asterisk. Por un lado, Nginx es invocado cada vez que un usuario accede a la URL del ambiente desplegado. Si las peticiones de los usuarios tienen como destino renderizar alguna vista de la aplicación web Django, entonces Nginx redirecciona la petición a UWSGI, mientras que si las peticiones de los usuarios tienen como destino el REGISTER de su webphone JSSIP, entonces Nginx redirecciona la petición hacia Kamailio (para establecer un websocket SIP). También, Nginx es invocado por Asterisk a la hora de establecer el websocket contra el Websocket-Python de OMniLeads, que aprovisiona la configuración proporcionada desde OMLApp.
_images/arq_omlnginx.png
  • Python websocket (https://gitlab.com/omnileads/omnileads-websockets): OMniLeads se apoya en un servidor de websockets (basado en Python), utilizado para dejar corriendo tareas en segundo plano (reportes y generación de CSVs) y recibir una notificación asíncrona cuando la tarea se haya completado, lo cual optimiza el desempeño de la aplicación. A su vez, es utilizado como puente entre OMLApp y Asterisk en el aprovisionamiento de la configuración de archivos .conf (etc/asterisk).

    Al iniciar Asterisk, se lanza un proceso que establece el websocket contra dicho componente, y a partir de allí, recibe notificaciones cada vez que se proporcionan cambios en la configuración. En su configuración por defecto, levanta el puerto TCP 8000, y las conexiones recibidas son siempre redirigidas desde Nginx.

_images/arq_omlws.png
  • Redis (https://gitlab.com/omnileads/omlredis): Redis es utilizado con 3 fines bien concretos. Por un lado, como caché para almacenar resultados de queries recurrentes implicadas en las vistas de supervisión de campañas y agentes; por otro lado se utiliza como DB para la presencia y localización de los usuarios; y finalmente para el almacenamiento de la configuración de Asterisk (etc/asterisk/) así como también de los parámetros de configuración implicados en cada módulo (campañas, troncales, rutas, IVR, etc.), reemplazando a la alternativa nativa de Asterisk (AstDB).
_images/arq_omlredis.png
  • PostgreSQL (https://gitlab.com/omnileads/omlpgsql): PGSQL es el motor de DB SQL utilizado por OMniLeads. A partir de allí, se materializan todos los reportes y métricas del sistema. También allí, se almacena toda la información de configuración que debe persistir en el tiempo. Recibe conexiones en su puerto TCP 5432 desde los componentes OMLApp (lectura/escritura) y desde Asterisk (escritura de logs).
_images/arq_omlpgsql.png
  • WombatDialer (https://docs.loway.ch/WombatDialer/index.html): Para trabajar con campañas de discado predictivo, OMniLeads recurre a éste software de terceros llamado WombatDialer. Éste discador, cuenta con una API TCP 8080 sobre la cual OMLApp genera conexiones para aprovisionar campañas y contactos, mientras que WombatDialer recurre a la interfaz AMI del componente Asterisk a la hora de generar llamadas salientes automáticas, y checkear el estado de los agentes de cada campaña. Éste componente utiliza su propio motor MySQL para operar.
_images/arq_omlwd.png

Deploy y variables de entorno

Habiendo procesado la exposición anterior sobre la función de cada componente y sus interacciones en términos del networking, pasamos a abordar el asunto del deploy.

Cada componente cuenta con un bash script y Ansible playbook que permiten la materialización del componente, ya sea sobre un Linux-Host dedicado o bien conviviendo con otros componentes en un mismo host.

Ésto es gracias al hecho de que la Ansible playbook puede ser invocada desde el bash script llamado first_boot_installer.tpl, en el caso de acudir al mismo como provisioner de un Linux-Host dedicado para hostear el componente dentro del marco de un cluster, así como también importada por la Ansible playbook del componente OMLApp a la hora de desplegar varios componentes en el mismo host donde corre la aplicación OMLApp.

_images/arq_deploy_cluster_aio.png

Por lo tanto, concluimos en el hecho de que cada componente puede, o bien existir en un host standalone, o también convivir con OMLApp en el mismo host. Éstas posibilidades son contempladas por el método de instalación.

Dicho método de instalación está completamente basado en variables de entorno que se generan en el deploy y tienen como finalidad entre otras cosas, contener las direcciones de red y puerto de cada componente necesario para lograr la interacción. Es decir, todos los archivos de configuración de cada componente de OMniLeads, buscan a su par invocando variables de entorno de OS. Por ejemplo, el componente Asterisk apunta sus AGIs a la envvar $REDIST_HOST y $REDIS_PORT a la hora de intentar generar una conexión hacia Redis.

Gracias a las variables de entorno, se logra una compatibilidad entre los enfoques bare-metal y contenedores docker, es decir que podemos desplegar OMniLeads instalando todos los componentes en un host, distribuyendo los mismos en varios hosts o directamente sobre contenedores Docker.

_images/arq_envvars_deploy.png

El hecho de aprovisionar los parámetros de configuración vía variables de entorno, y además considerando la posibilidad de desplegar siempre la aplicación resguardando los datos que deben persistir (grabaciones de llamadas y DB PostgreSQL) sobre recursos montados sobre el sistema de archivos de Linux que aloja cada componente, podemos entonces plantear el hecho de trabajar con infraestructura inmutable como opción si así lo quisiéramos. Podemos fácilmente destruir y recrear cada componente, sin perder los datos importantes a la hora de hacer un redimensionamiento del componente o plantear actualizaciones. Podremos simplemente descartar el host donde corre una versión y desplegar uno nuevo con la última actualización.

Tenemos el potencial del abordaje que plantea el paradigma de infraestructura como código o infraestructura inmutable, planteado desde la perspectiva de las nuevas generaciones IT que operan dentro de la cultura DevOps. Éste enfoque es algo opcional, ya que se puede manejar actualizaciones desde la óptica más tradicional sin tener que destruir la instancia que aloja el componente.

_images/arq_envvars_deploy_2.png

El potencial de acudir a cloud-init como provisioner

Cloud-init es un paquete de software que automatiza la inicialización de las instancias de la nube durante el arranque del sistema. Se puede configurar cloud-init para que realice una variedad de tareas. Algunos ejemplos de tareas que puede realizar cloud-init, son los siguientes:

  • Configurar un nombre de host.
  • Instalación de paquetes en una instancia.
  • Ejecución de scripts de aprovisionamiento.
  • Suprimir el comportamiento por defecto de la máquina virtual.

A partir de OMniLeads 1.16, cada componente contiene un script llamado fisrt_boot_installer.tpl. Dicho script puede ser precisamente invocado a nivel cloud-init, para que en el primer arranque del sistema operativo se pueda lanzar una instalación fresca del componente.

Como hemos mencionado, es posible invocar al script al momento de crear una VM cloud:

Otra opción, es renderizar como template de Terraform para ser lanzado como provisioning de cada instancia creada desde Terraform.

Más allá del componente en cuestión, el first_boot_installer.tpl tiene como finalidad:

  • Instalar algunos paquetes.
  • Ajustar alguna que otra configuración de la virtual machine.
  • Determinar parámetros de networking de la nueva instancia de Linux.
  • Ejecutar la Ansible playbook que instala el componente en el sistema operativo.

Los 3 primeros pasos son obviados cuando el componente se instala desde OMLApp compartiendo así el host.