feat: tägliches PostgreSQL-Backup mit 1 GB Rotation (#87)
- backup/Dockerfile: Alpine 3.21 + postgresql-client + dcron - backup/backup.sh: pg_dump -> MD5-Checksum-Vergleich (skip bei unveränderter DB) -> gzip-komprimiertes Backup mit Timestamp -> Rotation: älteste .sql.gz löschen bis Gesamtgröße < 1 GB - docker-compose.yml: neuer Service 'backup', Volume 'backup_data' - Cronjob: täglich 03:00 UTC
This commit is contained in:
parent
28b7e83297
commit
d66f0d65c3
3 changed files with 86 additions and 0 deletions
14
backup/Dockerfile
Normal file
14
backup/Dockerfile
Normal file
|
|
@ -0,0 +1,14 @@
|
||||||
|
FROM alpine:3.21
|
||||||
|
|
||||||
|
# Install PostgreSQL client and dcron (lightweight cron daemon)
|
||||||
|
RUN apk add --no-cache postgresql-client dcron
|
||||||
|
|
||||||
|
COPY backup.sh /usr/local/bin/backup.sh
|
||||||
|
RUN chmod +x /usr/local/bin/backup.sh
|
||||||
|
|
||||||
|
# Crontab: run backup daily at 03:00 UTC
|
||||||
|
RUN echo "0 3 * * * /usr/local/bin/backup.sh >> /var/log/backup.log 2>&1" \
|
||||||
|
> /etc/crontabs/root
|
||||||
|
|
||||||
|
# crond -f: run in foreground; -l 2: log level notice
|
||||||
|
CMD ["crond", "-f", "-l", "2"]
|
||||||
57
backup/backup.sh
Normal file
57
backup/backup.sh
Normal file
|
|
@ -0,0 +1,57 @@
|
||||||
|
#!/bin/sh
|
||||||
|
set -e
|
||||||
|
|
||||||
|
BACKUP_DIR="${BACKUP_DIR:-/backups}"
|
||||||
|
DB_HOST="${POSTGRES_HOST:-db}"
|
||||||
|
DB_NAME="${POSTGRES_DB:-krisenvorrat}"
|
||||||
|
DB_USER="${POSTGRES_USER:-krisenvorrat}"
|
||||||
|
MAX_SIZE_BYTES=$((1 * 1024 * 1024 * 1024)) # 1 GB
|
||||||
|
|
||||||
|
mkdir -p "$BACKUP_DIR"
|
||||||
|
|
||||||
|
TIMESTAMP=$(date -u +"%Y-%m-%d_%H%M")
|
||||||
|
BACKUP_FILE="${BACKUP_DIR}/${DB_NAME}_${TIMESTAMP}.sql.gz"
|
||||||
|
CHECKSUM_FILE="${BACKUP_DIR}/.last_checksum"
|
||||||
|
|
||||||
|
echo "[$(date -u)] Starting backup of database '${DB_NAME}' on host '${DB_HOST}'..."
|
||||||
|
|
||||||
|
# Dump to temp file for checksum comparison
|
||||||
|
TEMP_DUMP=$(mktemp)
|
||||||
|
export PGPASSWORD="${POSTGRES_PASSWORD:-}"
|
||||||
|
pg_dump -h "$DB_HOST" -U "$DB_USER" "$DB_NAME" > "$TEMP_DUMP"
|
||||||
|
|
||||||
|
# Compute MD5 of dump content to detect changes
|
||||||
|
CURRENT_CHECKSUM=$(md5sum "$TEMP_DUMP" | cut -d' ' -f1)
|
||||||
|
LAST_CHECKSUM=""
|
||||||
|
if [ -f "$CHECKSUM_FILE" ]; then
|
||||||
|
LAST_CHECKSUM=$(cat "$CHECKSUM_FILE")
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ "$CURRENT_CHECKSUM" = "$LAST_CHECKSUM" ]; then
|
||||||
|
echo "[$(date -u)] No changes since last backup. Skipping."
|
||||||
|
rm -f "$TEMP_DUMP"
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Compress and save
|
||||||
|
gzip -c "$TEMP_DUMP" > "$BACKUP_FILE"
|
||||||
|
rm -f "$TEMP_DUMP"
|
||||||
|
printf '%s' "$CURRENT_CHECKSUM" > "$CHECKSUM_FILE"
|
||||||
|
echo "[$(date -u)] Backup saved: ${BACKUP_FILE}"
|
||||||
|
|
||||||
|
# Rotate: remove oldest .sql.gz until total backup dir size <= 1 GB
|
||||||
|
while true; do
|
||||||
|
TOTAL=$(du -sb "$BACKUP_DIR" | cut -f1)
|
||||||
|
if [ "$TOTAL" -le "$MAX_SIZE_BYTES" ]; then
|
||||||
|
break
|
||||||
|
fi
|
||||||
|
OLDEST=$(ls -tr "${BACKUP_DIR}"/*.sql.gz 2>/dev/null | head -1)
|
||||||
|
if [ -z "$OLDEST" ]; then
|
||||||
|
echo "[$(date -u)] Warning: Cannot reduce backup dir below 1 GB, no more files to delete."
|
||||||
|
break
|
||||||
|
fi
|
||||||
|
echo "[$(date -u)] Rotation: removing ${OLDEST} (dir total: ${TOTAL} bytes)"
|
||||||
|
rm -f "$OLDEST"
|
||||||
|
done
|
||||||
|
|
||||||
|
echo "[$(date -u)] Backup complete."
|
||||||
|
|
@ -26,5 +26,20 @@ services:
|
||||||
depends_on:
|
depends_on:
|
||||||
- db
|
- db
|
||||||
|
|
||||||
|
backup:
|
||||||
|
build: ./backup
|
||||||
|
container_name: krisenvorrat-backup
|
||||||
|
restart: unless-stopped
|
||||||
|
environment:
|
||||||
|
- POSTGRES_HOST=db
|
||||||
|
- POSTGRES_DB=krisenvorrat
|
||||||
|
- POSTGRES_USER=krisenvorrat
|
||||||
|
- POSTGRES_PASSWORD=krisenvorrat
|
||||||
|
volumes:
|
||||||
|
- backup_data:/backups
|
||||||
|
depends_on:
|
||||||
|
- db
|
||||||
|
|
||||||
volumes:
|
volumes:
|
||||||
pgdata:
|
pgdata:
|
||||||
|
backup_data:
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue