Prometheus no sabe consultar una base de datos directamente: necesita un exportador que se conecte a ella, lea sus estadísticas internas y las publique en formato Prometheus. En este tutorial instalarás mysqld_exporter para MySQL y postgres_exporter para PostgreSQL en Ubuntu 24.04, cada uno con un usuario de base de datos con los permisos mínimos y como servicio de systemd. Después los añadirás a Prometheus, revisarás las métricas más útiles y crearás alertas para caídas y exceso de conexiones.
Requisitos previos
Para seguir esta guía necesitas:
- Un servidor con Ubuntu 24.04 LTS de 64 bits (x86_64), por ejemplo un VPS de CubePath, con MySQL 8.0 (paquete
mysql-server), PostgreSQL 16 (paquetepostgresql) o ambos. Si solo usas uno, sigue únicamente su parte. - Un usuario no root con privilegios
sudo. - Un servidor Prometheus con acceso de red a este servidor, con su configuración en
/etc/prometheus/prometheus.yml. En la guía su IP aparece comoprometheus_server_ip.
Las secciones de MySQL también sirven para MariaDB, que acepta los mismos permisos.
Paso 1: Crear el usuario de monitorización en MySQL
El exportador solo necesita leer estadísticas, así que conviene darle un usuario propio con permisos mínimos y un límite de conexiones para que nunca pueda saturar el servidor. Abre la consola de MySQL como root (en Ubuntu, root se autentica por socket):
sudo mysql
Ejecuta las siguientes sentencias, sustituyendo your_strong_password por una contraseña larga y aleatoria:
CREATE USER 'exporter'@'localhost' IDENTIFIED BY 'your_strong_password' WITH MAX_USER_CONNECTIONS 3;
GRANT PROCESS, REPLICATION CLIENT, SELECT ON *.* TO 'exporter'@'localhost';
PROCESS permite leer la lista de procesos y el estado de InnoDB, REPLICATION CLIENT el estado de la replicación, y SELECT las tablas de information_schema y performance_schema. Comprueba los permisos y sal:
SHOW GRANTS FOR 'exporter'@'localhost';
EXIT;
+----------------------------------------------------------------------------+
| Grants for exporter@localhost |
+----------------------------------------------------------------------------+
| GRANT SELECT, PROCESS, REPLICATION CLIENT ON *.* TO `exporter`@`localhost` |
+----------------------------------------------------------------------------+
Paso 2: Instalar mysqld_exporter
Crea un usuario de sistema para ejecutar el exportador:
sudo useradd --system --no-create-home --shell /usr/sbin/nologin mysqld_exporter
Consulta la última versión en la página de releases (0.20.0 al escribir esta guía), descárgala y verifica la suma de comprobación:
MYSQLD_EXPORTER_VERSION=0.20.0
cd /tmp
curl -fsSLO "https://github.com/prometheus/mysqld_exporter/releases/download/v${MYSQLD_EXPORTER_VERSION}/mysqld_exporter-${MYSQLD_EXPORTER_VERSION}.linux-amd64.tar.gz"
curl -fsSL -o mysqld_exporter_sha256sums.txt "https://github.com/prometheus/mysqld_exporter/releases/download/v${MYSQLD_EXPORTER_VERSION}/sha256sums.txt"
sha256sum -c --ignore-missing mysqld_exporter_sha256sums.txt
mysqld_exporter-0.20.0.linux-amd64.tar.gz: OK
Descomprime e instala el binario:
tar -xzf "mysqld_exporter-${MYSQLD_EXPORTER_VERSION}.linux-amd64.tar.gz"
sudo install -m 0755 "mysqld_exporter-${MYSQLD_EXPORTER_VERSION}.linux-amd64/mysqld_exporter" /usr/local/bin/
mysqld_exporter --version
mysqld_exporter, version 0.20.0 (branch: HEAD, revision: ...)
Paso 3: Guardar las credenciales de MySQL
Las versiones actuales de mysqld_exporter ya no leen la variable DATA_SOURCE_NAME: las credenciales van en un archivo con formato my.cnf. Créalo en un directorio propio:
sudo mkdir -p /etc/mysqld_exporter
sudo nano /etc/mysqld_exporter/my.cnf
[client]
user = exporter
password = your_strong_password
Sin host ni port, el exportador se conecta a localhost:3306. Protege el archivo para que solo root y el usuario del servicio puedan leerlo:
sudo chown root:mysqld_exporter /etc/mysqld_exporter/my.cnf
sudo chmod 640 /etc/mysqld_exporter/my.cnf
Paso 4: Crear el servicio de mysqld_exporter
sudo nano /etc/systemd/system/mysqld_exporter.service
[Unit]
Description=Prometheus MySQL Exporter
Wants=network-online.target
After=network-online.target mysql.service
[Service]
User=mysqld_exporter
Group=mysqld_exporter
ExecStart=/usr/local/bin/mysqld_exporter \
--config.my-cnf=/etc/mysqld_exporter/my.cnf \
--web.listen-address=:9104
Restart=on-failure
NoNewPrivileges=true
ProtectSystem=full
ProtectHome=true
[Install]
WantedBy=multi-user.target
Los recolectores activos por defecto cubren el estado global (SHOW GLOBAL STATUS), las variables de configuración y el estado de la replicación. No actives recolectores adicionales como --collect.info_schema.tables en servidores con miles de tablas: cada scrape consultaría information_schema completo.
Arranca el servicio:
sudo systemctl daemon-reload
sudo systemctl enable --now mysqld_exporter
Comprueba que el exportador se conecta a MySQL. La métrica mysql_up vale 1 si la conexión funciona:
curl -s http://localhost:9104/metrics | grep -E '^mysql_(up|global_status_threads_connected|global_variables_max_connections) '
mysql_global_status_threads_connected 2
mysql_global_variables_max_connections 151
mysql_up 1
Si mysql_up vale 0, revisa el motivo con sudo journalctl -u mysqld_exporter -n 20. Normalmente es un error de contraseña (Access denied) o de permisos del archivo my.cnf.
Paso 5: Crear el usuario de monitorización en PostgreSQL
PostgreSQL incluye el rol predefinido pg_monitor, que da acceso de lectura a todas las vistas de estadísticas sin permisos sobre los datos. Abre psql como el usuario postgres:
sudo -u postgres psql
Crea el rol, sustituyendo your_strong_password por una contraseña distinta de la de MySQL:
CREATE USER postgres_exporter WITH PASSWORD 'your_strong_password' CONNECTION LIMIT 3;
GRANT pg_monitor TO postgres_exporter;
\q
CREATE ROLE
GRANT ROLE
La configuración por defecto de pg_hba.conf en Ubuntu permite conexiones con contraseña (scram-sha-256) desde 127.0.0.1, así que no hace falta modificarla.
Paso 6: Instalar y configurar postgres_exporter
Crea el usuario de sistema, descarga la última versión desde la página de releases (0.20.1 al escribir esta guía) y verifícala:
sudo useradd --system --no-create-home --shell /usr/sbin/nologin postgres_exporter
POSTGRES_EXPORTER_VERSION=0.20.1
cd /tmp
curl -fsSLO "https://github.com/prometheus-community/postgres_exporter/releases/download/v${POSTGRES_EXPORTER_VERSION}/postgres_exporter-${POSTGRES_EXPORTER_VERSION}.linux-amd64.tar.gz"
curl -fsSL -o postgres_exporter_sha256sums.txt "https://github.com/prometheus-community/postgres_exporter/releases/download/v${POSTGRES_EXPORTER_VERSION}/sha256sums.txt"
sha256sum -c --ignore-missing postgres_exporter_sha256sums.txt
postgres_exporter-0.20.1.linux-amd64.tar.gz: OK
Instala el binario:
tar -xzf "postgres_exporter-${POSTGRES_EXPORTER_VERSION}.linux-amd64.tar.gz"
sudo install -m 0755 "postgres_exporter-${POSTGRES_EXPORTER_VERSION}.linux-amd64/postgres_exporter" /usr/local/bin/
postgres_exporter lee la cadena de conexión de la variable de entorno DATA_SOURCE_NAME. Guárdala en un archivo de entorno que solo root pueda leer (systemd lo carga antes de cambiar al usuario del servicio):
sudo mkdir -p /etc/postgres_exporter
sudo nano /etc/postgres_exporter/postgres_exporter.env
DATA_SOURCE_NAME=postgresql://postgres_exporter:[email protected]:5432/postgres?sslmode=disable
Si la contraseña contiene caracteres como @, : o /, codifícalos en formato URL (por ejemplo, @ se escribe %40). La opción sslmode=disable es adecuada porque la conexión no sale de la máquina.
sudo chmod 600 /etc/postgres_exporter/postgres_exporter.env
Crea la unidad de systemd:
sudo nano /etc/systemd/system/postgres_exporter.service
[Unit]
Description=Prometheus PostgreSQL Exporter
Wants=network-online.target
After=network-online.target postgresql.service
[Service]
User=postgres_exporter
Group=postgres_exporter
EnvironmentFile=/etc/postgres_exporter/postgres_exporter.env
ExecStart=/usr/local/bin/postgres_exporter \
--web.listen-address=:9187
Restart=on-failure
NoNewPrivileges=true
ProtectSystem=full
ProtectHome=true
[Install]
WantedBy=multi-user.target
Arranca el servicio y comprueba las métricas:
sudo systemctl daemon-reload
sudo systemctl enable --now postgres_exporter
curl -s http://localhost:9187/metrics | grep -E '^pg_(up|settings_max_connections) '
pg_settings_max_connections 100
pg_up 1
En el journal verás un aviso Error loading config sobre postgres_exporter.yml. Ese archivo solo es necesario para funciones avanzadas (autenticación para varios destinos) y el aviso es inofensivo.
Paso 7: Abrir los puertos a Prometheus
Los exportadores no tienen autenticación y las métricas revelan nombres de bases de datos y usuarios, así que solo deben ser accesibles desde el servidor de Prometheus:
sudo ufw allow from prometheus_server_ip to any port 9104 proto tcp
sudo ufw allow from prometheus_server_ip to any port 9187 proto tcp
sudo ufw status
Si Prometheus corre en este mismo servidor, no abras los puertos y cambia --web.listen-address a 127.0.0.1:9104 y 127.0.0.1:9187 en las unidades.
Paso 8: Añadir los exportadores a Prometheus
En el servidor de Prometheus, edita la configuración:
sudo nano /etc/prometheus/prometheus.yml
Añade dos trabajos dentro de scrape_configs, sustituyendo db_server_ip por la IP del servidor de base de datos:
- job_name: "mysql"
static_configs:
- targets: ["db_server_ip:9104"]
labels:
environment: "production"
- job_name: "postgresql"
static_configs:
- targets: ["db_server_ip:9187"]
labels:
environment: "production"
Valida y reinicia Prometheus:
promtool check config /etc/prometheus/prometheus.yml
sudo systemctl restart prometheus
En Status > Targets los trabajos mysql y postgresql deben aparecer como UP. Ejecuta mysql_up y pg_up en la pestaña Graph para confirmar que ambos devuelven 1.
Paso 9: Consultar las métricas clave
Estas son las consultas PromQL más útiles para empezar, válidas tanto en Prometheus como en paneles de Grafana.
MySQL
| Qué mide | Consulta |
|---|---|
| Consultas por segundo | rate(mysql_global_status_queries[5m]) |
| Conexiones abiertas | mysql_global_status_threads_connected |
| Uso de conexiones (%) | mysql_global_status_threads_connected / mysql_global_variables_max_connections * 100 |
| Hilos ejecutando consultas | mysql_global_status_threads_running |
| Consultas lentas por segundo | rate(mysql_global_status_slow_queries[5m]) |
| Conexiones fallidas por segundo | rate(mysql_global_status_aborted_connects[5m]) |
Slow_queries cuenta las consultas que superan long_query_time (10 segundos por defecto), aunque el log de consultas lentas esté desactivado. Si quieres que sea útil, baja ese valor en la configuración de MySQL, por ejemplo a 1 segundo.
PostgreSQL
| Qué mide | Consulta |
|---|---|
| Conexiones por base de datos | pg_stat_database_numbackends |
| Uso de conexiones (%) | sum by (instance, job) (pg_stat_database_numbackends) / pg_settings_max_connections * 100 |
| Transacciones confirmadas por segundo | sum by (datname) (rate(pg_stat_database_xact_commit[5m])) |
| Ratio de aciertos de caché (%) | sum(rate(pg_stat_database_blks_hit[5m])) / (sum(rate(pg_stat_database_blks_hit[5m])) + sum(rate(pg_stat_database_blks_read[5m]))) * 100 |
| Tamaño de cada base de datos | pg_database_size_bytes |
| Bloqueos mutuos (deadlocks) | increase(pg_stat_database_deadlocks[1h]) |
Un ratio de aciertos de caché por debajo del 99 % de forma sostenida en una base de datos OLTP suele indicar que shared_buffers o la RAM del servidor se han quedado cortos.
Para Grafana, en grafana.com hay paneles publicados por la comunidad para ambos exportadores: busca "mysqld_exporter" o "postgres_exporter" e impórtalos desde Dashboards > New > Import.
Paso 10: Crear alertas
Crea un archivo de reglas en el servidor de Prometheus:
sudo nano /etc/prometheus/database_alerts.yml
groups:
- name: databases
rules:
- alert: MySQLDown
expr: mysql_up == 0
for: 1m
labels:
severity: critical
annotations:
summary: "MySQL no responde en {{ $labels.instance }}"
description: "mysqld_exporter no puede conectarse a MySQL desde hace más de 1 minuto."
- alert: MySQLTooManyConnections
expr: mysql_global_status_threads_connected / mysql_global_variables_max_connections > 0.8
for: 5m
labels:
severity: warning
annotations:
summary: "MySQL usa más del 80 % de sus conexiones en {{ $labels.instance }}"
- alert: PostgreSQLDown
expr: pg_up == 0
for: 1m
labels:
severity: critical
annotations:
summary: "PostgreSQL no responde en {{ $labels.instance }}"
description: "postgres_exporter no puede conectarse a PostgreSQL desde hace más de 1 minuto."
- alert: PostgreSQLTooManyConnections
expr: sum by (instance, job) (pg_stat_database_numbackends) / pg_settings_max_connections > 0.8
for: 5m
labels:
severity: warning
annotations:
summary: "PostgreSQL usa más del 80 % de sus conexiones en {{ $labels.instance }}"
- alert: PostgreSQLDeadlocks
expr: increase(pg_stat_database_deadlocks[10m]) > 0
labels:
severity: warning
annotations:
summary: "Deadlocks en la base de datos {{ $labels.datname }} de {{ $labels.instance }}"
Si el exportador entero deja de responder, mysql_up y pg_up desaparecen en lugar de valer 0. Para cubrir ese caso, Prometheus genera la métrica up para cada destino: una regla con up{job=~"mysql|postgresql"} == 0 avisa cuando el exportador está caído.
Registra el archivo en /etc/prometheus/prometheus.yml dentro de rule_files:
rule_files:
- "database_alerts.yml"
Valida y reinicia:
promtool check rules /etc/prometheus/database_alerts.yml
promtool check config /etc/prometheus/prometheus.yml
sudo systemctl restart prometheus
Checking /etc/prometheus/database_alerts.yml
SUCCESS: 5 rules found
Para probar la alerta, detén MySQL (sudo systemctl stop mysql) en un entorno de pruebas: mysql_up pasará a 0 y, al cabo de un minuto, MySQLDown aparecerá en estado Firing en la pestaña Alerts. Vuelve a arrancarlo con sudo systemctl start mysql.
Solución de problemas
mysql_up 0 con Access denied for user 'exporter'@'localhost'. La contraseña de my.cnf no coincide con la del usuario, o el usuario se creó con otro host (por ejemplo 'exporter'@'%'). Compruébalo con SELECT user, host FROM mysql.user WHERE user = 'exporter';.
pg_up 0 con password authentication failed. Revisa la contraseña de DATA_SOURCE_NAME y que los caracteres especiales estén codificados en formato URL. Recuerda reiniciar el servicio tras editar el archivo de entorno.
Faltan métricas de algunas vistas de PostgreSQL. El rol no tiene pg_monitor. Comprueba con \du postgres_exporter en psql que aparece {pg_monitor} en la columna de pertenencia.
Scrapes lentos o context deadline exceeded en Prometheus. Algún recolector tarda demasiado en una base de datos grande. Revisa sudo journalctl -u mysqld_exporter o sudo journalctl -u postgres_exporter para ver cuál y desactívalo con su opción --no-collect.<nombre> (MySQL) o --no-collector.<nombre> (PostgreSQL).
Conclusión
Tienes MySQL y PostgreSQL publicando sus métricas en Prometheus mediante exportadores con usuarios de solo lectura, limitados en conexiones y accesibles solo desde Prometheus, además de alertas para caídas, saturación de conexiones y deadlocks. Como siguientes pasos, puedes activar la extensión pg_stat_statements y el recolector --collector.stat_statements para ver las consultas más costosas de PostgreSQL, enviar las alertas a Alertmanager o Grafana Alerting, y añadir node_exporter al mismo servidor para correlacionar la carga de la base de datos con CPU, memoria y disco.
