Monitor MySQL backup health with automated mysqldump, Prometheus metrics collection, and Grafana dashboards. Get instant alerts when backups fail or take too long.
Prerequisites
- MySQL server running
- Prometheus and Grafana installed
- Root or sudo access
- Basic understanding of systemd timers
What this solves
Automated MySQL backups are critical, but they fail silently. This tutorial sets up comprehensive backup monitoring using mysqldump with systemd timers, Prometheus MySQL Exporter for metrics collection, and Grafana dashboards for visualization. You'll get instant alerts when backups fail, take too long, or when backup files are missing.
Step-by-step configuration
Install required packages
Install MySQL client tools, systemd timer utilities, and monitoring components.
sudo apt update
sudo apt install -y mysql-client prometheus-mysqld-exporter gzip curl
Create backup directory structure
Set up directories for backup storage with proper permissions.
sudo mkdir -p /var/backups/mysql/{daily,logs}
sudo mkdir -p /var/lib/prometheus/mysql-backup-metrics
sudo chown mysql:mysql /var/backups/mysql
sudo chmod 755 /var/backups/mysql
Create MySQL backup user
Create a dedicated MySQL user with minimal required privileges for backups.
mysql -u root -p -e "CREATE USER 'backup_user'@'localhost' IDENTIFIED BY 'SecureBackupPass123!';"
mysql -u root -p -e "GRANT SELECT, SHOW DATABASES, LOCK TABLES, SHOW VIEW, EVENT, TRIGGER ON . TO 'backup_user'@'localhost';"
mysql -u root -p -e "FLUSH PRIVILEGES;"
Configure MySQL credentials
Store MySQL credentials securely for automated backup access.
[client]
user=backup_user
password=SecureBackupPass123!
host=localhost
port=3306
sudo chmod 600 /etc/mysql/backup.cnf
sudo chown mysql:mysql /etc/mysql/backup.cnf
Create backup script with metrics
Build a comprehensive backup script that generates Prometheus metrics.
#!/bin/bash
Configuration
BACKUP_DIR="/var/backups/mysql/daily"
LOG_FILE="/var/backups/mysql/logs/backup-$(date +%Y%m%d-%H%M%S).log"
METRICS_FILE="/var/lib/prometheus/mysql-backup-metrics/mysql_backup.prom"
RETENTION_DAYS=7
MAX_BACKUP_TIME_SECONDS=3600
Start metrics
START_TIME=$(date +%s)
echo "mysql_backup_start_timestamp $START_TIME" > $METRICS_FILE
echo "mysql_backup_running 1" >> $METRICS_FILE
Function to update metrics on exit
cleanup() {
END_TIME=$(date +%s)
DURATION=$((END_TIME - START_TIME))
echo "mysql_backup_running 0" >> $METRICS_FILE
echo "mysql_backup_end_timestamp $END_TIME" >> $METRICS_FILE
echo "mysql_backup_duration_seconds $DURATION" >> $METRICS_FILE
echo "mysql_backup_last_run_timestamp $END_TIME" >> $METRICS_FILE
}
trap cleanup EXIT
Log function
log() {
echo "$(date '+%Y-%m-%d %H:%M:%S') $1" | tee -a $LOG_FILE
}
log "Starting MySQL backup process"
Get database list
DATABASES=$(mysql --defaults-file=/etc/mysql/backup.cnf -e "SHOW DATABASES;" | grep -Ev "(Database|information_schema|performance_schema|mysql|sys)")
if [ -z "$DATABASES" ]; then
log "ERROR: No databases found to backup"
echo "mysql_backup_success 0" >> $METRICS_FILE
echo "mysql_backup_databases_total 0" >> $METRICS_FILE
exit 1
fi
Count databases
DB_COUNT=$(echo "$DATABASES" | wc -l)
echo "mysql_backup_databases_total $DB_COUNT" >> $METRICS_FILE
log "Found $DB_COUNT databases to backup"
Create timestamp for backup files
TIMESTAMP=$(date +%Y%m%d_%H%M%S)
Backup each database
SUCCESS_COUNT=0
TOTAL_SIZE=0
for DB in $DATABASES; do
log "Backing up database: $DB"
BACKUP_FILE="$BACKUP_DIR/${DB}_${TIMESTAMP}.sql.gz"
# Perform backup with compression
if mysqldump --defaults-file=/etc/mysql/backup.cnf \
--single-transaction \
--routines \
--triggers \
--events \
--hex-blob \
--opt \
"$DB" | gzip > "$BACKUP_FILE"; then
# Verify backup file exists and has content
if [ -s "$BACKUP_FILE" ]; then
FILE_SIZE=$(stat -f%z "$BACKUP_FILE" 2>/dev/null || stat -c%s "$BACKUP_FILE")
TOTAL_SIZE=$((TOTAL_SIZE + FILE_SIZE))
SUCCESS_COUNT=$((SUCCESS_COUNT + 1))
log "SUCCESS: $DB backed up successfully (${FILE_SIZE} bytes)"
else
log "ERROR: Backup file for $DB is empty"
rm -f "$BACKUP_FILE"
fi
else
log "ERROR: Failed to backup database $DB"
fi
done
Update success metrics
echo "mysql_backup_databases_success $SUCCESS_COUNT" >> $METRICS_FILE
echo "mysql_backup_total_size_bytes $TOTAL_SIZE" >> $METRICS_FILE
Cleanup old backups
log "Cleaning up backups older than $RETENTION_DAYS days"
find $BACKUP_DIR -name "*.sql.gz" -mtime +$RETENTION_DAYS -delete
find /var/backups/mysql/logs -name "*.log" -mtime +$RETENTION_DAYS -delete
Final status
if [ $SUCCESS_COUNT -eq $DB_COUNT ]; then
echo "mysql_backup_success 1" >> $METRICS_FILE
log "SUCCESS: All $DB_COUNT databases backed up successfully"
exit 0
else
echo "mysql_backup_success 0" >> $METRICS_FILE
log "ERROR: Only $SUCCESS_COUNT of $DB_COUNT databases backed up successfully"
exit 1
fi
sudo chmod +x /usr/local/bin/mysql-backup.sh
sudo chown mysql:mysql /usr/local/bin/mysql-backup.sh
Create systemd service and timer
Set up systemd timer for automated daily backups with logging.
[Unit]
Description=MySQL Database Backup
Wants=mysql.service
After=mysql.service
[Service]
Type=oneshot
User=mysql
Group=mysql
ExecStart=/usr/local/bin/mysql-backup.sh
StandardOutput=journal
StandardError=journal
TimeoutSec=3600
[Unit]
Description=Run MySQL backup daily at 2 AM
Requires=mysql-backup.service
[Timer]
OnCalendar=--* 02:00:00
RandomizedDelaySec=300
Persistent=true
[Install]
WantedBy=timers.target
sudo systemctl daemon-reload
sudo systemctl enable mysql-backup.timer
sudo systemctl start mysql-backup.timer
Configure MySQL Exporter for monitoring
Set up Prometheus MySQL Exporter to collect database metrics.
[Unit]
Description=Prometheus MySQL Exporter
Wants=mysql.service
After=mysql.service
[Service]
Type=simple
Restart=always
User=prometheus
Group=prometheus
ExecStart=/usr/bin/mysqld_exporter \
--config.my-cnf=/etc/mysql/backup.cnf \
--web.listen-address=127.0.0.1:9104 \
--collect.info_schema.tables \
--collect.info_schema.innodb_metrics \
--collect.global_status \
--collect.binlog_size \
--collect.info_schema.processlist
[Install]
WantedBy=multi-user.target
sudo systemctl daemon-reload
sudo systemctl enable mysql-exporter.service
sudo systemctl start mysql-exporter.service
Configure Node Exporter for backup metrics
Set up textfile collector to expose backup metrics to Prometheus.
sudo apt install -y prometheus-node-exporter
[Unit]
Description=Prometheus Node Exporter
Wants=network-online.target
After=network-online.target
[Service]
Type=simple
Restart=always
User=prometheus
Group=prometheus
ExecStart=/usr/local/bin/node_exporter \
--web.listen-address=127.0.0.1:9100 \
--collector.textfile.directory=/var/lib/prometheus/mysql-backup-metrics
[Install]
WantedBy=multi-user.target
sudo mkdir -p /var/lib/prometheus/mysql-backup-metrics
sudo chown prometheus:prometheus /var/lib/prometheus/mysql-backup-metrics
sudo chmod 755 /var/lib/prometheus/mysql-backup-metrics
sudo systemctl daemon-reload
sudo systemctl enable node-exporter.service
sudo systemctl start node-exporter.service
Configure Prometheus scraping
Add MySQL and backup metrics to Prometheus configuration.
global:
scrape_interval: 15s
evaluation_interval: 15s
rule_files:
- "/etc/prometheus/rules/mysql-backup.yml"
alerting:
alertmanagers:
- static_configs:
- targets:
- localhost:9093
scrape_configs:
- job_name: 'mysql-exporter'
static_configs:
- targets: ['127.0.0.1:9104']
scrape_interval: 30s
metrics_path: /metrics
- job_name: 'node-exporter-mysql-backup'
static_configs:
- targets: ['127.0.0.1:9100']
scrape_interval: 60s
metrics_path: /metrics
Create Prometheus alerting rules
Set up alert rules for backup failures and performance issues.
groups:
- name: mysql_backup_alerts
rules:
- alert: MySQLBackupFailed
expr: mysql_backup_success == 0
for: 5m
labels:
severity: critical
annotations:
summary: "MySQL backup failed"
description: "MySQL backup job failed. Last successful backup was {{ $value }} seconds ago."
- alert: MySQLBackupTooLong
expr: mysql_backup_duration_seconds > 3600
for: 0m
labels:
severity: warning
annotations:
summary: "MySQL backup taking too long"
description: "MySQL backup has been running for {{ $value }} seconds, exceeding the 1-hour threshold."
- alert: MySQLBackupNotRunning
expr: time() - mysql_backup_last_run_timestamp > 86400 * 2
for: 10m
labels:
severity: critical
annotations:
summary: "MySQL backup hasn't run recently"
description: "MySQL backup hasn't run for {{ $value | humanizeDuration }}. Check the systemd timer."
- alert: MySQLBackupPartialFailure
expr: mysql_backup_databases_success < mysql_backup_databases_total
for: 5m
labels:
severity: warning
annotations:
summary: "Some MySQL databases failed to backup"
description: "Only {{ $labels.mysql_backup_databases_success }} out of {{ $labels.mysql_backup_databases_total }} databases were backed up successfully."
- alert: MySQLBackupSizeUnusuallySmall
expr: mysql_backup_total_size_bytes < 1000000 # Less than 1MB
for: 5m
labels:
severity: warning
annotations:
summary: "MySQL backup size suspiciously small"
description: "MySQL backup size is {{ $value }} bytes, which seems unusually small. Verify backup integrity."
- name: mysql_performance_alerts
rules:
- alert: MySQLHighConnections
expr: mysql_global_status_threads_connected / mysql_global_variables_max_connections > 0.8
for: 5m
labels:
severity: warning
annotations:
summary: "MySQL connection usage high"
description: "MySQL is using {{ $value | humanizePercentage }} of available connections."
- alert: MySQLSlowQueries
expr: rate(mysql_global_status_slow_queries[5m]) > 5
for: 2m
labels:
severity: warning
annotations:
summary: "MySQL slow queries increasing"
description: "MySQL slow queries rate is {{ $value }} queries/second over the last 5 minutes."
sudo mkdir -p /etc/prometheus/rules
sudo chown prometheus:prometheus /etc/prometheus/rules/mysql-backup.yml
sudo systemctl restart prometheus
Configure Alertmanager
Set up Alertmanager for notification routing and email alerts.
global:
smtp_smarthost: 'localhost:587'
smtp_from: 'alerts@example.com'
smtp_auth_username: 'alerts@example.com'
smtp_auth_password: 'your-email-password'
route:
group_by: ['alertname']
group_wait: 30s
group_interval: 5m
repeat_interval: 12h
receiver: 'mysql-backup-team'
routes:
- match:
severity: critical
receiver: 'mysql-backup-critical'
receivers:
- name: 'mysql-backup-team'
email_configs:
- to: 'ops-team@example.com'
subject: 'MySQL Backup Alert: {{ range .Alerts }}{{ .Annotations.summary }}{{ end }}'
body: |
{{ range .Alerts }}
Alert: {{ .Annotations.summary }}
Description: {{ .Annotations.description }}
Instance: {{ .Labels.instance }}
Severity: {{ .Labels.severity }}
{{ end }}
- name: 'mysql-backup-critical'
email_configs:
- to: 'ops-team@example.com,dba-team@example.com'
subject: 'CRITICAL MySQL Backup Issue: {{ range .Alerts }}{{ .Annotations.summary }}{{ end }}'
body: |
CRITICAL ALERT - Immediate action required
{{ range .Alerts }}
Alert: {{ .Annotations.summary }}
Description: {{ .Annotations.description }}
Instance: {{ .Labels.instance }}
Time: {{ .StartsAt }}
{{ end }}
inhibit_rules:
- source_match:
severity: 'critical'
target_match:
severity: 'warning'
equal: ['alertname', 'instance']
sudo apt install -y prometheus-alertmanager
sudo systemctl enable alertmanager
sudo systemctl start alertmanager
Create Grafana dashboard
Import a comprehensive MySQL backup monitoring dashboard into Grafana.
{
"dashboard": {
"id": null,
"title": "MySQL Backup Monitoring",
"tags": ["mysql", "backup", "monitoring"],
"timezone": "browser",
"panels": [
{
"id": 1,
"title": "Backup Success Rate",
"type": "stat",
"targets": [
{
"expr": "mysql_backup_success",
"legendFormat": "Success",
"refId": "A"
}
],
"fieldConfig": {
"defaults": {
"color": {
"mode": "thresholds"
},
"thresholds": {
"steps": [
{"color": "red", "value": 0},
{"color": "green", "value": 1}
]
}
}
},
"gridPos": {"h": 8, "w": 12, "x": 0, "y": 0}
},
{
"id": 2,
"title": "Backup Duration",
"type": "graph",
"targets": [
{
"expr": "mysql_backup_duration_seconds",
"legendFormat": "Duration (seconds)",
"refId": "A"
}
],
"yAxes": [
{
"label": "Seconds",
"min": 0
}
],
"gridPos": {"h": 8, "w": 12, "x": 12, "y": 0}
},
{
"id": 3,
"title": "Database Backup Status",
"type": "table",
"targets": [
{
"expr": "mysql_backup_databases_total",
"legendFormat": "Total DBs",
"refId": "A"
},
{
"expr": "mysql_backup_databases_success",
"legendFormat": "Successful DBs",
"refId": "B"
}
],
"gridPos": {"h": 8, "w": 24, "x": 0, "y": 8}
},
{
"id": 4,
"title": "Backup Size Trend",
"type": "graph",
"targets": [
{
"expr": "mysql_backup_total_size_bytes",
"legendFormat": "Total Size (bytes)",
"refId": "A"
}
],
"yAxes": [
{
"label": "Bytes",
"min": 0,
"logBase": 2
}
],
"gridPos": {"h": 8, "w": 12, "x": 0, "y": 16}
},
{
"id": 5,
"title": "MySQL Performance",
"type": "graph",
"targets": [
{
"expr": "mysql_global_status_threads_connected",
"legendFormat": "Connected Threads",
"refId": "A"
},
{
"expr": "rate(mysql_global_status_slow_queries[5m])",
"legendFormat": "Slow Queries/sec",
"refId": "B"
}
],
"gridPos": {"h": 8, "w": 12, "x": 12, "y": 16}
}
],
"time": {
"from": "now-24h",
"to": "now"
},
"refresh": "30s"
}
}
curl -X POST \
http://admin:admin@localhost:3000/api/dashboards/db \
-H 'Content-Type: application/json' \
-d @/tmp/mysql-backup-dashboard.json
Verify your setup
Test the complete monitoring pipeline and verify all components are working.
# Check systemd timer status
sudo systemctl status mysql-backup.timer
Run backup manually to test
sudo systemctl start mysql-backup.service
sudo systemctl status mysql-backup.service
Verify backup files were created
ls -la /var/backups/mysql/daily/
Check metrics are being generated
cat /var/lib/prometheus/mysql-backup-metrics/mysql_backup.prom
Verify exporters are running
curl http://127.0.0.1:9104/metrics | grep mysql_
curl http://127.0.0.1:9100/metrics | grep mysql_backup
Check Prometheus targets
curl http://localhost:9090/api/v1/targets
Test alert rules
curl http://localhost:9090/api/v1/rules
View recent backup logs
tail -20 /var/backups/mysql/logs/backup-*.log
Common issues
| Symptom | Cause | Fix |
|---|---|---|
| Backup script fails with permission denied | Incorrect file ownership or permissions | sudo chown mysql:mysql /usr/local/bin/mysql-backup.sh && sudo chmod +x /usr/local/bin/mysql-backup.sh |
| MySQL exporter shows no metrics | Credentials file not readable | sudo chmod 600 /etc/mysql/backup.cnf && sudo chown mysql:mysql /etc/mysql/backup.cnf |
| Backup timer not triggering | Timer not enabled or systemd not reloaded | sudo systemctl daemon-reload && sudo systemctl enable mysql-backup.timer |
| Grafana dashboard shows no data | Prometheus not scraping backup metrics | Check Prometheus config and restart: sudo systemctl restart prometheus |
| Alerts not firing | Alert rules not loaded or Alertmanager not configured | curl http://localhost:9090/api/v1/rules and check rule syntax |
| Backup files are empty | MySQL user lacks required privileges | Grant additional privileges: GRANT SELECT, SHOW DATABASES, LOCK TABLES, SHOW VIEW, EVENT, TRIGGER ON . TO 'backup_user'@'localhost'; |
| Node exporter metrics missing | Textfile directory not configured | Verify --collector.textfile.directory parameter and directory permissions |
Next steps
- Monitor MySQL performance with Prometheus and Grafana dashboards for comprehensive database monitoring
- Configure backup monitoring with Prometheus and Grafana for multi-service backup oversight
- Monitor cron jobs and systemd timers with Prometheus for broader scheduled task monitoring
- Set up MySQL backup encryption and remote storage for enhanced security and offsite storage
- Configure MySQL point-in-time recovery with binary logs for granular recovery options
Running this in production?
Automated install script
Run this to automate the entire setup
#!/usr/bin/env bash
set -euo pipefail
# Colors for output
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m'
# Configuration
MYSQL_BACKUP_USER="${MYSQL_BACKUP_USER:-backup_user}"
MYSQL_BACKUP_PASS="${MYSQL_BACKUP_PASS:-SecureBackupPass123!}"
MYSQL_ROOT_PASS=""
# Usage
usage() {
echo "Usage: $0 [OPTIONS]"
echo "Options:"
echo " -p, --mysql-root-pass PASS MySQL root password (required)"
echo " -u, --backup-user USER Backup user name (default: backup_user)"
echo " -P, --backup-pass PASS Backup user password (default: SecureBackupPass123!)"
echo " -h, --help Show this help"
exit 1
}
# Parse arguments
while [[ $# -gt 0 ]]; do
case $1 in
-p|--mysql-root-pass)
MYSQL_ROOT_PASS="$2"
shift 2
;;
-u|--backup-user)
MYSQL_BACKUP_USER="$2"
shift 2
;;
-P|--backup-pass)
MYSQL_BACKUP_PASS="$2"
shift 2
;;
-h|--help)
usage
;;
*)
echo "Unknown option $1"
usage
;;
esac
done
if [[ -z "$MYSQL_ROOT_PASS" ]]; then
echo -e "${RED}Error: MySQL root password is required${NC}"
usage
fi
# Check if running as root
if [[ $EUID -ne 0 ]]; then
echo -e "${RED}This script must be run as root${NC}"
exit 1
fi
# Auto-detect distro
if [ -f /etc/os-release ]; then
. /etc/os-release
case "$ID" in
ubuntu|debian)
PKG_MGR="apt"
PKG_UPDATE="apt update"
PKG_INSTALL="apt install -y"
MYSQL_SERVICE="mysql"
;;
almalinux|rocky|centos|rhel|ol)
PKG_MGR="dnf"
PKG_UPDATE="dnf update -y"
PKG_INSTALL="dnf install -y"
MYSQL_SERVICE="mysqld"
;;
amzn|fedora)
PKG_MGR="yum"
PKG_UPDATE="yum update -y"
PKG_INSTALL="yum install -y"
MYSQL_SERVICE="mysqld"
;;
*)
echo -e "${RED}Unsupported distro: $ID${NC}"
exit 1
;;
esac
else
echo -e "${RED}Cannot detect Linux distribution${NC}"
exit 1
fi
# Cleanup on error
cleanup_on_error() {
echo -e "${RED}Installation failed. Cleaning up...${NC}"
systemctl stop mysqld_exporter 2>/dev/null || true
systemctl disable mysqld_exporter 2>/dev/null || true
rm -f /etc/systemd/system/mysqld_exporter.service
rm -f /usr/local/bin/mysqld_exporter
systemctl daemon-reload
}
trap cleanup_on_error ERR
echo -e "${BLUE}Setting up MySQL backup monitoring with Prometheus and Grafana${NC}"
# Step 1: Install packages
echo -e "${GREEN}[1/7] Installing required packages...${NC}"
$PKG_UPDATE
case "$PKG_MGR" in
apt)
$PKG_INSTALL mysql-client gzip curl wget
if ! $PKG_INSTALL prometheus-mysqld-exporter 2>/dev/null; then
echo -e "${YELLOW}Installing mysqld_exporter manually...${NC}"
INSTALL_EXPORTER_MANUALLY=1
fi
;;
*)
$PKG_INSTALL mysql gzip curl wget
INSTALL_EXPORTER_MANUALLY=1
;;
esac
# Install mysqld_exporter manually if needed
if [[ "${INSTALL_EXPORTER_MANUALLY:-0}" == "1" ]]; then
cd /tmp
wget -q https://github.com/prometheus/mysqld_exporter/releases/download/v0.15.1/mysqld_exporter-0.15.1.linux-amd64.tar.gz
tar xzf mysqld_exporter-0.15.1.linux-amd64.tar.gz
cp mysqld_exporter-0.15.1.linux-amd64/mysqld_exporter /usr/local/bin/
chown root:root /usr/local/bin/mysqld_exporter
chmod 755 /usr/local/bin/mysqld_exporter
rm -rf mysqld_exporter-*
fi
# Step 2: Create backup directories
echo -e "${GREEN}[2/7] Creating backup directory structure...${NC}"
mkdir -p /var/backups/mysql/{daily,logs}
mkdir -p /var/lib/prometheus/mysql-backup-metrics
# Get mysql user/group or create if not exists
if ! getent group mysql >/dev/null 2>&1; then
groupadd -r mysql
fi
if ! getent passwd mysql >/dev/null 2>&1; then
useradd -r -g mysql -d /var/lib/mysql -s /bin/false mysql
fi
chown mysql:mysql /var/backups/mysql
chmod 755 /var/backups/mysql
chown mysql:mysql /var/backups/mysql/daily
chown mysql:mysql /var/backups/mysql/logs
chmod 755 /var/backups/mysql/daily
chmod 755 /var/backups/mysql/logs
# Step 3: Create MySQL backup user
echo -e "${GREEN}[3/7] Creating MySQL backup user...${NC}"
mysql -u root -p"$MYSQL_ROOT_PASS" -e "CREATE USER IF NOT EXISTS '${MYSQL_BACKUP_USER}'@'localhost' IDENTIFIED BY '${MYSQL_BACKUP_PASS}';"
mysql -u root -p"$MYSQL_ROOT_PASS" -e "GRANT SELECT, SHOW DATABASES, LOCK TABLES, SHOW VIEW, EVENT, TRIGGER, PROCESS ON *.* TO '${MYSQL_BACKUP_USER}'@'localhost';"
mysql -u root -p"$MYSQL_ROOT_PASS" -e "FLUSH PRIVILEGES;"
# Step 4: Configure MySQL credentials
echo -e "${GREEN}[4/7] Configuring MySQL credentials...${NC}"
mkdir -p /etc/mysql
cat > /etc/mysql/backup.cnf << EOF
[client]
user=${MYSQL_BACKUP_USER}
password=${MYSQL_BACKUP_PASS}
host=localhost
port=3306
EOF
chown mysql:mysql /etc/mysql/backup.cnf
chmod 600 /etc/mysql/backup.cnf
# Step 5: Create backup script
echo -e "${GREEN}[5/7] Creating backup script with metrics...${NC}"
cat > /usr/local/bin/mysql_backup_monitor.sh << 'EOF'
#!/bin/bash
set -euo pipefail
# Configuration
BACKUP_DIR="/var/backups/mysql/daily"
LOG_FILE="/var/backups/mysql/logs/backup-$(date +%Y%m%d-%H%M%S).log"
METRICS_FILE="/var/lib/prometheus/mysql-backup-metrics/mysql_backup.prom"
RETENTION_DAYS=7
# Start metrics
START_TIME=$(date +%s)
echo "mysql_backup_start_timestamp $START_TIME" > $METRICS_FILE
echo "mysql_backup_running 1" >> $METRICS_FILE
# Function to update metrics on exit
cleanup() {
END_TIME=$(date +%s)
DURATION=$((END_TIME - START_TIME))
echo "mysql_backup_running 0" >> $METRICS_FILE
echo "mysql_backup_end_timestamp $END_TIME" >> $METRICS_FILE
echo "mysql_backup_duration_seconds $DURATION" >> $METRICS_FILE
echo "mysql_backup_last_run_timestamp $END_TIME" >> $METRICS_FILE
}
trap cleanup EXIT
# Log function
log() {
echo "$(date '+%Y-%m-%d %H:%M:%S') $1" | tee -a $LOG_FILE
}
log "Starting MySQL backup process"
# Get database list
DATABASES=$(mysql --defaults-file=/etc/mysql/backup.cnf -e "SHOW DATABASES;" | grep -Ev "(Database|information_schema|performance_schema|mysql|sys)")
if [ -z "$DATABASES" ]; then
log "ERROR: No databases found to backup"
echo "mysql_backup_success 0" >> $METRICS_FILE
echo "mysql_backup_databases_total 0" >> $METRICS_FILE
exit 1
fi
# Count databases
DB_COUNT=$(echo "$DATABASES" | wc -l)
echo "mysql_backup_databases_total $DB_COUNT" >> $METRICS_FILE
log "Found $DB_COUNT databases to backup"
# Create timestamp for backup files
TIMESTAMP=$(date +%Y%m%d_%H%M%S)
# Backup each database
SUCCESS_COUNT=0
TOTAL_SIZE=0
for DB in $DATABASES; do
log "Backing up database: $DB"
BACKUP_FILE="$BACKUP_DIR/${DB}_${TIMESTAMP}.sql.gz"
if mysqldump --defaults-file=/etc/mysql/backup.cnf \
--single-transaction \
--routines \
--triggers \
--events \
--hex-blob \
--opt \
"$DB" | gzip > "$BACKUP_FILE"; then
if [ -s "$BACKUP_FILE" ]; then
FILE_SIZE=$(stat -c%s "$BACKUP_FILE")
TOTAL_SIZE=$((TOTAL_SIZE + FILE_SIZE))
SUCCESS_COUNT=$((SUCCESS_COUNT + 1))
log "Successfully backed up $DB ($(($FILE_SIZE / 1024 / 1024)) MB)"
else
log "ERROR: Backup file for $DB is empty"
rm -f "$BACKUP_FILE"
fi
else
log "ERROR: Failed to backup database $DB"
fi
done
# Update final metrics
echo "mysql_backup_databases_successful $SUCCESS_COUNT" >> $METRICS_FILE
echo "mysql_backup_total_size_bytes $TOTAL_SIZE" >> $METRICS_FILE
if [ $SUCCESS_COUNT -eq $DB_COUNT ]; then
echo "mysql_backup_success 1" >> $METRICS_FILE
log "All backups completed successfully"
else
echo "mysql_backup_success 0" >> $METRICS_FILE
log "Some backups failed: $SUCCESS_COUNT/$DB_COUNT successful"
fi
# Cleanup old backups
find $BACKUP_DIR -name "*.sql.gz" -mtime +$RETENTION_DAYS -delete
find /var/backups/mysql/logs -name "backup-*.log" -mtime +$RETENTION_DAYS -delete
log "Backup process completed"
EOF
chown mysql:mysql /usr/local/bin/mysql_backup_monitor.sh
chmod 755 /usr/local/bin/mysql_backup_monitor.sh
# Step 6: Create systemd timer
echo -e "${GREEN}[6/7] Setting up systemd timer for backups...${NC}"
cat > /etc/systemd/system/mysql-backup.service << EOF
[Unit]
Description=MySQL Backup Service
After=mysql.service
[Service]
Type=oneshot
User=mysql
Group=mysql
ExecStart=/usr/local/bin/mysql_backup_monitor.sh
EOF
cat > /etc/systemd/system/mysql-backup.timer << EOF
[Unit]
Description=MySQL Backup Timer
Requires=mysql-backup.service
[Timer]
OnCalendar=daily
Persistent=true
[Install]
WantedBy=timers.target
EOF
# Create mysqld_exporter service if installed manually
if [[ "${INSTALL_EXPORTER_MANUALLY:-0}" == "1" ]]; then
cat > /etc/systemd/system/mysqld_exporter.service << EOF
[Unit]
Description=MySQL Exporter
After=network.target mysql.service
[Service]
Type=simple
User=mysql
Group=mysql
ExecStart=/usr/local/bin/mysqld_exporter --config.my-cnf=/etc/mysql/backup.cnf --collect.info_schema.processlist --collect.info_schema.tables --collect.info_schema.tablestats --collect.info_schema.userstats --collect.binlog_size --collect.auto_increment.columns
Restart=always
[Install]
WantedBy=multi-user.target
EOF
fi
systemctl daemon-reload
systemctl enable mysql-backup.timer
systemctl start mysql-backup.timer
# Step 7: Start services
echo -e "${GREEN}[7/7] Starting and enabling services...${NC}"
systemctl enable mysqld_exporter || true
systemctl start mysqld_exporter || true
# Verify installation
echo -e "${GREEN}Verifying installation...${NC}"
if systemctl is-active --quiet mysql-backup.timer; then
echo -e "${GREEN}✓ MySQL backup timer is active${NC}"
else
echo -e "${RED}✗ MySQL backup timer failed to start${NC}"
fi
if systemctl is-active --quiet mysqld_exporter; then
echo -e "${GREEN}✓ MySQL exporter is running${NC}"
else
echo -e "${YELLOW}⚠ MySQL exporter is not running - check logs with: journalctl -u mysqld_exporter${NC}"
fi
echo -e "${GREEN}Installation completed successfully!${NC}"
echo -e "${BLUE}Next steps:${NC}"
echo "1. Configure Prometheus to scrape metrics from localhost:9104"
echo "2. Set up Grafana dashboards for backup monitoring"
echo "3. Configure alerting rules for backup failures"
echo "4. Test backup with: systemctl start mysql-backup.service"
echo "5. Check backup logs in: /var/backups/mysql/logs/"
Review the script before running. Execute with: bash install.sh