98 lines
3.1 KiB
Bash
98 lines
3.1 KiB
Bash
#!/usr/bin/env bash
|
|
#
|
|
# Wakdo - backup quotidien de la BDD
|
|
#
|
|
# Execute par cron a 03h00 (voir /etc/crontabs/root).
|
|
# Dump complet de la BDD dans /backups (bind-mount vers ./var/backups),
|
|
# compresse gzip, horodate, rotation sur 14 derniers dumps.
|
|
#
|
|
# Variables d'env lues (injectees par docker-compose depuis .env) :
|
|
# - DB_HOST
|
|
# - DB_PORT
|
|
# - DB_NAME
|
|
# - DB_USER (on utilise le user applicatif, pas root)
|
|
# - DB_PASSWORD
|
|
#
|
|
# Le USER applicatif a un privilege restreint (moindre privilege) : DML
|
|
# (SELECT/INSERT/UPDATE/DELETE) + SHOW VIEW, TRIGGER, LOCK TABLES sur wakdo,
|
|
# sans DDL ni GRANT OPTION. mysqldump --single-transaction (ci-dessous) n'exige
|
|
# que SELECT (+ SHOW VIEW/TRIGGER pour ces objets). Privileges poses par
|
|
# db/init/10-scope-app-user.sh (volume vierge) ou manuellement (base existante).
|
|
#
|
|
# Exit codes :
|
|
# 0 - backup OK
|
|
# 1 - variables env manquantes
|
|
# 2 - mysqldump a echoue
|
|
# 3 - rotation a echoue
|
|
|
|
set -euo pipefail
|
|
|
|
BACKUP_DIR="/backups"
|
|
RETENTION_DAYS=14
|
|
TIMESTAMP="$(date +%Y%m%d_%H%M%S)"
|
|
DUMP_FILE="${BACKUP_DIR}/wakdo_${TIMESTAMP}.sql.gz"
|
|
|
|
log() {
|
|
echo "[backup-db $(date -Iseconds)] $*" >&2
|
|
}
|
|
|
|
# --- Verification variables ---
|
|
for var in DB_HOST DB_PORT DB_NAME DB_USER DB_PASSWORD; do
|
|
if [ -z "${!var:-}" ]; then
|
|
log "ERROR: variable $var vide ou non definie"
|
|
exit 1
|
|
fi
|
|
done
|
|
|
|
# --- Verification que /backups est ecrivable ---
|
|
if [ ! -w "${BACKUP_DIR}" ]; then
|
|
log "ERROR: ${BACKUP_DIR} n'est pas ecrivable (verifier bind-mount et permissions)"
|
|
exit 1
|
|
fi
|
|
|
|
# --- Dump ---
|
|
log "demarrage dump BDD ${DB_NAME} depuis ${DB_HOST}:${DB_PORT}"
|
|
# --single-transaction : dump consistent sans LOCK TABLES (innodb only).
|
|
# --routines --triggers : inclut procedures stockees et triggers (si on en a).
|
|
# --no-tablespaces : evite le besoin de PROCESS privilege.
|
|
if ! mysqldump \
|
|
--host="${DB_HOST}" \
|
|
--port="${DB_PORT}" \
|
|
--user="${DB_USER}" \
|
|
--password="${DB_PASSWORD}" \
|
|
--single-transaction \
|
|
--routines \
|
|
--triggers \
|
|
--no-tablespaces \
|
|
--default-character-set=utf8mb4 \
|
|
"${DB_NAME}" \
|
|
| gzip -9 > "${DUMP_FILE}"; then
|
|
log "ERROR: mysqldump a echoue"
|
|
# Supprime un dump partiel si mysqldump a commence a ecrire.
|
|
rm -f "${DUMP_FILE}"
|
|
exit 2
|
|
fi
|
|
|
|
# --- Verification du dump ---
|
|
# Un dump vide ou quasi-vide = probleme silencieux (ex: mauvais USER GRANT).
|
|
# On exige une taille min de 512 octets pour alerter tot.
|
|
DUMP_SIZE="$(stat -c '%s' "${DUMP_FILE}")"
|
|
if [ "${DUMP_SIZE}" -lt 512 ]; then
|
|
log "ERROR: dump suspect (${DUMP_SIZE} octets), supprime pour ne pas polluer la rotation"
|
|
rm -f "${DUMP_FILE}"
|
|
exit 2
|
|
fi
|
|
|
|
log "dump OK : ${DUMP_FILE} (${DUMP_SIZE} octets)"
|
|
|
|
# --- Rotation : supprime les dumps plus vieux que RETENTION_DAYS ---
|
|
if ! find "${BACKUP_DIR}" -maxdepth 1 -type f -name 'wakdo_*.sql.gz' \
|
|
-mtime "+${RETENTION_DAYS}" -delete; then
|
|
log "WARNING: rotation incomplete"
|
|
exit 3
|
|
fi
|
|
|
|
REMAINING="$(find "${BACKUP_DIR}" -maxdepth 1 -type f -name 'wakdo_*.sql.gz' | wc -l)"
|
|
log "rotation OK : ${REMAINING} dumps conserves"
|
|
|
|
exit 0
|