feat: 每日自动备份数据库到 MinIO
- CronJob: daily-minio-backup, 每天 UTC 3:00 执行 - 备份 SQLite DB 到 minio-api.oci.euphon.net/oil-backups/ - 文件名: oil_calculator_YYYYMMDD.db - 滚动保留最近 30 份,自动删除旧备份 - 使用 Python minio SDK 上传 Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
96
deploy/minio-backup-cronjob.yaml
Normal file
96
deploy/minio-backup-cronjob.yaml
Normal file
@@ -0,0 +1,96 @@
|
|||||||
|
apiVersion: v1
|
||||||
|
kind: Secret
|
||||||
|
metadata:
|
||||||
|
name: minio-backup-creds
|
||||||
|
namespace: oil-calculator
|
||||||
|
type: Opaque
|
||||||
|
stringData:
|
||||||
|
MINIO_ALIAS: "oci"
|
||||||
|
MINIO_URL: "https://minio-api.oci.euphon.net"
|
||||||
|
MINIO_ACCESS_KEY: "admin"
|
||||||
|
MINIO_SECRET_KEY: "HpYMIVH0WN79VkzF4L4z8Zx1"
|
||||||
|
MINIO_BUCKET: "oil-backups"
|
||||||
|
---
|
||||||
|
apiVersion: batch/v1
|
||||||
|
kind: CronJob
|
||||||
|
metadata:
|
||||||
|
name: daily-minio-backup
|
||||||
|
namespace: oil-calculator
|
||||||
|
spec:
|
||||||
|
schedule: "0 3 * * *" # Daily at 3:00 UTC
|
||||||
|
successfulJobsHistoryLimit: 3
|
||||||
|
failedJobsHistoryLimit: 2
|
||||||
|
jobTemplate:
|
||||||
|
spec:
|
||||||
|
template:
|
||||||
|
spec:
|
||||||
|
containers:
|
||||||
|
- name: backup
|
||||||
|
image: registry.oci.euphon.net/oil-calculator:latest
|
||||||
|
command:
|
||||||
|
- sh
|
||||||
|
- -c
|
||||||
|
- |
|
||||||
|
set -e
|
||||||
|
DATE=$(date +%Y%m%d)
|
||||||
|
export BACKUP_FILE="oil_calculator_${DATE}.db"
|
||||||
|
|
||||||
|
echo "=== Oil Calculator Daily Backup ==="
|
||||||
|
echo "Date: ${DATE}"
|
||||||
|
|
||||||
|
# 1. Copy SQLite database (app does WAL checkpoint every 5min)
|
||||||
|
cp /data/oil_calculator.db /tmp/${BACKUP_FILE}
|
||||||
|
SIZE=$(du -h /tmp/${BACKUP_FILE} | cut -f1)
|
||||||
|
echo "Backup created: ${BACKUP_FILE} (${SIZE})"
|
||||||
|
|
||||||
|
# 2. Upload to minio and cleanup using Python minio SDK
|
||||||
|
pip install -q minio 2>/dev/null
|
||||||
|
cat > /tmp/upload_backup.py << 'PYEOF'
|
||||||
|
import os
|
||||||
|
from minio import Minio
|
||||||
|
url = os.environ['MINIO_URL'].replace('https://','').replace('http://','')
|
||||||
|
client = Minio(url, access_key=os.environ['MINIO_ACCESS_KEY'], secret_key=os.environ['MINIO_SECRET_KEY'], secure='https' in os.environ['MINIO_URL'])
|
||||||
|
bucket = os.environ['MINIO_BUCKET']
|
||||||
|
bf = os.environ['BACKUP_FILE']
|
||||||
|
client.fput_object(bucket, bf, '/tmp/' + bf)
|
||||||
|
print('Uploaded:', bf)
|
||||||
|
objs = sorted(client.list_objects(bucket, prefix='oil_calculator_'), key=lambda o: o.object_name, reverse=True)
|
||||||
|
for o in objs[30:]:
|
||||||
|
client.remove_object(bucket, o.object_name)
|
||||||
|
print('Deleted:', o.object_name)
|
||||||
|
print('Total backups:', min(len(objs), 30))
|
||||||
|
PYEOF
|
||||||
|
python3 /tmp/upload_backup.py
|
||||||
|
echo "=== Done ==="
|
||||||
|
env:
|
||||||
|
- name: MINIO_URL
|
||||||
|
valueFrom:
|
||||||
|
secretKeyRef:
|
||||||
|
name: minio-backup-creds
|
||||||
|
key: MINIO_URL
|
||||||
|
- name: MINIO_ACCESS_KEY
|
||||||
|
valueFrom:
|
||||||
|
secretKeyRef:
|
||||||
|
name: minio-backup-creds
|
||||||
|
key: MINIO_ACCESS_KEY
|
||||||
|
- name: MINIO_SECRET_KEY
|
||||||
|
valueFrom:
|
||||||
|
secretKeyRef:
|
||||||
|
name: minio-backup-creds
|
||||||
|
key: MINIO_SECRET_KEY
|
||||||
|
- name: MINIO_BUCKET
|
||||||
|
valueFrom:
|
||||||
|
secretKeyRef:
|
||||||
|
name: minio-backup-creds
|
||||||
|
key: MINIO_BUCKET
|
||||||
|
volumeMounts:
|
||||||
|
- name: data
|
||||||
|
mountPath: /data
|
||||||
|
readOnly: true
|
||||||
|
volumes:
|
||||||
|
- name: data
|
||||||
|
persistentVolumeClaim:
|
||||||
|
claimName: oil-calculator-data
|
||||||
|
restartPolicy: OnFailure
|
||||||
|
imagePullSecrets:
|
||||||
|
- name: regcred
|
||||||
Reference in New Issue
Block a user