--- title: "Cómo monitorizar MySQL y PostgreSQL con Prometheus" description: "Instala mysqld_exporter y postgres_exporter en Ubuntu 24.04 con usuarios de solo lectura, conéctalos a Prometheus y crea alertas de caída y de conexiones." category: "Stack de Observabilidad" language: es source: https://cubepath.com/es/docs/stack-de-observabilidad/exportadores-mysql-y-postgresql-para-prometheus updated: 2026-09-25 --- # Cómo monitorizar MySQL y PostgreSQL con Prometheus 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 (paquete `postgresql`) 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 como `prometheus_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): ```bash sudo mysql ``` Ejecuta las siguientes sentencias, sustituyendo `your_strong_password` por una contraseña larga y aleatoria: ```sql 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: ```sql SHOW GRANTS FOR 'exporter'@'localhost'; EXIT; ``` ```output +----------------------------------------------------------------------------+ | 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: ```bash sudo useradd --system --no-create-home --shell /usr/sbin/nologin mysqld_exporter ``` Consulta la última versión en la [página de releases](https://github.com/prometheus/mysqld_exporter/releases) (0.20.0 al escribir esta guía), descárgala y verifica la suma de comprobación: ```bash 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 ``` ```output mysqld_exporter-0.20.0.linux-amd64.tar.gz: OK ``` Descomprime e instala el binario: ```bash 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 ``` ```output 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: ```bash sudo mkdir -p /etc/mysqld_exporter sudo nano /etc/mysqld_exporter/my.cnf ``` ```ini [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: ```bash 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 ```bash sudo nano /etc/systemd/system/mysqld_exporter.service ``` ```ini [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: ```bash 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: ```bash curl -s http://localhost:9104/metrics | grep -E '^mysql_(up|global_status_threads_connected|global_variables_max_connections) ' ``` ```output 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`: ```bash sudo -u postgres psql ``` Crea el rol, sustituyendo `your_strong_password` por una contraseña distinta de la de MySQL: ```sql CREATE USER postgres_exporter WITH PASSWORD 'your_strong_password' CONNECTION LIMIT 3; GRANT pg_monitor TO postgres_exporter; \q ``` ```output 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](https://github.com/prometheus-community/postgres_exporter/releases) (0.20.1 al escribir esta guía) y verifícala: ```bash 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 ``` ```output postgres_exporter-0.20.1.linux-amd64.tar.gz: OK ``` Instala el binario: ```bash 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): ```bash sudo mkdir -p /etc/postgres_exporter sudo nano /etc/postgres_exporter/postgres_exporter.env ``` ```ini DATA_SOURCE_NAME=postgresql://postgres_exporter:your_strong_password@127.0.0.1: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. ```bash sudo chmod 600 /etc/postgres_exporter/postgres_exporter.env ``` Crea la unidad de systemd: ```bash sudo nano /etc/systemd/system/postgres_exporter.service ``` ```ini [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: ```bash sudo systemctl daemon-reload sudo systemctl enable --now postgres_exporter curl -s http://localhost:9187/metrics | grep -E '^pg_(up|settings_max_connections) ' ``` ```output 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: ```bash 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: ```bash 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: ```yaml - 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: ```bash 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: ```bash sudo nano /etc/prometheus/database_alerts.yml ``` ```yaml 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`: ```yaml rule_files: - "database_alerts.yml" ``` Valida y reinicia: ```bash promtool check rules /etc/prometheus/database_alerts.yml promtool check config /etc/prometheus/prometheus.yml sudo systemctl restart prometheus ``` ```output 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.` (MySQL) o `--no-collector.` (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.