minecraft: add hourly world backup CronJob with 3-day local retention
This commit is contained in:
117
cluster/applications/minecraft/backup-cronjob.yaml
Normal file
117
cluster/applications/minecraft/backup-cronjob.yaml
Normal file
@@ -0,0 +1,117 @@
|
|||||||
|
---
|
||||||
|
# Minecraft world backup — hourly CronJob, 3-day local retention
|
||||||
|
#
|
||||||
|
# Strategy (two-tier):
|
||||||
|
# Short-term : this CronJob — hourly tarballs on papermc-backups PVC, 72-hour retention
|
||||||
|
# Long-term : Pure FlashArray protection group snapshots on utilidor (managed separately)
|
||||||
|
#
|
||||||
|
# Backup sequence:
|
||||||
|
# 1. RCON save-all — flush all dirty chunks to disk
|
||||||
|
# 2. RCON save-off — pause auto-save to keep the world consistent during tar
|
||||||
|
# 3. tar world directories to /backups/world-YYYY-MM-DDTHH-MM.tar.gz
|
||||||
|
# 4. RCON save-on — re-enable auto-save
|
||||||
|
# 5. Prune backups older than 3 days
|
||||||
|
#
|
||||||
|
# RCON password sourced from the papermc-rcon Secret (ESO-managed, same as server).
|
||||||
|
# The backup pod mounts both PVCs read-write; world-data is safe because the
|
||||||
|
# server has already quiesced saves via RCON before the tar runs.
|
||||||
|
#
|
||||||
|
# Note: both PVCs are RWO. The backup job runs only while the main server pod is
|
||||||
|
# running (RCON is reachable), so there is no volume attach conflict — they are
|
||||||
|
# mounted on the same node by the scheduler. If the server pod is down, the backup
|
||||||
|
# job will fail at the RCON step, which is correct behavior (nothing to back up).
|
||||||
|
apiVersion: batch/v1
|
||||||
|
kind: CronJob
|
||||||
|
metadata:
|
||||||
|
name: minecraft-backup
|
||||||
|
namespace: minecraft
|
||||||
|
labels:
|
||||||
|
app.kubernetes.io/name: minecraft
|
||||||
|
app.kubernetes.io/component: backup
|
||||||
|
spec:
|
||||||
|
schedule: "0 * * * *" # every hour on the hour
|
||||||
|
concurrencyPolicy: Forbid # skip if a previous backup is still running
|
||||||
|
successfulJobsHistoryLimit: 3
|
||||||
|
failedJobsHistoryLimit: 3
|
||||||
|
jobTemplate:
|
||||||
|
spec:
|
||||||
|
backoffLimit: 0 # don't retry — a partial backup is worse than no backup
|
||||||
|
template:
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
app.kubernetes.io/name: minecraft
|
||||||
|
app.kubernetes.io/component: backup
|
||||||
|
spec:
|
||||||
|
restartPolicy: Never
|
||||||
|
securityContext:
|
||||||
|
runAsNonRoot: false
|
||||||
|
fsGroup: 1000
|
||||||
|
containers:
|
||||||
|
- name: backup
|
||||||
|
image: itzg/rcon-cli:latest
|
||||||
|
imagePullPolicy: IfNotPresent
|
||||||
|
env:
|
||||||
|
- name: RCON_HOST
|
||||||
|
value: "journey-into-imagination.minecraft.svc.cluster.local"
|
||||||
|
- name: RCON_PORT
|
||||||
|
value: "25575"
|
||||||
|
- name: RCON_PASSWORD
|
||||||
|
valueFrom:
|
||||||
|
secretKeyRef:
|
||||||
|
name: papermc-rcon
|
||||||
|
key: rcon-password
|
||||||
|
command:
|
||||||
|
- /bin/sh
|
||||||
|
- -c
|
||||||
|
- |
|
||||||
|
set -e
|
||||||
|
|
||||||
|
TIMESTAMP=$(date -u +%Y-%m-%dT%H-%M)
|
||||||
|
BACKUP_FILE="/backups/world-${TIMESTAMP}.tar.gz"
|
||||||
|
RETAIN_DAYS=3
|
||||||
|
|
||||||
|
echo "[backup] Starting backup at ${TIMESTAMP}"
|
||||||
|
|
||||||
|
# Step 1: quiesce the world
|
||||||
|
echo "[backup] Flushing chunks (save-all)..."
|
||||||
|
rcon-cli save-all
|
||||||
|
|
||||||
|
echo "[backup] Pausing auto-save (save-off)..."
|
||||||
|
rcon-cli save-off
|
||||||
|
|
||||||
|
# Step 2: archive world directories
|
||||||
|
echo "[backup] Archiving world to ${BACKUP_FILE}..."
|
||||||
|
tar -czf "${BACKUP_FILE}" \
|
||||||
|
-C /data \
|
||||||
|
world \
|
||||||
|
world_nether \
|
||||||
|
world_the_end \
|
||||||
|
whitelist.json \
|
||||||
|
ops.json \
|
||||||
|
banned-players.json \
|
||||||
|
banned-ips.json
|
||||||
|
|
||||||
|
echo "[backup] Archive complete: $(du -sh ${BACKUP_FILE} | cut -f1)"
|
||||||
|
|
||||||
|
# Step 3: resume auto-save
|
||||||
|
echo "[backup] Resuming auto-save (save-on)..."
|
||||||
|
rcon-cli save-on
|
||||||
|
|
||||||
|
# Step 4: prune old backups
|
||||||
|
echo "[backup] Pruning backups older than ${RETAIN_DAYS} days..."
|
||||||
|
find /backups -name "world-*.tar.gz" -mtime +${RETAIN_DAYS} -delete
|
||||||
|
REMAINING=$(find /backups -name "world-*.tar.gz" | wc -l)
|
||||||
|
echo "[backup] Done. ${REMAINING} backup(s) retained."
|
||||||
|
volumeMounts:
|
||||||
|
- name: world-data
|
||||||
|
mountPath: /data
|
||||||
|
readOnly: true
|
||||||
|
- name: backups
|
||||||
|
mountPath: /backups
|
||||||
|
volumes:
|
||||||
|
- name: world-data
|
||||||
|
persistentVolumeClaim:
|
||||||
|
claimName: papermc-world-data
|
||||||
|
- name: backups
|
||||||
|
persistentVolumeClaim:
|
||||||
|
claimName: papermc-backups
|
||||||
19
cluster/applications/minecraft/backup-pvc.yaml
Normal file
19
cluster/applications/minecraft/backup-pvc.yaml
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
---
|
||||||
|
# Minecraft backup storage — local hourly backups, 3-day retention
|
||||||
|
# Sized for ~72 backup tarballs; world data compresses well (~90% reduction typical)
|
||||||
|
# Long-term retention handled by Pure FlashArray protection group snapshots (utilidor)
|
||||||
|
apiVersion: v1
|
||||||
|
kind: PersistentVolumeClaim
|
||||||
|
metadata:
|
||||||
|
name: papermc-backups
|
||||||
|
namespace: minecraft
|
||||||
|
labels:
|
||||||
|
app.kubernetes.io/name: minecraft
|
||||||
|
app.kubernetes.io/component: backup
|
||||||
|
spec:
|
||||||
|
accessModes:
|
||||||
|
- ReadWriteOnce
|
||||||
|
storageClassName: px-fa-direct-access
|
||||||
|
resources:
|
||||||
|
requests:
|
||||||
|
storage: 10Gi
|
||||||
Reference in New Issue
Block a user