Backupscript

Hier ein Script welches Dateien und MySQL-Datenbanken sichern kann.
In den ersten Zeilen steht auch wie man es installiert.

#!/bin/bash
# 

### Installation:
## Auf jeweiligem Server per SSH verbinden und folgendes ausführen:
## touch /usr/local/bin/backup.sh
## chmod a+x /usr/local/bin/backup.sh && ln -s /usr/local/bin/backup.sh /etc/cron.daily/ && vi /usr/local/bin/backup.sh
##
## In die neue Datei diesen Inhalt hineinkopieren und die Variablen anpassen, danach Fertig.


# Datensicherungsziel OHNE abschliessenden Slash
DEST="/datensicherung"
TMPDIR="${DEST}/tmpbackup"
BACKUPLOG=${DEST}/${BINHOSTNAME}-backup.log
MAILRCPT="<EMPFÄNGER_MAILADRESSE>"

# Dateien loeschen die aelter als X Minuten sind (1440 = 24 Stunden)
OLDMIN="1380" # 23 Stunden

# MySQL Zugangsdaten
DBHOST=""
DBUSER=""
DBPASS=""

# MySQL Datenbanken (Leerzeichen getrennt)
DBS=""
DBIGNORE="(^mysql|_schema)" # Grep Regex

# MongoDB Zugangsdaten
MONGODBUSER=""
MONGODBPASS=""

# Datei -/Ordnerpfade (Leerzeichen getrennt, ohne abschliessenden Slash)
DIRS=""

#### Ab hier Finger weg, wenn keine Ahnung ####

# binary Pfade
BINHOSTNAME=`which hostname`
BINMYSQLDUMP=`which mysqldump`
BINTAR=`which tar`
BINGZIP=`which gzip`
BINCAT=`which cat`
BINFIND=`which find`
BINRM=`which rm`
BINMOUNT=`which mount`
BINUMOUNT=`which umount`
BINDOCKER=`which docker`
BINDOCKERCOMPOSE=`which docker-compose`
BINMAIL=`which mail`
BINSENDMAIL=`which sendmail`
BINSSMTP=`which ssmtp`
DOCKERCONTAINERDIR="/var/lib/docker/volumes"

# Timestamp fuer logging
LOGTS () {
  date "+%b %d %Y %H:%M:%S"
}

# Filename prefix
FILEPREFIX="$(date +%Y-%m-%d_%H-%M).`${BINHOSTNAME} -s`"


#
# Mount Check
#
ERROR=0
if grep -qs "${DEST}" /proc/mounts; then
        echo "${LOGTS} It's mounted."

        if touch "${DEST}/test.touch" 2>/dev/null; then
                rm "${DEST}/test.touch"
                echo "${LOGTS} It's writeable."
        else
          echo "${LOGTS} WARNUNG: Ziel nicht beschreibbar."
                ERROR="1"
        fi

else
        echo "${LOGTS} WARNUNG: Ziel nicht gemounted."
        ERROR=1
fi

if [ ${ERROR} != 0 ]; then
        echo "${LOGTS} WARNUNG: Versuche mount!"
        ${BINUMOUNT} ${DEST} 2>/dev/null
        ${BINMOUNT} ${DEST}

        if touch "${DEST}/test.touch" 2>/dev/null; then
                rm "${DEST}/test.touch"
                echo "${LOGTS} It's writeable."
        else
                echo "${LOGTS} WARNUNG: Ziel immer noch nicht beschreibbar."
                exit 1
        fi

fi


function db_backup () {

#
# MySQL
#

if [ ! -z "$DBUSER" ]; then
  DBS=$(/usr/bin/mysql -u$DBUSER -p$DBPASS -h$DBHOST --batch --disable-column-names -e "SHOW DATABASES;")
  if [ ! -z "$DBIGNORE" ]; then
    DBS=$(echo "$DBS" | grep -vE "$DBIGNORE")
  fi
  for db in ${DBS}
  do
    echo "$(LOGTS): sichere Datenbank: $db"
    /usr/local/bin/mysqldump -u${DBUSER} --password=${DBPASS} -h $DBHOST --opt $db > "${DEST}/${FILEPREFIX}.$db.sql"
  done
  if [ $? -eq 0 ]; then
    DBBACKUP=0
  else
    DBBACKUP=1
  fi
fi
}

function influxdb_backup () {
#
# Influxdb
#

INFLUXCONTAINER=$(${BINDOCKER} ps -f "name=influxdb2" --format "{{.ID}}")

echo "$(LOGTS): sichere Influxdb"

${BINDOCKER} exec -t ${INFLUXCONTAINER} influx backup /backup/${FILEPREFIX}-influx -t <INFLUXDB_TOKEN>

if [ $? -eq 0 ]; then
    INFLUXDBBACKUP=0
  else
    INFLUXDBBACKUP=1
  fi

mv <PFAD_ZU_EXT_BACKUPDIR_AUSSERHALB_DES_CONTAINERS/${FILEPREFIX}-influx ${DEST}
}

unction mongodb_backup () { 
#
# mongodb
#
echo "$(LOGTS): sichere Mongodb"

MONGODBLIST=$(${BINDOCKER} exec mongodb sh -c "mongo -u ${MONGODBUSER} -p ${MONGODBPASS} --quiet --eval  \"printjson(db.adminCommand('listDatabases'))\"| jq  '.databases[].name' | tr -d '\"'")

for mdb in $MONGODBLIST
do
  ${BINDOCKER} exec mongodb sh -c "exec mongodump -u ${MONGODBUSER} -p ${MONGODBPASS} --authenticationDatabase admin --quiet --tlsInsecure --db ${mdb} --archive --gzip --dumpDbUsersAndRoles > /backup/mongodb-${mdb}-$FILEPREFIX.archive.gz"
done

if [ $? -eq 0 ]; then
    MONGODBBACKUP=0
  else
    MONGODBBACKUP=1
  fi

}

function paperless_backup () {
#
# Paperless
#
## backup paperless
echo "$(LOGTS): sichere Paperless"

${BINDOCKER} exec -t paperless-ngx document_exporter ../export -d -z

if [ $? -eq 0 ]; then
    PAPERLESSBACKUP=0
  else
    PAPERLESSBACKUP=1
  fi

}

function file_backup () {
#
# Dateien
#
echo "${LOGTS} sichere Dateien: ${DIRS}"

${BINTAR} -zcvf "${DEST}/${FILEPREFIX}.files.tar.gz" ${DIRS} 1> /dev/null

CONTAINER=$(ls <PFAD_ZU_DOCKER_COMPOSE_PROJEKTEN>)
## stop containers
for application in ${CONTAINER}
do
  echo "$(LOGTS): Stoppe Docker Container: $application."
  ${BINDOCKER} compose -f ${DOCKERCONTAINERDIR}/$application/docker-compose.yml pause
done

# sync files
echo "$(LOGTS): sichere Dateien aus: ${DIRS}"
mkdir ${TMPDIR}
rsync -a ${DIRS} ${TMPDIR}
if [ $? -eq 0 ]; then
  FILEBACKUP=0
else
  FILEBACKUP=1
fi

## start containers
for application in ${CONTAINER}
do
  echo "$(LOGTS): Starte Docker Container: $application."
  ${BINDOCKER} compose -f ${DOCKERCONTAINERDIR}/$application/docker-compose.yml unpause
done

# tar synced files
echo "$(LOGTS): Erstelle Archiv aus : ${TMPDIR}"
${BINTAR} -cvf "${DEST}/${FILEPREFIX}.files.tar" ${TMPDIR}/* 1> /dev/null
# ${BINTAR} -zcvf "${DEST}/${FILEPREFIX}.files.tar.gz" ${TMPDIR}/* 1> /dev/null
if [ $? -eq 0 ]; then
  FILETARBACKUP=0
else
  FILETARBACKUP=1
fi
echo "$(LOGTS): Archiv erstellt, rämue auf."
rm -r ${TMPDIR}
}

function file_purge () {
#
# Alte Backups loeschen
#
echo "$(LOGTS): loesche Backups welche aelter als ${OLDMIN} Sekunden sind"
${BINFIND} ${DEST}/ -type f \( -iname \*.tar -o -iname \*.sql \) -mmin +${OLDMIN} -exec ${BINRM} {} \;
${BINFIND} ${DEST}/ -type d -name "*-influx" -mmin +${OLDMIN} -exec ${BINRM} -r {} \;
}

function send_mail () {
#
# Mail Backup-Log
#
if ! [[ "1" =~ ^(${DBBACKUP}|${INFLUXDBBACKUP}|${MONGODBBACKUP}|${PAPERLESSBACKUP}|${FILEBACKUP}|${FILETARBACKUP})$ ]]; then
#if [ "${DBBACKUP}" -eq 0 ] && [ "${INFLUXDBBACKUP}" -eq 0  ] && [ "${FILEBACKUP}" -eq 0  ] && [ "${FILETARBACKUP}" -eq 0  ]; then
  SUBJECT="OK: $(hostname)_Backup"
else
  SUBJECT="WARN: $(hostname)_Backup-Fehler"
fi

echo "$(LOGTS): Versende Mail"
#####${BINMAIL} ${MAILRCPT} -s ${SUBJECT} << EOF
#####${BINSENDMAIL} ${MAILRCPT} << EOF
#####${BINSSMTP} ${MAILRCPT} << EOF
subject:"${SUBJECT}"

Die erstellten Sicherungsdateien sind:
`ls -lh -d ${DEST}/${FILEPREFIX}*|awk '{printf "%-4s %-3s %-2s %-5s %s\n", $5, $6, $7, $8,$9}'`

Informationen zum Sicherungslog: `ls -l ${BACKUPLOG}|awk '{print $9}'`
`cat ${BACKUPLOG}|grep "$(date "+%b %d %Y")"`


echo "Errorlevel von DBBACKUP ${DBBACKUP}"
echo "Errorlevel von INFLUXDBBACKUP ${INFLUXDBBACKUP}"
echo "Errorlevel von MONGODBBACKUP ${MONGODBBACKUP}"
echo "Errorlevel von PAPERLESSBACKUP ${PAPERLESSBACKUP}"
echo "Errorlevel von FILEBACKUP ${FILEBACKUP}"
echo "Errorlevel von TARBACKUP ${FILETARBACKUP}"
EOF
}

#DBBACKUP=0
#INFLUXDBBACKUP=0
#MONGODBBACKUP=1
#PAPERLESSBACKUP=0
#FILEBACKUP=0
#FILETARBACKUP=0


db_backup
influxdb_backup
mongodb_backup
paperless_backup
file_backup
file_purge
send_mail

exit 0

Inkrementelles Backup

Damit man täglich nicht immer die selbe Menge sichern muss, sondern nur die Änderungen, weist man tar an eine „Inkrementelle Sicherung“ zu erstellen.
Man fügt zum tar-Befehl einfach den Parameter -g <PFAD_ZU>/<SNAPSHOTFILE> hinzu.
Um dann zwischendurch noch Komplette Sicherungen zu erstellen kommt noch wahlweise der Parameter –level=0
In unserem Script fügt man oben einen neuen Parameter hinzu und ändert folgende Zeile:

+++ SNAPFILE=${DEST}/snap.snar
--- ${BINTAR} -zcvf "${DEST}/${FILEPREFIX}.files.tar.gz" ${DIRS} 1> /dev/null
+++ ${BINTAR} -zcvf "${DEST}/${FILEPREFIX}.files.tar.gz" -g ${SNAPFILE} $1 ${DIRS} 1> /dev/null

Nun fügt man in der /etc/crontab diese Zeilen hinzu, um z.B. Sonntags eine komplette und an den sonstigen Tagen einen inkrementelle Sicherung zu erstellen.

# m h dom mon dow user  command
<MINUTE> <STUNDE> * * 0   root  <PFAD_ZU>/backup.sh "--level=0" > <PFAD_ZU_FULLBACKUP_LOG>
<MINUTE> <STUNDE> * * 1-6   root  <PFAD_ZU>/backup.sh > <PFAD_ZU_INKBACKUP_LOG>