feat(mysql): store physical backups in Backblaze B2 - #6
Open
viasnake wants to merge 1 commit into
Open
Conversation
viasnake
force-pushed
the
feat/mysql-backup-b2
branch
from
August 31, 2026 18:34
2c77e7a to
6f1e38e
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
Why
The MySQL platform needs off-host physical backup storage with remote restore validation. The final platform uses a private Backblaze B2 bucket while retaining prepared XtraBackup data, role-aware scheduling, shared locking, and restore validation.
Changes
Architecture
The backup path is
SECONDARY -> local XtraBackup staging -> xtrabackup --prepare -> rclone over HTTPS -> Backblaze B2. Restore validation always downloads from B2 into isolated local scratch before starting a network-disabled MySQL instance.Why rclone
rclone provides a native B2 backend, Application Key authentication, checksum-aware upload and download, and direct transfer of prepared XtraBackup data to object storage.
B2 credential model
Inventory contains only the bucket,
mysql-sharedprefix, and rclone remote name. The Application Key ID and Application Key are required secret inputs rendered to/etc/rclone/mysql-backup.confasroot:rootmode0600underno_log. The host role validates but does not create the bucket or key.Backup flow
The current healthy
SECONDARYcreates and prepares a local physical backup, uploads it withrclone copy, verifies it withrclone check, and writescomplete.jsonlast. Only then does the job archive closed binlogs, write the run manifest, update status, and remove local staging.Binlog flow
Closed binlogs use
mysql-shared/binlog/<node>/<server-uuid>/. Existing objects with the same SHA-1 and size are skipped; the same name with different content fails. Each run writes its GTID and checksum manifest undermanifests/<backup-run-id>.json.Restore flow
The restore test selects the latest completed UTC run or an explicit
mysql_restore_backup_id, validates marker identity, downloads with rclone, checks remote object names and checksums, validates prepared metadata, then runs the isolated MySQL checks and cleanup.Impact
MySQL convergence requires an existing private B2 bucket plus a dedicated bucket- and prefix-restricted Application Key. The committed bucket value remains empty until the operator supplies the real name.
Security
Credentials are absent from command lines and logs. rclone
v1.75.0is installed from pinned official archives with architecture-specific SHA256 checksums. Backup code never uses remote synchronization or deletion commands. Remote identities, completion metadata, local staging, downloaded object paths, and restore process ownership fail closed. A B2 outage fails backup monitoring without coupling B2 to MySQL, ReplicaSet, or Router availability.Validation
PYTHONDONTWRITEBYTECODE=1 mise run check: 88 tests passed; seven playbook syntax checks passed; ansible-lint reported zero failures and zero warnings.git diff --check.venv/bin/ansible-inventory --graphv1.75.0local-backend probe: empty-prefix preflight, upload/check/marker, latest and explicit selection, remote download/integrity validation, binlog idempotence, and mismatched-binlog rejection passed.Runtime acceptance still blocked
Implemented and statically validated. No production apply, live B2 upload, live B2 download, production backup, or production restore was performed. Runtime acceptance remains pending until an operator creates the private bucket and dedicated Application Key, supplies the bucket and secrets, and runs backup and restore validation on the MySQL nodes. Retention, Lifecycle Rules, and Object Lock remain intentionally deferred.