Artículo
· 25 abr, 2023 Lectura de 13 min

Configurando Mirror en Docker

Una necesidad habitual en nuestros clientes es la configuración tanto de HealthShare HealthConnect como de IRIS en modo de alta disponibilidad.

Es común en otros motores de integración del mercado que se promocionen con configuraciones de "alta disponibilidad", pero realmente no suele ser del todo cierto. Por lo general dichas soluciones trabajan con bases de datos externas y por lo tanto, si estas no están a su vez configuradas en alta disponibilidad, al producirse una caída de la base de datos o la pérdida de conexión a la misma toda la herramienta de integración queda inutilizable.

En el caso de las soluciones de InterSystems este problema no existe, al ser la base de datos parte y nucleo de las propias herramientas. ¿Y cómo ha solucionado InterSystems el problema de la alta disponibilidad? ¿Con abstrusas configuraciones que podrían arrastrarnos a una espiral de enajenamiento y locura? ¡NO! Desde InterSystems hemos escuchado y atendido vuestras quejas (como siempre intentamos hacer ;) ) y hemos puesto a disposición de todos nuestros usuarios y desarrolladores la función de mirroring.

Mirroring

¿Cómo funciona el Mirror? El concepto en si es muy sencillo. Como ya sabréis tanto IRIS como HealthShare trabajan con un sistema de journaling que registra toda operación de actualización sobre las bases de datos de cada instancia. Este sistema de journaling es el que posteriormente nos sirve para recuperar las instancias sin apenas pérdida de datos en caso de caída de las mismas. Pues bien, son estos archivos de journal los que se envían entre las instancias configuradas en mirror y permiten mantener permanentemente actualizados las instancias configuradas en mirror.

Arquitectura

Expliquemos brevemente como sería la arquitectura de un sistema configurado en Mirror:

  • Dos instancias configuradas en modo Failover:
    • Nodo activo: recibe todas las operaciones de lectura/escritura habituales.
    • Nodo pasivo: en modo lectura, recibe de forma síncrona cualquier cambio producido en el nodo activo.
  • 0-14 instancias asíncronas: tantas instancias asíncronas como se quieran utilizar, pueden ser de dos tipos:
    • DR async (Disaster Recovery): nodos en modo lectura que no forman parte del Failover aunque se le puede promocionar manualmente.De ser así podría llegar a promocionarse automáticamente a nodo primario en caso de caída de los otros dos nodos del Failover. La actualización de sus datos es en modo asíncrono, por lo que no se garantiza la frescura de los mismos.
    • Reporting Asyncs: nodos actualizados de forma asíncrona para su uso en tareas de BI o explotaciones de datos. No pueden ser promocionados al Failover ya que se pueden realizar escrituras sobre los datos.
  • ISCAgent: instalado en cada servidor donde se encuentre una instancia. Será el encargado de monitorizar el estado de las instancias de dicho servidor. Es otra vía de comunicación entre los servidores del Mirror además de la comunicación directa.
  • Arbiter: es un ISCAgent instalado de forma independiente a los servidores que forman el Mirror y permite aumentar la seguridad y el control de los failover dentro del mismo monitorizando tanto a los ISCAgent instalados como a las instancias de IRIS/HealthShare. Su instalación no es obligatoria.

Este sería el funcionamiento de un Mirror formado por un failover con dos únicos nodos:

In an InterSystems IRIS mirror, when the primary becomes unavailable, the mirror fails over to the backup.

Aviso previo

El proyecto asociado a este artículo no dispone de una licencia activa que permita la configuración del mirror. Si queréis probarlo enviadme directamente un email o añadid un comentario al final del artículo y me pondré en contacto con vosotros.

Despliegue en Docker

Para este artículo vamos primeramente a montar un pequeño proyecto en Docker que nos permita montar 2 instancias en failover con un Arbiter. Por defecto las imágenes de IRIS disponibles para Docker tienen ya instalado y configurado el ISCAgent, por lo que nos podremos saltar ese paso. Será necesario configurar el proyecto que viene asociado al artículo desde un Visual Studio Code, ya que nos permitirá posteriormente trabajar de una forma más cómoda con los archivos del servidor.

Veamos que forma tendría nuestro docker-compose.yml:

version: '3.3'
services:
  arbiter:
      container_name: arbiter
      hostname: arbiter
      image: containers.intersystems.com/intersystems/arbiter:2022.1.0.209.0
      init: true
      command:
        - /usr/local/etc/irissys/startISCAgent.sh 2188
  mirrorA:
    image: containers.intersystems.com/intersystems/iris:2022.1.0.209.0
    container_name: mirrorA
    depends_on:
      - arbiter
    ports:
    - "52775:52773"
    volumes:
    - ./sharedA:/shared
    - ./install:/install
    - ./management:/management
    command:
      --check-caps false
      --key /install/iris.key
      -a /install/installer.sh
    environment:
    - ISC_DATA_DIRECTORY=/shared/durable
    hostname: mirrorA
  mirrorB:
    image: containers.intersystems.com/intersystems/iris:2022.1.0.209.0
    container_name: mirrorB
    depends_on:
      - arbiter
      - mirrorA
    ports:
    - "52776:52773"
    volumes:
    - ./sharedB:/shared
    - ./install:/install
    - ./management:/management
    command:
      --check-caps false
      --key /install/iris.key
      -a /install/installer.sh
    environment:
    - ISC_DATA_DIRECTORY=/shared/durable
    hostname: mirrorB

Podemos ver que tenemos definidos 3 containers:

  • Arbiter: correspondiente al ISCAgent (aunque la imagen se llame Arbiter) que se desplegará para el control de las instancias de IRIS que formarán el Failover del Mirror. Al arrancar el container ejecutará un fichero shell que arrancará el ISCAgent escuchando en el puerto 2188 del container.
  • mirrorA: container en el que se desplegará la imagen de IRIS v.2022.1.0.209 y que posteriormente configuraremos como nodo primario del Failover.
  • mirrorB: container en el que se desplegará la imagen de IRIS v.2022.1.0.209 y que posteriormente configuraremos como nodo secundario del Failover.

Cuando ejecutemos el comando docker-compose up -d se desplegarán en nuestro Docker los contenedores definidos, debiendose ver tal que así en nuestro Docker Desktop (si lo hacemos desde Windows).

Configuración del mirror.

Con nuestros contenedores desplegados procederemos a acceder a las instancias que vamos a configurar en mirror, la primera se encontrará escuchando en el puerto 52775 (mirrorA) y la segunda en el 52776 (mirrorB). El usuario y la contraseña de acceso serán superuser / SYS

Debido a la que las instancias se encuentran desplegadas en Docker, tendremos dos opciones de para configurar las IP de nuestros servidores. La primera es usar directamente el nombre de nuestros contenedores en la configuración (que es la más sencilla) o bien comprobar las IP que Docker ha asignado a cada contenedor (abriendo la consola y ejecutando un ifconfig que nos devuelva la IP asignada). Por motivos de claridad utilizaremos para el ejemplo los nombres que les hemos dado a cada contenedor como dirección de cada uno dentro de Docker.

Primeramente configuraremos la instancia que utilizaremos como nodo activo del FailOver. En nuestro caso será la que hemos denominado mirrorA.

El primer paso será habilitar el servicio de mirroring, por lo que accederemos al menú de mirror desde el portál de gestión: System Adminsitration --> Configuration -->  Mirror Settings --> Enable Mirror Service y marcaremos el check de Service Enabled:

Con el servicio habilitado ya podremos empezar a configurar nuestro nodo activo. Tras habilitar el servicio podréis observar que se han habilitado nuevas opciones en el menú de Mirror:

En este caso, al no tener ninguna configuración de mirror ya creada deberemos crear una nueva con la opción de Create Mirror. Cuando accedemos a dicha opción el portal de gestión nos abrirá una nueva ventana desde la que podremos configurar nuestro mirror:

Veamos con más detalle cada una de las opciones:

  • Mirror Name: el nombre con el que identificaremos a nuestro mirror. Para nuestro ejemplo lo llamaremos MIRRORSET
  • Require SSL/TLS: para nuestro ejemplo no configuraremos conexión mediante SSL/TLS aunque en entornos productivos sería más que conveniente para evitar que el archivo de journal se comparta sin ningún tipo de cifrado entre las instancias. Si tenéis interés en configurarlo tenéis toda la información necesaria en la siguiente URL de la documentación.
  • Use Arbiter: esta opción no es obligatoria, pero es bastante recomendable, ya que añade una capa de seguridad a la configuración de nuestro mirror. Para nuestro ejemplo lo dejaremos marcado e indicaremos la IP en el que tenemos funcionando nuestro Arbiter. Para nuestro ejemplo la IP será en nombre de contenedor "arbiter".
  • User Virtual IP: en entornos Linux/Unix está opción es muy intersante ya que nos permite configurar una IP virtual para nuestro nodo activo que será gestionada por nuestro Mirror. Esta IP virtual debe pertenecer a la misma subred en la que se encuentren los nodos del Failover. El funcionamiento de la IP virtual es muy sencillo, en caso de caída del nodo activo el mirror configurará automáticamente la IP virtual en el servidor en el que se encuentre el nodo pasivo que va a ser promocionado. De tal forma, la promoción del nodo pasivo a activo será totalmente transparente para los usuarios, ya que estos seguirán conectados a la misma IP, aunque esta se encontrará configurada en un servidor distinto. Si queréis saber más al respecto de la IP virtual podéis revisar esta URL de la documentación.

El resto de la configuración podemos dejarla tal como está. En el lado derecho de la pantalla veremos la información relativa a este nodo en el mirror:

  • Mirror Member Name: nombre de este miembro del mirror, por defecto tomará el nombre del servidor junto con el de la instancia.
  • Superserver Address: dirección IP del superserver de este nodo, en nuestro caso, mirrorA.
  • Agent Port: puerto en el que se ha configurado el ISCAgent correspondiente a este nodo. Por defecto el 2188.

Una vez configurado los campos necesarios podemos proceder a guardar el mirror. Podemos comprobar como ha quedado la configuración desde el monitor del mirror (System Operation --> Mirror Monitor).

Perfecto, aquí tenemos nuestro mirror recién configurado. Como véis únicamente figura el nodo activo que acabamos de crear. Muy bien, vayamos entonces a añadir nuestro nodo pasivo en el Failover. Accedemos al portal de gestión de mirrorB y accedemos al menú de Mirror Settings. Como ya hicimos para la instancia de mirrorA deberemos habilitar el servicio de Mirror. Repetimos la operación y en cuanto se actualicen las opciones del menú elegiremos Join as Failover.

Aquí tenemos la pantalla de conexión al mirror. Expliquemos brevemente que son cada uno de los campos:

  • Mirror Name: nombre que dimos al mirror en el momento de la creación, en nuestro ejemplo MIRRORSET.
  • Agent Address on Other System: IP del servidor en el que se encuentra desplegado el ISCAgent del nodo activo, para nosotros será mirrorA
  • Agent Port: puerto de escucha del ISCAgent del servidor en el que creamos el mirror. Por defecto el 2188.
  • InterSystems IRIS Instance Name: el nombre de la instancia de IRIS en el nodo activo. En este caso coincide con el del nodo pasivo, IRIS.

Tras grabar los datos del mirror tendremos la opción de definir la información relativa al nodo pasivo que estamos configurando. Echemos nuevamente un vistazo a los campos que podemos configurar del nodo pasivo:

  • Mirror Member Name: nombre que tomará el nodo pasivo en el mirror. Por defecto formado por el nombre del servidor y la instancia.
  • Superserver Address: dirección IP de acceso al superserver nuestro nodo pasivo. En este caso mirrorB.
  • Agent Port: puerto de escucha del ISCAgent instalado en el servidor del nodo pasivo que estamos configurando. Por defecto el 2188.
  • SSL/TLS Requirement: al no configurarlo en la declaración del mirror no es configurable.
  • Mirror Private Address: dirección IP del nodo pasivo. Como hemos visto, al usar Docker podremos usar el nombre del contenedor mirrorB.
  • Agent Address: dirección IP al servidor donde está instalado el ISCAgent. Igual que antes, mirrorB.

Grabamos la configuración tal y como hemos indicado y volvemos al monitor del mirror para comprobar que tenemos todo correctamente configurado. Podemos visualizar el monitor tanto del nodo activo en mirrorA como el del pasivo en mirrorB. Veamos las sutiles diferencias entre ambos.

Monitor del mirror en nodo activo mirrorA:

Monitor del mirror en nodo pasivo mirrorB:

Como véis la información mostrada el similar, cambiando básicamente el orden de los miembros del failover. También las opciones son distintas, veamos alguna de ellas:

  • Nodo activo mirrorA:
    • Set No Failover: impide la ejecución del failover en caso de parada de alguna de las instancias que forman parte de él.
    • Demote other member: elimina al otro miembro del failover (en este caso mirrorB) de la configuración del mirror.
  • Nodo pasivo mirrorB:
    • Stop Mirror On This Member: detiene la sincronización del mirror en el nodo pasivo del failover.
    • Demote To DR Member: "degrada" a este nodo pasando de formar parte del failover con su sincronización en tiempo real al modo Disaster Recovery en modo asíncrono.

Perfecto, ya tenemos nuestros nodos configurados, ahora nos queda el paso final en nuestra configuración. Decidir que tablas pasaran a formar parte del mirror y configurarlo en ambos nodos. Si observáis el README.md del proyecto asociado a este artículo veréis que indicamos la existencia de dos aplicaciones que usamos habitualmente para las formaciones y que son automáticamente desplegadas al arrancar los contenedores de Docker.

La ventaja de usar esta configuración es que no tendremos que replicar a mano los NAMESPACES ni las bases de datos relacionadas de cara a configurar a mano el mirror, ya tenemos en ambas instancias los mismos namespaces y bases de datos.

La primera es COMPANY que nos permite guardar registros de empresas y la segunda es PHONEBOOK que nos permite añadir contactos de personal relacionados con las empresas registradas, así como clientes.

Añadamos una compañía:

Y a continuación un contacto de dicha compañía:

Los datos de la compañía se registrarán en la base de datos COMPANY y los del contacto en PERSONAL, ambas bases de datos están mapeadas para que puedan ser accesibles desde el Namespace PHONEBOOK. Si comprobamos las tablas en ambos nodos veremos que en mirrorA tenemos los datos tanto de la compañía como del contacto pero que el mirrorB aún no hay nada, como es lógico.

Compañías registradas en mirrorA:

Muy bien, procedamos a configurar las bases de datos en nuestro mirror. Para ello, desde nuestro nodo activo (mirrorA), accedemos a la pantalla de administración de las bases de datos locales (System Administrator  --> Configuration --> System Configuration --> Local Databases) y pulsamos en la opción de Add to Mirror, seleccionamos de la lista las bases de datos que queremos añadir leyendo con detenimiento el mensaje que se nos muestra en pantalla:

Una vez que hayamos añadido las bases de datos al mirror desde el nodo activo deberemos realizar un backup de las mismas o bien copiar los archivos de base de datos (IRIS.dat) y restaurarlos sobre el nodo pasivo. Si decidís realizar la copia directa de los archivos IRIS.dat tened en cuenta que debería realizarse previamente una pausa de la escritura en el fichero, podéis ver los comandos necesarios en la siguiente URL de la documentación. En nuestro ejemplo no será necesario realizar dicha pausa ya que nadie más que nosotros estamos escribiendo en ella.

Antes de realizar dicha copia de los archivos de las bases de datos comprobemos el estado del mirror desde el monitor del nodo activo:

Veamos ahora el nodo pasivo:

Como podemos observar, desde el nodo pasivo se nos está informando que si bien tenemos configuradas 3 bases de datos en el mirror la configuración aún no está hecha. Procedamos a copiar las bases de datos del nodo activo al pasivo, no olvidemos que debemos desmontar las bases de datos del nodo pasivo para poder hacer la copia y para ello accederemos desde el portal de gestión a System Configuration --> Databases y accediendo a cada una de ellas procedemos a desmontarlas.

¡Perfecto! Bases de datos desmontadas. Accedamos al código del proyecto asociado al artículo desde Visual Studio Code y vemos que tenemos una serie de carpetas donde se encuentran las instalaciones de IRIS, sharedA  para el mirrorA sharedB para el mirrorB. Accedamos a las carpetas donde se encuentran las base de datos COMPANY, CUSTOMER y PERSONAL en (/sharedA/durable/mgr) y procedamos a copiar los IRIS.dat de cada base de datos en el mirror a los directorios oportunos del mirrorB (/sharedB/durable/mgr).

Una vez finalizada la copia montamos nuevamente las bases de datos de mirrorB y comprobamos desde el monitor del mirror en mirrorB el estado de las bases de datos configuradas:

¡Bingo! nuestro mirror ha reconocido las bases de datos y ahora sólo necesitaremos activarlas y ponerlas al día. Para ello pulsaremos sobre la acción Activate y a continuación sobre Catchup, que aprecerá tras la activación. Veamos como quedan finalmente:

Perfecto, nuestras bases de datos ya están correctamente configuradas en mirror, si consultamos la base de datos de COMPANY deberíamos ver la que registramos inicialmente desde mirrorA:

Obviamente nuestra base de datos COMPANY tiene el registro que introdujimos con anterioridad en mirrorA, al fin y al cabo hemos copiado toda la base de datos. Procedamos a añadir una nueva empresa desde mirrorA a la que llamaremos "Another company" y procedamos a consultar nuevamente la tabla de la base de datos COMPANY:

Aquí la tenemos. Únicamente nos quedará asegurarnos de que nuestras bases de datos configuradas en mirror se encuentran únicamente en modo lectura en nuestro nodo pasivo mirrorB:

¡Ahí están! en modo R de lectura. Pues ya tenemos nuestro mirror configurado y nuestras bases de datos sincronizadas. En el caso de que tuviesemos funcionando producciones no sería ningún problema ya que el mirror se encarga de forma automática de gestionarlas, arrancándolas en el nodo pasivo caso de caída del nodo activo.

¡Muchas gracias todos los que habéis llegado hasta este punto! Ha sido largo pero confío que os resulte de utilidad.

Comentarios (0)1
Inicie sesión o regístrese para continuar