19 KiB
weight | title | date | draft | summary | resources | categories | tags | ||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
4 | Monitorización de contenedores Docker con cAdvisor, Prometheus y Grafana | 2022-01-06T22:40:20+0100 | false | Notas sobre monitorización de contenedores |
|
|
|
{{< admonition type=warning title="Work in progress" open=true >}}
Apuntes incompletos de monitorización de contenedores Docker
{{< /admonition >}}
Monitorización
Después de conseguir instalar Jam Session en mi Docker y ver como montan la monitorización del sistema me han entrado ganas de probar a montar algo para todos los contenedores instalados en mi VPS.
{{< admonition type=tip title="Referencias" open=true >}}
- Monitoring Docker Services with Prometheus
- Monitoring setup with docker-compose
- Un template básico en Github
- Otro docker-compose programado por Eisteinish Muy completo, también en Github {{< /admonition >}}
Prometheus y cAdvisor
Inspirándonos en la configuración de monitorización que venía con Jam Session, vamos a intentar configurar un sistema propio. Vamos a usar también Grafana y Prometheus, pero añadiendo cAdvisor a la receta como nos proponen en la propia web de Prometheus (la combinación de cAdvisor, Prometheus y Grafana parece un estándar de facto en la industria)
cAdvisor es un software de Google, escrito en Go, y programado específicamente para la captura de métricas de contenedores; necesita acceso a varios directorios del host (que mapearemos con bind-mounts en Docker) para capturar los datos. cAdvisor expone el puerto 8080
(interfaz web y REST api) y por defecto permite a Prometheus acceder a varias métricas.
Prometheus es un toolkit de monitorización de métricas. Prometheus almacena las métricas como series temporales, a las que se pueden asignar pares etiqueta valor opcionales (parece un funcionamiento análogo al de InfluxDB) Soporta un lenguaje de consultas denominado PromQL. Puede capturar métricas por pulling o por pushing. Normalmente, en procesos de vida larga, Prometheus hace scrapping en los orígenes de métricas definidos, pero también implementa un push-gateway para procesos de vida muy corta.
Prometheus, igual que cAdvisor, expone un interfaz web a través del cual podemos hacer consultas con PromQL y ver los resultados en modo tabla o modo gráfico.
Pero esto es solo la punta del iceberg. Entre otras cosas:
- Prometheus nos da la posibilidad de instalar otras dos aplicaciones: Alertmanager y Node Exporter
- Alertmanager se encarga de gestionar las Alertas que Prometheus genere a partir de las métricas para canalizarlas a distintos sistemas de comunicación (nos da muchísimas opciones)
- Node Explorer está especializado en monitorizar métricas de la máquina host.
- Hay todo un ecosistema de bibliotecas y clientes para exportar métricas compatibles con Prometheus desde multitud de sistemas.
cAdvisor
Empezamos por configurar el contenedor de cAdvisor. Creamos un directorio mon
para nuestros nuevos contenedores de monitorización, y en ese directorio nuestro fichero docker-compose.yml
:
version: '3.9'
services:
cadvisor:
image: gcr.io/cadvisor/cadvisor:latest
container_name: cadvisor
# ports:
# - 8080:8080
volumes:
- /:/rootfs:ro
- /var/run:/var/run:rw
- /sys:/sys:ro
- /var/lib/docker/:/var/lib/docker:ro
networks:
backend:
ipv4_address: 172.20.0.10
networks:
backend:
external: true
La configuración no tiene mucho misterio, hacemos bind-mounts de todos los recursos del host que necesita cAdvisor para recolectar métricas. Como queremos consumir todas las métricas en local comentamos el mapeo de puertos por que no queremos que el VPS deje acceso desde internet a las métricas. Por la misma razón Traefik no necesita saber nada de este contenedor (ya que no será accesible desde fuera)
En principio entiendo que cAdvisor captura las métricas directamente desde Docker así que de momento lo conectamos solo a la red backend
.
Con esto tenemos todo listo para lanzar el servicio cAdvisor con dcupd
(mi alias para docker-compose up -d
). Para comprobar el contenedor desde fuera del VPS podemos hacerlo por un túnel ssh:
ssh -L 9081:172.20.0.10:8080 dockadmin@fomalhaut
Ahora podemos conectar nuestro navegador a http://localhost:9081 y comprobar que cAdvisor está funcionando y detecta nuestros contenedores en el VPS.
{{< figure src="cAdvisor_web_a.png" title="Web de cAdvisor" >}}
Aunque podemos ver métricas (e incluso gráficas) de todos los contenedores no es muy "amistoso" y está bastante limitado. Pero su función principal es recolectar las métricas de los contenedores y eso lo hace perfectamente.
{{< figure src="cAdvisor_web_b.png" title="Gráficas en la web de cAdvisor" >}}
Prometheus
Ya tenemos cAdvisor funcionando. Vamos a configurar Prometheus creando su fichero de configuración:
mkdir mon/prometheus
touch mon/prometheus/prometheus.yml
Con el contenido:
global:
# How frequently to scrape targets (by default 1m)
scrape_interval: 15s
# How long until a scrape request times out (by default 10s)
# scrape_timeout: 10s
# How frequently evaluate the rules (by default 1m)
evaluation_interval: 15s
# Alerting section specifies settings related to Alertmanager
alerting:
alertmanagers:
- static_configs:
- targets:
# whatever you want
# Rule files specifies a list of globs. Rules and alerts are read from all matching files
# rule_files:
# List of scrape configurations
scrape_configs:
- job_name: 'prometheus'
static_configs:
- targets: ['prometheus:9090']
labels:
alias: 'prometheus'
- job_name: 'cadvisor'
static_configs:
- targets: ['cadvisor:8080']
labels:
alias: 'cadvisor'
En la sección global
configuramos los períodos de polling a los origenes de métricas y el período de evaluación de reglas de alarmas.
Dejamos preparada la sección alerting
donde configuraremos las reglas de alarmas. Y la sección rule_files
.
En la seccion scrape_configs
configuramos orígenes de métricas que queremos capturar. De momento configuramos el propio contenedor prometheus
, que publicará sus metricas en el puerto 9090, y configuramos también el contenedor cadvisor
que como ya vimos expone sus métricas en el puerto 8080. Al definir los orígenes de las métricas usamos los nombres de red de los contenedores (dentro de la red Docker) y asignamos a cada uno una etiqueta que nos permita distinguir los datos más tarde.
Ahora nos toca levantar el contenedor de Prometheus, modificamos el fichero docker-compose.yml
:
version: '3.9'
networks:
frontend:
external: true
backend:
external: true
volumes:
prometheus_data:
name: prometheus_data
grafana_data:
name: grafana_data
services:
cadvisor:
image: gcr.io/cadvisor/cadvisor:latest
container_name: cadvisor
# ports:
# - 8080:8080
volumes:
- /:/rootfs:ro
- /var/run:/var/run:rw
- /sys:/sys:ro
- /var/lib/docker/:/var/lib/docker:ro
networks:
backend:
ipv4_address: 172.20.0.10
nodeexporter:
image: prom/node-exporter:v1.3.1
container_name: nodeexporter
volumes:
- /proc:/host/proc:ro
- /sys:/host/sys:ro
- /:/rootfs:ro
command:
- '--path.procfs=/host/proc'
- '--path.rootfs=/rootfs'
- '--path.sysfs=/host/sys'
- '--collector.filesystem.ignored-mount-points=^/(sys|proc|dev|host|etc)($$|/)'
restart: unless-stopped
networks:
backend:
ipv4_address: 172.20.0.11
prometheus:
image: prom/prometheus:v2.32.1
container_name: prometheus
# ports:
# - 9090:9090
volumes:
# - ${PWD}/prometheus/prometheus.yml:/etc/prometheus/prometheus.yml:ro
- ${PWD}/prometheus/:/etc/prometheus/:ro
- prometheus_data:/prometheus
command:
- '--config.file=/etc/prometheus/prometheus.yml'
- '--storage.tsdb.path=/prometheus'
- '--web.console.libraries=/usr/share/prometheus/console_libraries'
- '--web.console.templates=/usr/share/prometheus/consoles'
depends_on:
- cadvisor
networks:
backend:
ipv4_address: 172.20.0.12
Igual que con cAdvisor no queremos publicar el puerto de Prometheus en internet. En todo caso, si después nos interesa, lo expondremos via Traefik.
Igual que con cAdvisor podemos probar el acceso a Prometheus via túnel ssh. Una vez conectados al interfa web de Prometheus podemos comprobar el estado de los dos origenes de datos definidos para ver que los dos están activos (deberían).
Ejecutando:
ssh -L 9082:172.20.0.10:9090 dockadmin@fomalhaut
Podremos acceder a nuestro nuevo Prometheus en http://localhost:9082. Si queremos hacer pruebas de consultas en la documentación oficial tenemos unos cuantos ejemplos.
{{< figure src="prometheus_targets.png" title="Orígenes de datos en la web de Prometheus" >}}
Grafana
Vamos a añadir Grafana a nuestra plataforma de monitorización. Grafana es un software especializado en la elaboración de cuadros de mando (_Dashboards) y representación gráfica de valores. Y lo hace francamente bien. Permite definir tanto los orígenes de datos como los propios Dashboards mediante ficheros de configuración o desde el interfaz web. Además los Dashboards pueden exportarse e importarse como ficheros .json
. Incluso hay una página web donde se comparten los Dashboards entre usuarios.
La sección de Grafana en el fichero docker-compose.yml
quedaría
grafana:
image: grafana/grafana:8.3.3
container_name: grafana
user: "472"
depends_on:
- prometheus
# ports:
# - 3000:3000
volumes:
- grafana_data:/var/lib/grafana
- ${PWD}/grafana/provisioning/:/etc/grafana/provisioning/
env_file:
- ${PWD}/grafana/config.env
networks:
backend:
ipv4_address: 10.20.0.15
frontend:
ipv4_address: 10.21.0.15
restart: always
labels:
- "traefik.enable=true"
- "traefik.docker.network=frontend"
- "traefik.http.routers.grafana.rule=Host(`grafana.midominio.com`)"
- "traefik.http.routers.grafana.entrypoints=https"
- "traefik.http.routers.grafana.service=grafana"
- "traefik.http.services.grafana.loadbalancer.server.port=3000"
Tenemos que preparar el fichero de entorno mon/grafana/grafana.env
con los datos confidenciales de acceso a Grafana.
GF_SECURITY_ADMIN_USER=admin
GF_SECURITY_ADMIN_PASSWORD=unaBuenaPassword
GF_USERS_ALLOW_SIGN_UP=false
# GF_SERVER_ROOT_URL=https://grafana.midomino.com
Con esta configuración ya podemos levantar el contenedor de Grafana y comprobar que podemos acceder sin problemas. (Claro que antes tenemos que declarar la ruta en la DNS Zone)
{{< figure src="grafana_home.png" title="Web de Grafana" >}}
Como ya hemos comentado podríamos configurar completamente Grafana desde el interfaz web, pero vamos a crear la fuente de datos asociada a nuestro Prometheus desde el fichero de configuración de Grafana.
Para eso creamos el fichero mon/grafana/provisioning/datasources/datasource.yml
con el contenido:
apiVersion: 1
datasources:
- name: Prometheus
type: prometheus
access: proxy
orgId: 1
url: http://prometheus:9090
basicAuth: false
isDefault: true
editable: true
Una vez tengamos el fichero podemos reiniciar el contenedor de Grafana con dco restart grafana
(docker-compose restart grafana
si no usas mis alias). Después del reinicio podremos ver la nueva fuente de datos en la web de Grafana.
{{< figure src="grafana_promsrc.png" title="Grafana, datasource de Prometheus" >}}
Nuestro primer Dashboard lo importaremos directamente desde la web. Vamos a usar como ejemplo este Dashboard de la página web de Grafana. Vemos que tiene el id=193
. Podríamos descargarlo como fichero .json
pero nos basta con el id
para probarlo. Clickamos en el signo +
a la izquierda en la página web de nuestro Grafana y escogemos la opción Import
. Basta con teclear el id 193
para que tengamos el Dashboard definido en Grafana.
{{< figure src="grafana_contdash.png" title="Grafana: Dashboard de Contenedores" >}}
Prometheus: Añadiendo Node Exporter
Si queremos que Prometheus capture también métricas de la maquina host, podemos instalar Node Exporter. La sección del fichero docker-compose.yml
para el Node Exporter sería:
nodeexporter:
image: prom/node-exporter:v0.18.1
container_name: nodeexporter
volumes:
- /proc:/host/proc:ro
- /sys:/host/sys:ro
- /:/rootfs:ro
command:
- '--path.procfs=/host/proc'
- '--path.rootfs=/rootfs'
- '--path.sysfs=/host/sys'
- '--collector.filesystem.ignored-mount-points=^/(sys|proc|dev|host|etc)($$|/)'
restart: unless-stopped
networks:
backend:
ipv4_address: 172.20.0.11
Tenemos que añadir el nuevo origen de datos en el fichero mon/prometheus/prometheus.yml
(lineas 31 y siguientes) que nos quedaría:
global:
# How frequently to scrape targets (by default 1m)
scrape_interval: 15s
# How long until a scrape request times out (by default 10s)
# scrape_timeout: 10s
# How frequently evaluate the rules (by default 1m)
evaluation_interval: 15s
# Alerting section specifies settings related to Alertmanager
alerting:
alertmanagers:
- static_configs:
- targets:
# whatever you want
# Rule files specifies a list of globs. Rules and alerts are read from all matching files
# rule_files:
# List of scrape configurations
scrape_configs:
- job_name: 'prometheus'
static_configs:
- targets: ['prometheus:9090']
labels:
alias: 'prometheus'
- job_name: 'cadvisor'
static_configs:
- targets: ['cadvisor:8080']
labels:
alias: 'cadvisor'
- job_name: 'node-exporter'
scrape_interval: 5s
static_configs:
- targets: ['nodeexporter:9100']
labels:
alias: 'nodeexporter'
Con la nueva fuente de datos definida podemos reiniciar el contenedor Prometheus con dco restart prometheus
(docker-compose restart prometheus
)
Podemos volver a conectarnos a Prometheus via túnel ssh como ya describimos y comprobar que los Targets son ahora tres y están todos arriba.
{{< figure src="prometheus_targets_b.png" title="Prometheus con tres targets activos" >}}
Para ver los nuevos datos que aporta el Node Exporter a nuestro contenedor Prometheus tenemos que definir un nuevo Dashboard que nos los muestre en el contenedor Grafana.
Vamos a añadir el nuevo Dashboard definiendolo con un fichero .json
para ello definiremos dos ficheros en nuestro directorio mon/grafana/provisioning/dashboards
(más info)
El primer fichero dashboard.yml
define el provider y tiene el siguiente contenido:
apiVersion: 1
providers:
- name: 'Prometheus'
orgId: 1
folder: ''
type: file
disableDeletion: false
editable: true
options:
path: /etc/grafana/provisioning/dashboards
En el segundo fichero dco_prom.json
definiremos el Dashboard que hemos copiado de aquí.
Si reiniciamos ahora el contenedor de Grafana, dco restart grafana
veremos que ya tenemos los dos Dashboard disponibles; el que añadimos desde la red y el que hemos añadido ahora mismo por fichero.
En el nuevo Dashboard podemos ver ya métricas del host, como la memoria usada, la carga de CPU del host y la capacidad del sistema de ficheros.
Vemos también que el nuevo Dashboard viene preparado para visualizar Alerts pero no tenemos ninguna definida (todavía).
{{< figure src="grafana_nexporter.png" title="Dashboard con datos de Node Exporter" >}}
Prometheus: Definición de Alarmas
Vamos a definir condiciones de Alarma en Prometheus. Necesitamos crear un nuevo fichero mon/prometheus/alert.rules
, donde definiremos un par de alarmas:
groups:
- name: example
rules:
# Alert for any instance that is unreachable for >2 minutes.
- alert: service_down
expr: up == 0
for: 2m
labels:
severity: page
annotations:
summary: "Instance {{ $labels.instance }} down"
description: "{{ $labels.instance }} of job {{ $labels.job }} has been down for more than 2 minutes."
- alert: high_load
expr: node_load1 > 0.5
for: 2m
labels:
severity: page
annotations:
summary: "Instance {{ $labels.instance }} under high load"
description: "{{ $labels.instance }} of job {{ $labels.job }} is under high load."
Tenemos que editar también el fichero mon/prometheus/prometheus.yml
para añadir nuestro fichero alert.rules
en la sección de rule_files
que quedaría así:
# Rule files specifies a list of globs. Rules and alerts are read from all matching files
rule_files:
- 'alert.rules'
Si rearrancamos Prometheus (ya sabes dco restart prometheus
o docker-compose restart prometheus
) y se cumplen las condiciones para la alarma podremos verla en el Dashboard:
{{< figure src="grafana_alarm.png" title="Dashboard con Alarma" >}}
Las alarmas que hemos definido no tienen demasiado sentido, pero antes de profundizar en como definir alarmas significativas vamos a configurar las notificaciones a través de Alert Manager.