domingo, 17 de junio de 2018

ELK - Gestión de logs en cloud

Los sistemas cloud están siendo toda una revolución, a todos los niveles. A nivel de operaciones, y también a nivel de desarrollo.

En esta entrada nos vamos a focalizar en la gestión de logs. Presentaremos una forma de recoger logs de diferentes aplicaciones, agruparlos y entregarlos para su visualización vía web.

Estamos hablando de ELK (Elasticsearch, Logstash y Kibana).

Elasticsearch será el que agrupe los logs de los diferentes micorservicios, a modo de repositorio.
Logstash será el enrutador que traslade los logs desde los microservicios hacia Elasticsearh
Kibana será la capa visual, capaz de explotar el conjunto de logs depositados en Elasticsearch.

El esquema gráfico sería el siguiente:



En la imagen se ven como fuentes de logs varios contenedores docker, y así será en un entorno cloud.

No obstante, en esta entrada sólo nos centraremos en ver cómo levantar un entorno ELK en docker, sin fuentes de log. En una entrada posterior mostraremos como hacer que una aplicación o microservicio, ya sea en un entorno windows normal, o bien en un contenedor, vierta sus logs a nuestro ELK.

En muchas entradas que vayamos haciendo iremos usando este ELK como gestor de los logs de las aplicaciones que vayamos usando.


Prerequisitos

Suponemos que ya tenemos instalado docker en windows. En otro caso consultar la web oficial de docker.

También necesitaremos tener instalado docker-compose. Si no es así, consultar el enlace.

Si necesitamos crear una docker-machine deberemos instalar esta herramienta siguiendo el siguiente enlace.


Ficheros

Los ficheros que vamos a usar para levantar nuestro entono ELK en docker serán
  • docker-compose.yml
  • Dockerfile
  • 02-beats-input.conf
Veamos cada uno de ellos


docker-compose-yml

Este es el fichero principal, el que lanzaremos a través del comando "docker-compose". Este es su contenido:

    version: '3'

    services:
      elk:
        build: ./elk
        image: elkimg
        container_name: elkct
        ports:
          - "5601:5601"
          - "9200:9200"
          - "5044:5044"
        volumes:
          - elkvl:/var/lib/elasticsearch

    volumes:
      elkvl:

El atributo "build" indica que ha de acceder a la carpeta "./elk" a buscar el fichero Dockerfile que veremos a continuación.
Los atributos image y container son sólo etiquetas que permiten identificar tanto la imagen que se creará a partir de la original que veremos en el Dockerfile, y el container o imagen en ejecución.

Los "ports" son los expuestor por Kibana (5601), Elasticsarch (9200) y Logstash (5044).

Usaremos un volumen para dar persistencia entre reinicios de nuestra docker-machine y del container ELK. Así siempre tendremos histórico de consulta.


Dockerfile

Será el fichero que contendrá la configuración para la creación de la imagen ELK local, y para levantar el container asociado a la misma.

Este es su contenido

    FROM sebp/elk

    COPY 02-beats-input.conf /etc/logstash/conf.d/02-beats-input.conf

Como se puede observar es bien sencillo.

El FROM nos indica la imagen a usar como fuente. Se trata de una imagen pública compartida en docker hub. Siempre que usemos una imagen pública como base, que será prácticamente siempre, deberemos consultar la documentación que los creadores de la misma hayan publicado.
Habría otras soluciones. Hay ejemplos en la web que usan tres contenedores, uno por cada elemento. Nosotros hemos optado por esta solución, más compacta (un tres en uno).

El COPY copia el fichero local con la configuración de logstash en la ruta que se indica.

En este caso, la imagen está en "https://hub.docker.com/r/sebp/elk/". Además, para esta imagen concreta, los creadores han compartido una web con toda la información detallada (http://elk-docker.readthedocs.io/). La extensa documentación también ha sido un elemento a favor de optar por esta solución.


02-beats-input.conf

Este fichero, tal como decíamos arriba, viene documentado en la web anterior. Según se nos indica, se ha de copiar en momento de creación y arranque del container en la ruta interna "/etc/logstash/conf.d/02-beats-input.conf". Su contenido si que puede ser libre, y podemos consultar la web de Logstash para ver cual es la configuración adecuada.

En nuestro caso tiene este contenido:

    input {
      tcp {
        port => 5044
        codec => json_lines
      }
    }

    output {
      elasticsearch {
        hosts => "localhost:9200"
        manage_template => false
index => "micro-%{servicename}"
        document_type => "java_log" 
      }
    }

El apartado input nos indica que los logs llegarán a logstash vía tcp desde las aplicaciones o microservicios cliente. El puerto que escucha logstash será el 5044 (por ello está expuesto en el docker-compose.yml antes visto).

En el apartado output se configura el destino de los logs recibidos. Como se ve será elasticsearch, que escucha en el puerto 9200. 
El index se llamará "micro-XXXX", donde XXXX será el nombre que las aplicaciones cliente configuren en su propio código (ya veremos este detalle en la siguiente entrada).


Ejecución

Antes de lanzar nada arrancamos nuestra docker-machine. En nuestro caso habitualmente no trabajamos con la "default", que es la que crea la instalación de docker en un Windows 7.

Lo que hacemos siempre es arrancar una de trabajo que llamamos "arq-root". Lo hacemos de la siguiente forma

    docker-machine start arq-root

Si necesitásemos crear una docker.-machine hemos de hacerlo con

    docker-machine create --driver virtualbox --virtualbox-memory 8096 nombre-machine

    la asignación de memoria usualmente no se utiliza y se dejan los valores por defecto.

Habitualmente lo lanzamos desde una consola bash (cygwin, git bash, ...). También podemos hacerlo desde la consola que se instala al instalar docker (Docker Quickstart Terminal). Cuando acaba de arranca seteamos las variables necesarias para poder trabajar con esta docker-machine:

    eval $(docker-machine env arq-root)

Si trabajásemos desde una consola cmd de windows deberíamos hacerlo así

    @FOR /f "tokens=*" %i IN ('docker-machine env arq-root') DO @%i

A continuación cambiamos de directorio de trabajo hasta situarnos en el que contiene el fichero docker-compose.yml. Una vez ahí lanzamos el siguiente comando:

    docker-compose up -d

Nos levantará un contenedor a partir del contenido del fichero docker-compose.yml en forma "dettached", liberándonos el terminal, para poder seguir trabajando.

La primera vez tardará un rato ya que se ha de descargar la imagen del docker-hub. Generará una nueva imagen con el nombre dado más arriba (elkimg) y levantará un container con el nombre "elkct" (dado en el docker-compose.yml).

Cuando acaba de descargar y levantar el container podemos ver los logs mediante

    docker-compose logs -f   (-f para que se quede visualizando los logs en la medida que se generan)


Resultado

Ahora podemos ver más en detalle cuál es el resultado obtenido. Sobre cada imagen se indica el comando a ejecutar o url a invocar.

Revisamos las imágenes disponibles en nuestra docker-machine:

    docker images
    En rojo la imagen original (sebp/elk), en amarillo la generada.

Ahora vemos los containers activos

    docker ps


Vemos el volumen creado

    docker volume ls



Accedemos a los índices en nuestro elasticsearh
     
    Sólo hay uno interno. Aún ninguna aplicación ha enviado logs a ELK.

  
Accedemos a Kibana



Accedemos al container
   docker exec -it elkct bash

y vemos los índices en la ruta "/var/lib/elasticsearch/nodes/0/indices"

    vemos que el identificador "2pX6KZfKQnOsntt7ZB1FhA" coincide con el mostrado más arriba      vía web.

vemos el fichero de configuración copiado según el Dockerfile:

Los logs de los tres componentes están en "/var/log/elasticsearch".

Esta información de dentro del container es sólo a título informativo. Un buena práctica nos dice que "un container no se toca". Lo que se tenga que customizar se hará vía ficheros de configuración copiados, modificados, etc... en el momento de arranque del container (a través del Dockerfile, etc...).

Con esto cerramos esta entrada. En la próxima veremos como una aplicación puede enviar sus logs a ELK. Veremos asimismo como explotarlos después.


Parada

Una vez visto todo, podemos parar primero el container y después la docker-machine

La parada del container la podemos hacer de dos formas, mediante docker-compose ya que el arranque lo hemos hecho asi, o bien a través del mismo container

primera forma:
    docker-compose down (aconsejable cuando levantamos con docker-compose)

segunda forma: 
  parada del container:           docker stop elkct
  eliminación del container:   docker rm elkct

Por último paramos la docker-machine
    docker-machine stop arq-root

Si en algún momento queremos eliminar una imagen usaremos
    docker rmi nombre_imagen

Si en algún momento queremos eliminar una docker-machine lo haremos así:
    docker-machine rm nombre_machine


Mas info

Por último, los ficheros de esta entrada los podéis obtener de nuestro repo en gitlab.

Se pueden consultar las webs oficiales
Elasticsearch: https://www.elastic.co/products/elasticsearch
Kibana: https://www.elastic.co/products/kibana
Logstash: https://www.elastic.co/products/logstash

No hay comentarios:

Publicar un comentario