Stack de monitorización con Kubernetes

Jesus Sayar
The Cocktail Engineering
6 min readJan 17, 2018

--

Port of Hamburg — Glynlowe

En The Cocktail usamos un stack de monitorización basado en InfluxDB, Kapacitor, Telegraf y Grafana. Hasta ahora esta arquitectura la teníamos montada en maquinas de Amazon configuradas manualmente, pero recientemente decidimos realizar una mejora, replicando el este stack utilizando una arquitectura basa en microservicios con Kubernetes.

Montar una arquitectura de monitorización con Kubernetes nos aporta muchas ventajas, pero la automatización de despliegues, los escalados, la alta disponibilidad y la gestión de configuración de las aplicaciones creemos que son las más importantes.

Stack

  • InfluxDB es una base de datos de series temporales (Time Series Database) que guarda series de datos indexadas a lo largo del tiempo. Utilizado comúnmente como solución de almacenamiento en caso de uso que involucre grandes cantidades de datos situadas en instantes de tiempo.
  • Telegraf es un agente de recolección de datos, cuyo objetivo es enviar telemetría del sistema o de la aplicación a sistemas de almacenamiento como InfluxDB o Graphite.
  • Grafana es una herramienta de visualización de series de datos temporales. Muy utilizada para visualizar datos en tiempo real de infraestructuras y aplicaciones o en servicios de monitorización.
  • Kapacitor es un motor de procesamiento de datos, que permite el procesado de información en streams o batches. Permite crear alertas y detectar anomalias, realizando acciones especificas en función es estas alertas.
  • Chronograf es una interfaz de usuario para una arquitectura basada en los componentes de InfluxData.

Arquitectura

Como se ilustra en la imagen inferior las piezas de nuestro stack interactúan de la siguiente manera:

  • Telegraf recolecta información (consumo CPU, memoria, espacio en disco etc…) de nuestras aplicaciones para enviarla a InfluxDB.
  • Kapacitor analiza toda la información que va llegando a InfluxDB en base a alertas que hemos configurado, y en caso de situaciones anómalas (demasiado consumo de CPU, poco espacio en disco etc…), lanza una notificación a Slack y notifica a nuestro servicio de guardias.
  • Chronograf se comunica con InfluxDB y Kapacitor y nos brinda una interfaz de gestión.
  • Grafana se conecta a InfluxDB y nos permite crear dashboards para cada proyecto, donde podemos visualizar toda la información recolectada generando gráficas de todo tipo, como consumo de CPU, memoria o peticiones por segundo.

Kubernetes

Como hemos visto los componentes del stack tienen funciones bien definidas, y pueden realizar su tarea de manera aislada, por lo que desplegar cada pieza del stack en un contenedor respeta este desacoplamiento, y permite una gestión y una administración individualizada idónea.

Para el montaje nos apoyaremos en Google Cloud puesto que permite montar un cluster de Kubernetes de manera relativamente sencilla, sin preocuparnos demasiado por su administración, por lo que ha sido nuestro proveedor elegido.

La configuración de cada una de estos componentes en Kubernetes es muy similar, por ello para no extender demasiado el articulo, vamos a ilustrar únicamente la ficheros de configuración de InfluxDB.

Contenedores

Hemos utilizado los contenedores oficiales en la mayoría de las situaciones. Únicamente hemos creado nuestros propias imágenes de Docker para el Nginx frontal y el Kapacitor.

Kapacitor permite la definición de alertas para ejecutar eventos cuando se detectan situaciones anómalas, dichas alertas son configuradas en ficheros y la mejor manera de mantener un control de versiones y aprovisionarlos en nuestro contenedor es generar nuestra propia imagen de Docker.

En cuanto al Nginx, generar nuestra propia imagen de Docker es la mejor manera de configurar nuestros vhosts, manteniéndolos versionados.

A continuación podemos ver un deployment de K8s para InfluxDB. Hemos empleado un contenedor oficial de InfluxDB, un volumen de almacenamiento externo y un volumen de configuración, en los siguientes apartados analizaremos más en detalle estos aspectos.

apiVersion: extensions/v1beta1
kind: Deployment
metadata:
name: influxdb
namespace: monitoring
spec:
replicas: 1
template:
metadata:
labels:
app: influxdb-app
spec:
affinity:
nodeAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
nodeSelectorTerms:
- matchExpressions:
- key: failure-domain.beta.kubernetes.io/zone
operator: In
values:
- europe-west1-d
containers:
- name: influxdb-app
image: influxdb:alpine
ports:
- name: api
containerPort: 8086
volumeMounts:
- name: influxdb-storage
mountPath: /var/lib/influxdb
- name: influxdb-config
mountPath: /etc/influxdb
volumes:
- name: influxdb-storage
gcePersistentDisk:
# This disk must already exist.
pdName: influxdb
fsType: ext4
- name: influxdb-config
configMap:
name: influxdb

Configuración

Como parte de la solución para el almacenamiento de los ficheros de configuración se ha optado por el uso del ConfigMap de K8s, puesto que este puede ser montado como volumen en el deployment de la aplicación. A continuación tenemos un ejemplo de uso del recurso para almacenar la configuración de InfluxDB.

apiVersion: v1
kind: ConfigMap
metadata:
name: influxdb
namespace: monitoring
data:
influxdb.conf: |-
### Welcome to the InfluxDB configuration file.

Es muy común tener que configurar claves, tokens y contraseñas, por suerte las mayoría de las aplicaciones están desarrolladas de tal manera que los mismos valores que se indican en los ficheros de configuración puedan ser proporcionados en variables de entorno.

Si este es el caso puedes generar un recurso tipo Secret de K8s donde almacenar estos valores comprometidos y posteriormente configurar variables de entorno en los contenedores donde se carguen los valores de dichos Secrets. Así lo hemos hecho nosotros para configurar en Grafana los tokens necesarios para el login con Google.

containers:
- name: grafana-app
image: grafana/grafana:4.6.2
ports:
- name: admin-interface
containerPort: 3000
env:
- name: GF_AUTH_GOOGLE_CLIENT_ID
valueFrom:
secretKeyRef:
name: grafana-secrets
key: google_client_id
- name: GF_AUTH_GOOGLE_CLIENT_SECRET
valueFrom:
secretKeyRef:
name: grafana-secrets
key: google_client_secret

Volúmenes de datos

Todos los componentes del stack tienen una base de datos para almacenar información, ya sea InfluxDB que es una base de datos como tal, como Grafana y Chronograf que utilizan un base de datos para almacenar configuración personal y otros datos de administración.

Para poder tirar y levantar nuevos Pods sin perder información, la mejor estrategia es externalizar el almacenamiento de datos. Hemos optado por externalizar los datos a unidades de disco de Google Cloud, que son montadas en nuestros Pods como volúmenes de información.

Un detalle importante es que las unidades de disco en Google Cloud son recursos zonales, por lo tanto si creas una unidad de disco en europe-west1-d por ejemplo, solo los Pods levantados en dicha zona podrán acceder a los datos. Por tanzo hemos configurado nuestros deployments para que los Pods sean levantados en dicha zona, para ello hemos utilzado los Affinity de Kubernetes. Dicha configuración está presente como hemos visto en el fichero de deployment.

affinity:
nodeAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
nodeSelectorTerms:
- matchExpressions:
- key: failure-domain.beta.kubernetes.io/zone
operator: In
values:
- europe-west1-d

Servicios

Los servicios de Kubernetes son los que facilitan el acceso a las aplicaciones de nuestro cluster.

En nuestro caso hemos implementado servicios accesibles únicamente desde dentro del cluster (tipo ClusterIP) para Grafana, Chronograf y Kapacitor. El motivo de utilizar este tipo de servicios para Grafana y Chronograf es que utilizaremos un Nginx por delante para controlar el acceso web. Kapacitor se ocupará de analizar los datos de InfluxDB en busca de anomalías que hagan saltar alarmas, por lo que puede realizar su trabajo sin necesidad de salir del cluster.

Por otra parte hemos desarrollado servicios accesibles desde fuera del cluster, por tanto visibles desde cualquier punto de internet (tipo NodePort) para Nginx e InfluxDB.

Con un Nginx podremos gestionar el acceso a los servicios de Grafana y Chronograf que mencionamos anteriormente, por otra parte InfluxDB proporciona una API que puede ser expuesta directamente, por lo que un servicio externo es la solución mas directa.

A continuación vemos el servicio NodePort que expone InfluxDB en el puerto 31096 del cluster.

apiVersion: v1
kind: Service
metadata:
labels:
app: influxdb-svc
name: influxdb-svc
namespace: monitoring
spec:
type: NodePort
ports:
- name: tcp-8086-8086
nodePort: 31096
port: 8086
protocol: TCP
targetPort: 8086
selector:
app: influxdb-app

Balanceadores Externos

Para acceder desde Internet a los servicios externos, podemos utilzar los balanceadores de Google Cloud. Permiten crear Backend dirigidos a nuestros servicios de Kubernetes especificando el puerto de nuestro servicio, y crear reglas de host para encaminar los dominios solicitados a los backends adecuados.

De esta forma podríamos crear un backend para InfluxDB que apunte a nuestro cluster de K8s al puerto 31096 expuesto por el servicio que hemos definido anteriormente, y luego definir una regla host que encamine las peticiones de a http://influxdb.mydomain.com hacia dicho backend.

Enlaces de utilidad

--

--