sábado, 6 de abril de 2019

ELK - Introducción a Logstash

Con este artículo voy a comenzar una nueva sección dedicada a estudiar la pila ELK. Vamos a empezar con una pequeña introducción a Logstash, lo que nos permitirá entender su función y cómo configurar un pipeline sencillo.

Bueno y ¿que es logstash? pues es el elemento de la pila ELK que permite procesar datos para luego enviarlos a Elasticsearch, para indexarlos y realizar tareas de análisis y búsqueda.

Logstash funciona mediante pipelines, siendo cada uno de ellos los que realizan, en su conjunto, todo el proceso. Un pipeline de logstash tendrá uno o más plugins de entrada, que recibirán los datos que luego se colocarán en una cola interna. Esta cola es por defecto pequeña y se almacena en memoria, pero puede configurarse para que se almacene en disco y ampliar su tamaño.
Los datos se leen de esta cola interna y se procesan a través de plugins de filtro, que se configuran en secuencia y estos datos procesados se envían a los plugins de salida correspondientes, que formatean y envían los datos a Elasticsearch o a otro destino como veremos. Todo esto podemos verlo, más o menos, del siguiente modo:
Estrucutura lógica de un pipeline.
Cada uno de los pasos que forman el pipeline nos permitirá especificar lo siguiente:
  • Plugin de entrada, mediante los cuales especificartemos el origen de nuestros datos. Este plugin puede especificar como fuentes de origen ficheros, stdin, topics de Kafka, varias APIs públicas disponibles, syslog, los beats de Elastic, etc. En este artículo usaremos como plugin de entrada filebeat, para recibir datos de los ficheros de una máquina cliente.
  • Filtros, los cuales nos permitirán procesar la información recibida y modificarla como más nos interese, creando los campos que necesitemos para su posterior procesado.
  • Plugins de salida, que nos permitirán especificar donde enviará Logstash los datos procesados. Al igual que en el caso de los plugins de entrada, hay disponibles una gran cantidad de plugins de salida para enviar los datos a una base de datos MongoDB, a un topic de Kafka, a un socket TCP, a stdout, a un servidor Zabbix, etc. En este artículo enviaremos la salida a stdout, para poder comprobar como logstash procesa la información y la modifica de acuerdo a los filtros que configuremos.
Para generar información, y salvo que dispongas de una gran cantidad de máquinas generando logs que procesar, he creado un pequeño script en Python que genera entradas de syslog que enviaré a Logstash y utilizaré a lo largo del artículo. Si quieres usar este script, echarle un ojo, modificarlo o lo que sea, puedes descargarlo de GitHub.

Como es lógico debemos instalar Logstash, en la máquina que queremos usar como servidor y filebeat en la máquina cliente, auunque pueden estar instalados en la misma máquina. Para la instalación, recomiendo consultar la documentación de instalación proporcionada por Elastic, aunque disponen de sus propios repositorios con lo que es muy sencillo instalarlos.

Filebeat es un agente de Elastic para el envio del contenido de ficheros a los diferentes elementos de la pila ELK y que veremos más detenidamente en posteriores artículos. Por tanto, para empezar, configuramos el cliente, para lo cual es necesario modificar el fichero de configuración filebeat.yml del módulo filebeat. Este módulo dispone de un fichero de configuración cuya ruta por defecto, si instaláis el paquete oficial de Elastic, está en la ruta /etc/filebeat.

Ficheros de configuración de filebeat.
Lo que necesitamos especificar en el fichero de configuración es la ruta de los ficheros que van a enviarse y cual es el destino al que deben enviarse. En la configuración por defecto, filebeat dispone de un solo input, que se denomina log, y especifica que deben leerse los ficheros de la ruta /var/log, con lo que habilitamos dicho input, ya que vamos a enviar registros de syslog:

Configuración de input de filebeat.
La línea include_lines permite especificar que expresiones regulares deben contener las líneas de los ficheros de log para que se envíen al destino que luego especifiquemos. Esta es una manera de realizar un primer filtrado en origen, asegurándonos que solamente se enviarán aquellas líneas de nuestro interés. En el caso que nos ocupa, las lineas de syslog a enviar serán aquellas que contengan la cadena SYSLOG_GEN, como puede verse en la imagen anterior.

Por último solo es necesario habilitar la salida a la que filebeat enviará los datos, para lo cual debemos configurar la sección Logstash output del fichero indicando el nombre o dirección IP del host donde se encuentre instalado Logstash:

Configuración de output de filebeat.
Como se ve en la imagen anterior, podemos enviar la salida de filebeat directamente a un cluster de Elasticsearch pero es muy importante tener en cuenta que solo puede configurarse un output para filebeat. En nuestro caso lo enviamos al servidor donde se encuentra Logstash.

Una vez configurado filebeat en el cliente, pasamos a configurar Logstash definiendo un pipeline muy sencillo, en el cual solo vamos a tener un plugin de entrada, un filtro y un plugin de salida. 
En general los pipelines de Logstash se crean sobre uno o más ficheros de configuración, los cuales pueden estar en un directorio único que especificaremos en el fichero de configuración /etc/logstash/pipelines.yml, con lo que Logstash los concatenrá al arrancar, usando todos los pipelines simultáneamente.

Ficheros de configuración de Logstash.
Cómo el fichero pipelines.yml apunta a la ruta /etc/logstash/conf.d, crearemos los ficheros de configuración de los pipelines en dicho directorio
Ficheros de configuración de pipelines.
Teniendo en cuenta como es la estructura lógica que hemos visto de un pipeline, la estructura básica del fichero de configuración será:

Definición básica de un pipeline.
Para este caso, nuestro plugin de entrada será filebeat, comenzaremos sin especificar un filtro, lo cual es válido y el plugin de salida será stdout. En ese caso, el fichero de configuración del pipeline será como se muestra en la siguiente imagen:

Pipeline básico sin filtro.
Como vemos, el plugin de entrada especifica que para este pipeline Logstash recibirá información de filebeat en el puerto 5044, que no se va a aplicar ningún filtro y que el plugin de salida es stdout, usando un codec para mostrar la salida de un forma más entendible.

Con esta configuración podemos arrancar el servicio filebeat y el generador de syslog en el cliente y, para arrancar logstash usaremos un comando como:
 
Arranque de Logstash especificando la configuración de pipeline.
Desde el cliente, el generador de entradas de syslog está enviando registros a logstash, a través de filebeat, como los siguientes:
 
Salida de syslog_generator.
Con esta configuración, syslog_generator crea una línea de syslog fija con un identificador y una dirección IP ficticia, simulando así una línea de syslog básica.
 
Con esto ¿que nos muestra Logstash en stdout? pues una salida como la siguiente:
 
Salida de Logstash.
Si revisamos detenidamente la imagen anterior, vemos que Logstash nos muestra una serie de campos, formados por parejas clave-valor, muchos de los cuales genera a partir de la información que recibe de filebeat. Al usar beats como plugin de entrada, logstash aplica por defecto un codec específico mediante el cual nos muestra el hostname de origen, la versión de sistema operativo, timestamp, plataforma, etc.
 
Como también podemos ver, hay un campo específico que contiene el mensaje completo generado por syslog_generator y que se guarda en los ficheros de log del cliente a través de su syslog local. Como vemos en la salida, el campo es:
 
Campo message recibido en Logstash desde filebeat.
Debemos tener en cuenta que, al usar filebeat especificando como origen /var/log, este envía el contenido de los ficheros de dicha ruta a la salida configurada, en este caso Logstash. El campo message se corresponde con una línea de syslog, que contiene el registro de syslog generado por el script syslog_generator que estamos lanzando en el cliente.
 
Con esta configuración tan simple, hemos visto como podemos enviar entradas de ficheros de un cliente a Logstash mediante el uso de filebeat. Cómo la idea es procesar dichas entradas, aplicando filtros, en los próximos posts veremos como crear dichos filtros con dissect y con grok para poder extraer los datos que nos interesan de ese campo message.
 

sábado, 9 de marzo de 2019

Cluster ONTAP - Configuración de SVMs con subredes

Hoy vamos con una entrada sencilla de cluster ONTAP. Veamos como podemos usar la característica de subnets o subredes, para configurar de forma automática la red de nuestras SVMs.

Mediante el uso de subredes, la configuración de red de cualquier SVM se realizará de forma automática. Al aplicar una subred a una SVM configuraremos la dirección IP, la máscara de subred y el gateway de la LIF asociada a dicha SVM.

Esta característica está claramente orientada a los proveedores de servicio, donde cada cliente tendrá su propia red separada del resto.

Veamos como crear una subred y aplicarla a la configuración de nuestras SVMs.

Primero crearemos un IPspace y un dominio de broadcast, en el que incluiremos los puertos físicos que tendrán acceso a dicha red:

Creación del IPspace y dominio de broadcast para la subred.
Al listar los puertos de red de nuestro cluster, podremos comprobar que los puertos especificados están en el IPspace y dominio de broadcast recien creados:

Puertos ethernet del cluster, IPspace y dominio de broadcast.

Ahora podemos crear la subred, lo más importante es definir el pool de direcciones IP que se asignarán a las SVMs en el momento de crearlas:

Creación de la subred.
Nuestra subred desde OnCommand System Manager.

Ahora, en el momento de configurar la red de una nueva SVM, elegiremos Using a subnet y seleccionaremos la subred recien creada:

Creación de una SVM con una subred, asignamos el IPspace.
Creación de una SVM con una subred, configuración del data LIF.
Información de uso de la subred desde OnCommand System Manager.

Como vemos, la LIF de la nueva SVM tiene habilitado el DNS dinámico, pero dentro de la configuración de la SVM será necesario habilitarlo si disponemos de un servidor DNS que permita las, tan temidas por algunos, actualizaciones dinámicas:

Configuración del data LIF de la nueva SVM. La opción DDNS está activada.
Habilitamos el DNS dinámico en la configuración de la SVM.
Al permitir las actualizaciones automáticas en el DNS la IP se registrará perfectamente.
Por tanto, como vemosc los sistemas NetApp nos permiten configurar de forma rápida las LIFs de nuestras SVMs y además, de una forma muy simple, integrarlo con nuestro servicio DNS.

En próximas entradas veremos como podemos replicar datos usando SnapMirror para establecer una estrategia de recuperación de desastres.


sábado, 23 de febrero de 2019

Administración básica de Docker swarm - Parte II

Hoy continuamos con la administración de Docker swarm y en concreto, vamos a repasar como usar volúmenes con los servicios que desplegamos en nuestros clusters de dockerhosts.

Originalmente, como ya vimos en su momento, se usaba la opción -v o --volume, para montar volúmenes con nuestros contenedores standalone y, para el despliegue de servicios, se usaba la opción -m o --mount. Desde la versión 17.06 de Docker lo recomendado es usar --mount en ambos casos, aunque se sigue soportando el uso de --volume con contenedores standalone.
Recordando un poco lo que ya sabemos de Docker, hay dos tipos de montajes o volúmenes que podemos usar. Los dos tipos de montajes o volúmenes disponibles son:
  • Volúmenes de datos (volume mounts). Estos son los volúmenes independientes de nuestros dockerhosts y que deben ser gestionados de manera separada. Lo lógico es que estos volúmenes los proporcionen sistemas de almacenamiento específicos y que, mediante un driver determinado, podamos interactuar con el proveedor del almacenamiento y administrar los volúmenes.
  • Volúmenes de tipo bind. De forma muy simple estos son volúmenes que están en cada dockerhost del swarm, es decir son rutas dentro del sistema de archivos de nuestros dockerhosts.
Como ya he comentado, aunque se sigue soportando la opción -v o --volume, lo recomendado es usar siempre la opción -m o --mount, independientemente del tipo de volumen a usar. Al montar nuestros volúmenes con --mount podremos especificar diferentes opciones, como vimos en el post sobre Trident.

Para especificar los volúmenes de nuestros servicios o contenedores, la sintaxis de la opción --mount utiliza determinadas claves, mediante las cuales podemos especificar las siguientes opciones:
  • Tipo de montaje, con la opción type. Esta opción nos permite especificar si el montaje será de tipo bind, volume o tmpfs. El tipo tmpfs nos permite crear un volumen en la memoria asignada al contenedor, lo cual está indicado para aquellos datos temporales que no queremos o no es necesario que tengan persistencia. 
  • Origen del montaje, con la opción source. Para volúmenes con un nombre determinado, este campo contendrá el nombre del volumen. Si no especificamos un nombre se creará un volumen anónimo.
  • Ruta o punto de montaje, con la opción destination. Con esta opción especificaremos el punto de montaje dentro del contenedor.
  • Resto de opciones, especificadas como volume-opt, que nos permitirán especificar opciones adicionales de configuración.
Recordando el post sobre Trident, creamos un servicio montando un volumen y especificando un driver, del siguiente modo:

Especificamos un volumen en la creación de un servicio.
Vamos a revisarlo más detenidamente, definiendo un servicio simple que incluya volúmenes y modificando un servicio ya existente añadiendo o eliminando volúmenes.

Empezando por la parte más sencilla, con el subcomando volume de docker podemos controlar los volúmenes de nuestro cluster swarm, o de dockerhosts individuales. Las opciones disponibles del comando docker volume son las siguientes:

Opciones disponibles del comando docker volume.
Si usamos el comando docker volume sin ninguna opción, el driver utilizado será local, es decir las operaciones se realizarán sobre volúmenes locales del dockerhost sobre el que estemos operando. Por ejemplo, podemos crear un volumen en un solo dockerhost: 

Creación de un volumen local en un dockerhost.
Tambien podemos crear un volumen, con el mismo nombre, en cada uno de los dockerhost que forman parte del swarm:

Creación de un volumen local en los nodos de un swarm.
De la salida del comando docker volume ls vemos que, la primera columna indica que el driver utilizado es local. Esto indica que ese volumen solo existe en el dockerhost y por tanto, los datos que un servicio o contenedor genere y almacene en dicho volumen solo estarán disponibles en dicho dockerhost.

Los volúmenes creados con el driver local se crearán siempre en la ruta /var/lib/docker/volumes, lo cual podemos comprobar usando el comando docker volume inspect:

Obteniendo la información de un volumen.
Como es lógico, podemos cambiar esta ruta especificando la opción --data-root en el arranque del demonio dockerd.

Ahora que hemos creado un volumen común a todos los dockerhosts del swarm, podemos arrancar un servicio simple que use dicho volumen, especificándolo con --mount, mediante el comando siguiente:

Creación de un servicio con un volumen local.
De esta manera, al crear el servicio sin especificar un driver, docker usará el driver local y buscará un volumen con el nombre especificado en el dockerhost para montarlo en los contenedores. En caso de especificar un volumen que no exista, el volumen se creará con el nombre especificado o, si no especificamos un nombre, se creará un volumen anónimo:

Creación de un servicio con múltiples volúmenes.
Al listar los volúmenes disponibles en cada uno de los dockerhosts del swarm, vemos que se ha creado el volumen VOL_logs_swarm que no existía antes y dos volúmenes anónimos, uno se corresponde con el volumen para la ruta especificada /WWW_tmp y el otro volumen anónimo es para el volumen indicado en la definición de la imagen usada, que en este caso  se monta en /WWW.

Volúmenes creados para el servicio.
Por tanto, docker nos permitirá crear los volúmenes o los creará en caso de ser necesario, ya que como vemos se crea un volumen anónimo cada vez que lanzamos un contenedor a partir de una imagen que incluye volúmenes en su configuración.

Cuando queramos eliminar volúmenes que ya no se usen, podremos usar el comando docker volume prune, lo cual los eliminará. Como es lógico, al no especificar el driver, esto solo se aplicará a los volúmenes locales:

Eliminamos los volúmenes no usados. En este caso son volúmenes locales.
Ahora, usando plugins específicos para la creación de volúmenes para nuestros servicios, como por ejemplo el driver Trident para interactuar con sistemas NetApp, podemos crear un servicio que utilice volúmenes de datos comunes proporcionados por una SVM y volúmenes locales para datos temporales que no necesitamos que se mantengan:

Creación de un servicio con volúmenes desde una SVM.
En este ejemplo creamos un servicio que usa un volumen ya existente en la SVM (swarm_HTML), otro volumen que no existe y que el driver creará por nosotros (swarm_LOG) y un volumen local que tampoco existía con anterioridad (LOCAL_tmp) y que Docker creará en cada dockerhost:

El volumen VOL_swarm_LOG lo creará Trident al crear el servicio.

Lista de volúmenes existentes en nuestro swarm.
Por tanto podemos combinar volúmenes de diferentes poveedores en el mismo servicio con solo especificar el driver correcto para cada uno de ellos. Podemos comprobar los volúmenes en uso por el servicio usando el comando docker service inspect:

Lista de volúmenes en uso por el servicio.
En caso de ser necesario podemos actualizar la configuración del servicio y añadir o quitar volúmenes, mediante la opción --mount-rm del comando docker service update. Por ejemplo, si ncesitamos eliminar el volumen local, podemos hacer lo siguiente:

Eliminamos un volúmen de un servicio.
Con lo que ahora, al inspeccionar el servicio, vemos que solo está usando los volúmenes proporcionado por la SVM del sistema NetApp:

El servicio ahora usa solo dos volúmenes, dados por el driver Trident.
Para aplicar este update, como vemos en la salida siguiente, Docker ha parado cada contenedor del servicio y lo ha vuelto a arrancar con la nueva configuración:

Contenedores arrancados con la nueva configuración de volúmenes.
Por tanto y para terminar, hemos visto como Docker nos permite gestionar los volúmenes necesarios para nuestros contenedores o servicios y su integración con proveedores de almacenamiento como NetApp. Como siempre, para una información mucho más detallada, os recomiendo consultar la documentación oficial de Docker.

sábado, 2 de febrero de 2019

Cluster ONTAP - Usando SyncMirror

Hoy volvemos con una nueva entrada sobre ONTAP, en la que veremos que es y cómo usar SyncMirror.

De forma muy simple, SyncMirror permite realizar mirrors de agregados, es decir, nos permite añadir otra capa de protección adicional replicando todos los datos de un agregado de forma local o remota. SyncMirror es una de las funcionalidades que emplea MetroCluster para replicar datos entre diferentes sites, permitiéndonos tener copias de los agregados del site principal en el site secundario.

Si nos fijamos en la salida del comando aggr show de cualquier agregado en un sistema NetApp, independientemente de si es 7-mode o C-mode, veremos que siempre aparece un dispositivo llamado plex0 asociado con los raid groups de los agregados:

Relación entre plexes y agregados.
Ese dispositivo plex0 representa una de las "patas" de nuestro mirror y lo que SyncMirror nos permite es añadir la otra "pata", que será plex1. Cada plex estará formado por discos de diferentes pools, siendo un pool un conjunto lógico de discos.

Lo primero que es necesario tener en cuenta es que para asegurar el mirror, debemos usar discos separados físicamente. En el caso del despliegue de un MetroCluster, esa separación ya viene dada y en el caso de usarlo localmente, tendremos que usar discos de diferentes bandejas y stacks. Esta comprobación la realiza de forma automática el sistema, comprobando que los discos son idénticos y que están en diferentes stacks.

Por defecto, todos los discos de un sistema ONTAP están siempre en el Pool0, como podemos ver en la siguiente imagen:

Por defecto no hay discos en el Pool1.
En caso de intentar hacer un mirror de un agregado ya existente con discos del mismo pool, recibiremos un error como el siguiente:

Necesitaremos discos en pools separados para poder crear el mirror de un agregado.
Por tanto, para poder usar SyncMirror tendremos que asignar discos a diferentes pools y, teniendo en cuenta que estamos usando el simulador, debemos asignar los discos por bandeja virtual. Como cada bandeja del simulador tiene 14 discos, y cada una empieza en la bahía 16, será necesario deshabilitar el autoassign de uno de los nodos, eliminar el propietario de la mitad de discos del nodo y volver a asignarlos, ya especificando que se añadan al Pool1. En resumen, la asignación de discos la realizamos del siguiente modo:

Agregamos discos al Pool1 del simulador.
Listamos los discos disponibles en el Pool1.
De esta manera tendremos la mitad de los discos del nodo en cada uno de los pools y podremos crear el mirror con el siguiente comando, que nos dará el siguiente mensaje de error indicando que al menos son necesarios diez discos para crear correctamente el agregado en mirror: 

Necesitaremos al menos un total de 10 discos, 5 por pool, para crear el agregado en mirror usando RAID-DP.
Como independientemente de estar creando un agregado en mirror, es necesario crear los raid groups de los agregados con el número de discos requeridos por tipo de RAID, si creamos raid groups de tipo RAID-DP el mínimo número es 5, con lo que si por ejemplo vamos a usar 7 discos por Pool, es decir 7 del Pool0 y 7 del Pool1, el total de discos será 14.

Para crear el agregado replicado con SyncMirror solo es necesario ejecutar el siguiente comando, la primera vez con la opción simulate:

Simulamos la creación del agregado en mirror.
y cómo no recibimos ningún error adicional, creamos el agregado en mirror repitiendo el comando anterior pero sin la opción simulate. Tras esto, la salida del comando storage aggregate show-status es la siguiente:

 

Podemos comprobar que el agregado está en mirror con el comando storage aggregate show:

Agregado en estado mirrored.
Y si comprobamos los detalles del agregado especificando el nombre del mismo, o usamos el parámetro -instance, podemos ver los plexes y los discos de cada uno de ellos:


Ahora, para realizar alguna prueba de sincronización, lo más sencillo es crear una SVM y asignar un volúmen a la misma dentro de dicho agregado. Exportamos el nuevo volúmen por NFS y lo montamos en un cliente Linux para escribir datos de prueba.

Con el volúmen ya exportado y montado en un cliente por NFS, podemos poner uno de los plexes offline, para comprobar como el agregado y por tanto el volúmen, sigue estando accesible y es totalmente transparente para el cliente NFS. Si quiero consultar los plexes existentes en un sistema, puedo usar el comando siguiente:

Consultamos los plexes disponibles en el sistema.
Y para pasar al estado offline el primer Plex, y por tanto simular un fallo, ejecutamos el siguiente comando:

Pasamos el plex0 al estado offline.
Ahora, al comprobar los plexes existentes, podemos ver que el plex0 está inactivo:

Uno de los plexes está inactivo.
Pero desde el cliente NFS comprobamos que este sigue teniendo acceso a la ruta montada sin problemas y puede realizar operaciones de escritura y lectura:

El cliente NFS no experimenta ningún corte y el mirror del agregado funciona perfectamente.
Si copiamos mas datos y aumentamos el tamaño del volumen, podremos ver como ONTAP resincroniza ambos plexes cuando volvamos a poner online el plex0:

Aumentamos el tamaño del volumen y copiamos más datos.
Ahora ponemos de nuevo online el plex0 y, comprobando la salida del comando storage aggregate plex show, podemos comprobar que ambos plexes se están resincronizando:

Ponemos el plex0 online y el sitema resincroniza ambos plexes.
La salida del comando nos muestra el porcentaje de resincronización.
Cómo es lógico, este proceso puede tardar más o menos en función de cuantos datos tengan que resincronizarse.

Para que un agregado deje de estar en mirror solo es necesario borrar uno de los plexes usando el subcomando delete, como podemos ver en la siguiente imagen:

Borrado de uno de los plexes de un agregado en mirror.
Cómo el comando anterior solo borra uno de los plexes del agregado, no se produce ningún tipo de pérdida de datos y el estado del agregado sigue apareciendo como normal:

Estado de los agregados tras borrar el plex.
Si intentamos borrar un plex de un agregado que no está en mirror, recibiremos el siguiente mensaje de error indicando que el agregado no está en mirror y que, por tanto, no puede borrarse el último plex de un agregado:

Error al intentar borrar el último plex de un agregado.
Por útlimo, indicar que no hace falta crear el agregado en mirror desde el principio con SyncMirror, sino que podemos hacer mirror de cualquier agregado ya existente con solo ejecutar el siguiente comando:

Protegemos un agregado existente con SyncMirror.
Aquí vemos como se sincronizan ambos plexes.
El último punto a tener en cuenta es que es recomendable que los discos estén zeroed, ya que en caso contrario el sistema hará el zero de los mismos y los añadirá al plex una vez que haya terminado con lo que si antes de terminar de hacer el zero se rebota el nodo, entonces el plex se queda vacio y hay que eliminarlo manualmente para repetir el proceso.