From 33e463a36da4a574b734756d7ccce147a59dd277 Mon Sep 17 00:00:00 2001 From: Michael Fornaro <20387402+xUnholy@users.noreply.github.com> Date: Thu, 25 Jun 2026 14:06:58 +1000 Subject: [PATCH] feat(cmangos-database): nightly logical (mysqldump) backups MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Adds restorable SQL-dump backups of the irreplaceable game DBs to complement the existing crash-consistent RBD snapshot. The live DBs are mixed-engine (classiccharacters: 79 InnoDB + 13 MyISAM; classicrealmd: mostly small MyISAM), and MyISAM isn't safe to restore from a raw volume snapshot taken mid-write — a logical dump is. - pvc-dumps.yaml: dedicated 10Gi PVC for the dumps - backup-cronjob.yaml: nightly (03:00) mariadb-dump of classicrealmd + classiccharacters, gzipped + timestamped, keep newest 7 per DB. Uses the app user (SELECT is enough for --single-transaction --no-tablespaces; verified against the live DB). classicmangos (world) is omitted — it's reconstructible via init-db + the in-repo migrations; classiclogs is disposable. - replicationsource-dumps.yaml: VolSync ships the dumps PVC to Kopia at 05:00, reusing the existing repo secret; retain daily:7 weekly:4. --single-transaction is consistent for the InnoDB bulk (all player rows) with no write-blocking; the few small MyISAM tables are best-effort. Upgrade to a privileged backup user + --lock-all-tables once live root is rotated (after the PTR DB split lands and decouples the on-main db-init from root). --- .../cmangos-database/app/backup-cronjob.yaml | 91 +++++++++++++++++++ .../cmangos-database/app/kustomization.yaml | 3 + .../cmangos-database/app/pvc-dumps.yaml | 16 ++++ .../app/replicationsource-dumps.yaml | 31 +++++++ 4 files changed, 141 insertions(+) create mode 100644 kubernetes/apps/base/game-servers/cmangos-database/app/backup-cronjob.yaml create mode 100644 kubernetes/apps/base/game-servers/cmangos-database/app/pvc-dumps.yaml create mode 100644 kubernetes/apps/base/game-servers/cmangos-database/app/replicationsource-dumps.yaml diff --git a/kubernetes/apps/base/game-servers/cmangos-database/app/backup-cronjob.yaml b/kubernetes/apps/base/game-servers/cmangos-database/app/backup-cronjob.yaml new file mode 100644 index 00000000000..427e665500e --- /dev/null +++ b/kubernetes/apps/base/game-servers/cmangos-database/app/backup-cronjob.yaml @@ -0,0 +1,91 @@ +--- +# Nightly logical backup of the irreplaceable game DBs (accounts + characters). +# Complements the hourly crash-consistent RBD snapshot (replicationsource.yaml) +# with restorable, engine-independent SQL dumps — the MyISAM tables in these +# DBs aren't safe to restore from a raw volume snapshot mid-write. +# +# classicmangos (world) is intentionally NOT dumped: it's reconstructible via +# init-db + the in-repo migration Jobs. classiclogs is disposable. +# +# $$ escapes are for Flux post-build substitution (only ${CLUSTER_TIMEZONE} is +# a Flux var; everything else is runtime bash). +apiVersion: batch/v1 +kind: CronJob +metadata: + name: cmangos-db-logical-backup + namespace: game-servers +spec: + schedule: "0 3 * * *" + concurrencyPolicy: Forbid + successfulJobsHistoryLimit: 3 + failedJobsHistoryLimit: 3 + jobTemplate: + spec: + backoffLimit: 2 + template: + spec: + restartPolicy: Never + securityContext: + runAsUser: 1000 + runAsGroup: 1000 + runAsNonRoot: true + fsGroup: 1000 + fsGroupChangePolicy: OnRootMismatch + seccompProfile: + type: RuntimeDefault + containers: + - name: dump + image: mariadb:12.2 + envFrom: + # MANGOS_DBUSER / MANGOS_DBPASS — SELECT is enough for the + # --single-transaction dump below. + - secretRef: + name: cmangos-database-creds + env: + - name: TZ + value: ${CLUSTER_TIMEZONE} + command: + - /bin/bash + - -c + - | + set -euo pipefail + BACKUP_DIR=/backup + DBS="classicrealmd classiccharacters" + RETAIN=7 + TS=$$(date +%Y%m%d-%H%M%S) + + for db in $$DBS; do + out="$${BACKUP_DIR}/$${db}-$${TS}.sql.gz" + echo "Dumping $${db} -> $${out}" + # --single-transaction: consistent snapshot of the InnoDB + # bulk (all player rows) with no write-blocking. The few + # small MyISAM tables are best-effort. --no-tablespaces + # avoids needing the PROCESS privilege, so SELECT suffices. + mariadb-dump -h cmangos-database -P 3306 \ + -u "$${MANGOS_DBUSER}" -p"$${MANGOS_DBPASS}" \ + --single-transaction --quick --no-tablespaces \ + "$${db}" | gzip -c > "$${out}" + done + + echo "Pruning to newest $${RETAIN} per DB" + for db in $$DBS; do + # shellcheck disable=SC2012 + ls -1t "$${BACKUP_DIR}/$${db}-"*.sql.gz 2>/dev/null \ + | tail -n +$$((RETAIN + 1)) | xargs -r rm -f -- + done + + echo "Backups on volume:" + ls -lh "$${BACKUP_DIR}" + volumeMounts: + - name: dumps + mountPath: /backup + resources: + requests: + cpu: 100m + memory: 128Mi + limits: + memory: 512Mi + volumes: + - name: dumps + persistentVolumeClaim: + claimName: cmangos-db-dumps diff --git a/kubernetes/apps/base/game-servers/cmangos-database/app/kustomization.yaml b/kubernetes/apps/base/game-servers/cmangos-database/app/kustomization.yaml index cc3fe1e0479..d019366b5d3 100644 --- a/kubernetes/apps/base/game-servers/cmangos-database/app/kustomization.yaml +++ b/kubernetes/apps/base/game-servers/cmangos-database/app/kustomization.yaml @@ -7,5 +7,8 @@ resources: - helmrelease.yaml - externalsecret-database.yaml - pvc-database.yaml + - pvc-dumps.yaml + - backup-cronjob.yaml - replicationsource.yaml + - replicationsource-dumps.yaml - volsync-externalsecret.yaml diff --git a/kubernetes/apps/base/game-servers/cmangos-database/app/pvc-dumps.yaml b/kubernetes/apps/base/game-servers/cmangos-database/app/pvc-dumps.yaml new file mode 100644 index 00000000000..fde20784895 --- /dev/null +++ b/kubernetes/apps/base/game-servers/cmangos-database/app/pvc-dumps.yaml @@ -0,0 +1,16 @@ +--- +# Holds logical (mysqldump) backups of the irreplaceable game DBs. Separate +# from the data PVC so VolSync can ship these dumps to Kopia independently of +# the crash-consistent RBD snapshot of /var/lib/mysql. +apiVersion: v1 +kind: PersistentVolumeClaim +metadata: + name: cmangos-db-dumps + namespace: game-servers +spec: + accessModes: + - ReadWriteOnce + resources: + requests: + storage: 10Gi + storageClassName: ceph-block diff --git a/kubernetes/apps/base/game-servers/cmangos-database/app/replicationsource-dumps.yaml b/kubernetes/apps/base/game-servers/cmangos-database/app/replicationsource-dumps.yaml new file mode 100644 index 00000000000..e31359e3f45 --- /dev/null +++ b/kubernetes/apps/base/game-servers/cmangos-database/app/replicationsource-dumps.yaml @@ -0,0 +1,31 @@ +--- +# Ships the logical SQL dumps (cmangos-db-dumps PVC) to Kopia. Reuses the same +# repo secret as the data-PVC ReplicationSource — Kopia handles multiple +# sources in one repository. Runs at 05:00, after the 03:00 dump CronJob. +apiVersion: volsync.backube/v1alpha1 +kind: ReplicationSource +metadata: + name: cmangos-db-dumps +spec: + sourcePVC: cmangos-db-dumps + trigger: + schedule: "0 5 * * *" + kopia: + accessModes: + - ReadWriteOnce + cacheAccessModes: + - ReadWriteOnce + cacheCapacity: 2Gi + cacheStorageClassName: ceph-block + compression: zstd-fastest + copyMethod: Snapshot + moverSecurityContext: + runAsUser: 1000 + runAsGroup: 1000 + fsGroup: 1000 + repository: cmangos-database-volsync-secret + retain: + daily: 7 + weekly: 4 + storageClassName: ceph-block + volumeSnapshotClassName: csi-ceph-blockpool