Ticket #311 & 326 : Implement Automated MySQL Backups Using mysqldump - SupaHotBall/OE2-Group-D GitHub Wiki
Task
Action Required:
-
Implement automated MySQL logical backups with these specifications.
-
Your solution should be able to handle regular logical backups of MySQL databases, and integrated with our existing rsync backup infrastructure.
-
Backup Configuration
- Tool: mysqldump with transactional consistency
- Schedule: Daily at 0200 + Weekly full backups
- Retention:
- 7 daily backups
- 3 weekly backups
- Security Requirements
- Create dedicated backup user with minimum privileges
- You may consider encrypting the backups before transfer using gpg or any other suitable encrytion tool
- Testing and Integration with Our Existing Systems
- Create Puppet backup module with cron job to schedule this process
- Test restoration procedures
- Validate ownCloud data integrity post-restore
- Acceptance Criteria:
- β Daily logical backups of your team database(s)
- β Verified restoration procedure
- β Backup validation tests successful
- β Schedule Daily/Weekly restoration drills
- Documentation & Deliverables:
- Backup module/script with inline comments
- Restoration runbook
- Integration notes for existing backup system
- Troubleshooting /error documentation where applicable
Steps Taken
Create the MariaDB Backup User
sudo mysql
CREATE USER 'backup'@'localhost' IDENTIFIED BY 'changeme';
GRANT SELECT, LOCK TABLES, SHOW VIEW ON *.* TO 'backup'@'localhost';
FLUSH PRIVILEGES;
Exit the MariaDB shell and run
sudo nano /root/.my.cnf
Then paste the following code into the file
[client]
user=backup
password=changeme
Ensure that proper permissions are set. This is to ensure that only root can read the file which is important for security
sudo chmod 600 /root/.my.cnf
Update the backup script
#!/bin/bash
# Encrypted MariaDB Backup Script with rsync integration
BACKUP_DIR="/var/backups/mysql"
DATE=$(date +"%Y-%m-%d_%H-%M")
HOSTNAME=$(hostname -s)
RECIPIENT="backup@group-d"
KEY="/etc/backup/.ssh/id_rsa"
DEST="[email protected]:/home/group-d/db/"
mkdir -p "$BACKUP_DIR/daily" "$BACKUP_DIR/weekly"
# Determine backup type based on the day (Sunday = weekly)
if [ "$(date +%u)" -eq 7 ]; then
TYPE="weekly"
RETENTION=3
else
TYPE="daily"
RETENTION=7
fi
DUMP_FILE="$BACKUP_DIR/$TYPE/${HOSTNAME}_mysqldump_${DATE}.sql"
ENCRYPTED_FILE="$DUMP_FILE.gpg"
# Step 1: Dump the MariaDB database
mysqldump --defaults-file=/root/.my.cnf --all-databases --single-transaction --add-drop-table > "$DUMP_FILE"
# Step 2: Encrypt the SQL dump with GPG
gpg --yes --batch --output "$ENCRYPTED_FILE" --encrypt --recipient "$RECIPIENT" "$DUMP_FILE"
# Step 3: Delete unencrypted file for security
rm -f "$DUMP_FILE"
# Step 4: Retention cleanup
find "$BACKUP_DIR/$TYPE" -name "*.sql.gpg" -type f -mtime +$RETENTION -delete
# Step 5: Sync encrypted backup to offsite server
rsync -avz -e "ssh -i $KEY" "$ENCRYPTED_FILE" "$DEST"
# Step 6: Logging
echo "[$(date)] Encrypted MariaDB $TYPE backup completed and transferred." >> /var/log/backup-db.log
Generate a GPG key
gpg --full-generate-key
Follow the steps below for the prompt
Prompt | What to Choose |
---|---|
Please select kind of key | 1 β RSA and RSA (default) |
RSA keysize | 4096 |
Expiration | 0 (key does not expire) |
Real name | Backup User |
Email address | backup@group-d |
Passphrase | Leave blank (for automated use) |
Proceed confirmation | Press O to confirm |
Check that the gpg key shows up on the list
gpg --list-keys
Run the backup script once finished to test that it works
After verifying that it works, integrate the new script with the cron job in the backup module so that it runs daily at 0200. Edit the init.pp file in the manifests directory of the backup module
# === Database Server (db-d) ===
if $facts['networking']['hostname'] == 'db-d' {
file { '/usr/local/bin/backup-db.sh':
source => 'puppet:///modules/backup/backup-db.sh',
owner => 'root',
group => 'root',
mode => '0755',
}
file { '/var/log/backup-db.log':
ensure => present,
owner => 'root',
group => 'root',
mode => '0644',
}
cron { 'db-backup':
ensure => present,
command => '/usr/local/bin/backup-db.sh',
user => 'root',
minute => 10,
hour => 2,
}
['db-2am', 'db-8am', 'db-2pm', 'db-8pm'].each |$name| {
cron { $name:
ensure => absent,
}
}
}
Ensure that the backup-db.sh script is either copied into the files directory of the backup module or created in there. Once the init.pp file has been edited, go back to the db server and apply the changes with sudo puppet agent --test
Testing Restoration Procedures
Copy the gpg file over from the offsite backup server to the db server. Ensure that you are in the db server while entering the following commands
scp [email protected]:/home/group-d/db/db-d_mysqldump_2025-05-19_08-25.sql.gpg ~/
Decrypt the gpg file
gpg --output db-restore.sql --decrypt ~/db-d_mysqldump_2025-05-19_08-25.sql.gpg
Run sudo mysql -u root < db-restore.sql
to restore the database
Check that the database has been restored
sudo mysql -e "SHOW DATABASES;"
Verify Owncloud integrity
Restoration Runbook
- Log in to the DB server
ssh group-d@ipaddress
- Copy the encrypted backup from the offsite server
scp [email protected]:/home/group-d/db/db-d_mysqldump_YYYY-MM-DD_HH-MM.sql.gpg ~/
- Decrypt the file with GPG
gpg --output db-restore.sql --decrypt ~/db-d_mysqldump_YYYY-MM-DD_HH-MM.sql.gpg
- Restore the database
sudo mysql -u root < db-restore.sql
- Verify restoration
sudo mysql -e "SHOW DATABASES;"
- Check ownCloud UI
- Visit: https://group-d.op-bit.nz
- Log in with Admin account.
- Verify files are visible and accessible.
π Scheduled Restore Drills
π Date | π οΈ Type | π Description | β Status |
---|---|---|---|
2025-05-20 | Manual Restore | Decrypt and restore .sql.gpg to MariaDB |
β Completed |
2025-05-27 | Manual Restore | Repeat full restore from most recent backup | β³ Planned |
π Integration Notes with Existing Backup System
π§© Component | π Integration Description |
---|---|
ποΈ rsync | Used to transfer encrypted .sql.gpg dumps from DB server to the offsite backup server |
π GPG | Encrypts backup dumps to prevent unauthorized access |
π mysqldump | Logical database export tool used with transactional flags (--single-transaction ) |
β±οΈ cron (Puppet) | Puppet module ensures daily (02:10) scheduled backups via cron |
π Folder Structure | /var/backups/mysql/daily and /var/backups/mysql/weekly maintained with retention policies |
π Puppet Managed | All configurations and scripts (backup, cron, logrotate) are managed using Puppet |
Challenges
GPG Encryption + Rsync Integration Issue (MariaDB Backup)
βProblem Summary
During testing of the new MariaDB backup script using GPG and rsync, the following error occurred:
The GPG key for backup@group-d
was created under the group-d
user account, but the script was run via sudo
, meaning it executed under the root
user.
The root GPG keyring did not have access to the backup@group-d
public key, so encryption failed.
Because .sql.gpg
was never created, rsync
had no file to transfer β resulting in code 23.
β Solution
- Export the GPG public key (as
group-d
user) and import the key into root's GPG keyring:
gpg --export --armor backup@group-d > backup_pubkey.asc
sudo su -
gpg --import /home/group-d/backup_pubkey.asc
exit
- Set key trust to ultimate as root
sudo su -
gpg --edit-key backup@group-d
- Inside the gpg prompt type
trust
and then select 5
- Quit and re-run the script
The encrypted .sql.gpg backup was successfully created and transferred to the offsite server via rsync.
External Resources
N/A
Ticket Reference
https://rt.dataraster.com/Ticket/Display.html?id=311 https://rt.dataraster.com/Ticket/Display.html?id=326