Die Grundidee : die Daten automatisch auf einen entfernten Backup-Server oder eine entfernte NAS Station schicken.
Unsere Backups sollen nicht nur automatisch und sicher laufen. Die Daten müssen auch unbedingt periodisch auf eine "entfernte" Lokation (zum Beispiel zu uns ins Büro) kopiert werden. Darum wird auf jedem Server (im Idelafalle) täglich eine Sicherung aller Typo3 Domains automatisiert auf eine im Server vorhandene separate Backup-Platte kopiert. Das ist hier bereits ausführlich besprochen worden.
Und zur Zeit einmal am Wochende wird Nachts gleichzeitig mit der normalen Sicherung auch ein "scp"-Transfer "auf einen Server weit weg" gestartet.
Und wie wir das gelöst haben, kommt jetzt.
"SCP" ist das sogenannte "secure copy programm".
Dieses Kopierprogramm nutzt die gleichen Verschlüsselungsmechanismen, die auch putty benutzt. Sie können per scp Dateien von SSH-Server zu SSH-Server kopieren. Doch wir wollen es per Cronjob und Shell-Scripten automatisieren und Nachts laufen lassen.
Unsere Grundlagen sind die laufenden TYPO3-Server in unserem Datacenter, bei uns alles auf Linux unter Suse. Und die Ziele sind bei uns autarke lokale Buffalo NAS Boxen auch alles auf Linux mit Terabyte-Platten im lokalen Firmen-EDV-Raum hinter unserer Firewall.
.
Die Vorbereitung zum automatischen scp handshake per SSH.
Mitten in unserem "copyhome" Script befindet sich die Zeile mit dem "scp" (secure copy) Kommando.
Doch Nachts ist niemand da, der das Passwort für die sichere Verbindung eintippt. Um das zu automatisieren, muß man die Schlüssel auf dem Quell-Server erzeugen und auf dem Ziel-Server hinterlegen.
Das machen wir so:
Auf dem Quell-Server (oder Rechner) (bei uns Suse Linux) erzeugt man
als "root" User oder mit "sudo ssh-keygen -t dsa" und 3 x ENTER den Schlüssel und damit erhält man den Keyfile (den Schlüssel) im Verzeichnis "/root/.ssh/id_dsa.pub".
ssh-keygen -t dsa
Generating public/private dsa key pair.
Enter file in which to save the key (/root/.ssh/id_dsa):
Enter passphrase (empty for no passphrase):
Enter same passphrase again:
Your identification has been saved in /root/.ssh/id_dsa.
Your public key has been saved in /root/.ssh/id_dsa.pub.
The key fingerprint is:
38:b4:30:16:22:62:f8:47:81:31:c4:bc:f1:08:06:6c root@www51.ipw.net
The key's randomart image is:
+--[ DSA 1024]----+
|B==oo. |
|=E=o.. |
|oo *+ . |
| +.o+ o |
| . + S |
| . |
| |
| |
| |
+-----------------+
damit ist das Programm fertig
[www51] / $
Dieser Schlüssel (also der Inhalt der Datei) muß muss dann (mit winscp oder anderen Hilfsmitteln) auf den Zielrechner (bei uns die Buffalo-Linkstation) in den neu anzulegenden File "/root/.ssh/authorized_keys" kopiert werden. Dieser File "authorized_keys" kann mehrere keys (Schlüssel) sowohl von mehreren Usern und als auch Quell-Servern enthalten (man muß das editieren).
Zum Editieren ist der "joe" nicht so gut geeignet, weil er diese langen Zeilen am Bildschirmende abtrennt und einfach nicht überträgt.)
Der Schlüssel ist an den Hostnamen und Usernamen gebunden. Bei uns laufen alle diese "copyhome" Skripte als "root"! Zum Debuggen kann man SCP mit der Option "-v" aufrufen.
Hier kommt das gesamte "copy-home" Shell-Script Verion 2.8
#!/bin/bash -u
# option "-u" = exit the script if a variable is missing content
# call with option -x to debug
# it must be bash, in order to use arrays
#
# Backup Typo3 Installation, 2009-07-11 Rudolf Reuter
# put script in the target folder and start from there.
# backup 3 folder /fileadmin/, /typo3conf/ and /uploads/ und die global-extensions and gzip
# backup als mysql dump der einzelnen typo3 database
# C3. gzip archive
# FD send activity email, or FTP
#
# possible only with the help of www.tldp.org/LDP/abs/abs-guide.pdf
# abs = advanced bash scripting
# Version is shown at the log echo .
# 2010-01-04-GR V2.3 die globalen banner-bilder werdenmit gesichert - /typo3/banner-vorlagen
# der scp copy Bereich ist nur noch hier drinnen
#
# 2010-01-19-GR V2.7 - zz ist jetzt zz_log_text und kleine Fehler sind raus
# 2010-01-24-GR V2.8 - alle passwoerter jetzt in Variablen kommen aus server.dat
#
# copy home inklusive backup !!!!!
#
# includefile 1: domain-data file with multi-domain !!! parametern
source domains.dat
element_count=${#ROOT[@]}
# includefile 2: server-data with server name, Linux version, folder path
source server.dat
echo "==== domains.dat und server.dat gelesen ======"
# System setup - hier nichts ändern, nur in Haupt-Script !!
VERSION="2.8 vom 24.01.2010 -rr-gr"
LOCKFILE="/var/lock/bak_typo3_$$.lock"
PRODUCTION=0 # 0= testing, 1= PRODUCTION - dieser mode wird automatisch gesetzt
# wenn in server.dat das Prefix-Webverzeichnis gesetzt ist, dann ist es immer production = 1
# wenn Variable leer, dann lokaler Testmodus
if test -d $PFXWWW; then
PRODUCTION=1
echo "==== Produktions-Version = 1 erkannt ======"
else
PFXWWW="/var/lib" # for testing only
echo "==== Test-Version = 0 !!!! erkannt ======"
fi
# todo : jahreszahl abfragen und verzeichnis automatisch erzeugen bzw. abprüfen
if ! test -d $PFXBAK; then
PFXBAK="/bak-vol" # for Production - steht aber auch in server.dat !!!
fi
SCPTEMPDIR="/bak-vol/scp-tmp-dir"
mkdir "$SCPTEMPDIR"
DATE=$(date +"%Y%m%d") # e.g. 20090711
NOW=$(date +"%Y%m%d%H%M") # e.g. 200907111709
MYSQLSERVER=127.0.0.1
##### SCP Kennwort handshake Initialisierung - wird in diesem Script gebraucht !!
# SCP without password prompt: client: ~/ssh-keygen -t dsa 3xENTER, copy ~/.ssh/id_dsa.pub to server (Buffalo) ~/.ssh/authorized_keys
SCPENA=1 # set to 0 to disable, 1 = enabled
if [ $PRODUCTION -eq "1" ]; then
# scp geht über die Durchleitung der Fritzbox !!!!!! dort IP des Inhaus-Servers eintragen
echo "==== Production = 1 - scp home ist aktiviert"
# SCPHOST und SCPUSER und SCPTARGET stehen jetzt alle in server.dat !!
else
# testversion wird local nach Inhaus-Server gesendet
SCPHOST="192.168.17.100"
SCPUSER="root"
SCPTARGET="/mnt/disk1/share"
fi
# In case of an error, send an email to $EMAILADR, takes 2 parameters: 1. command, 2. line number
function test_error_email() {
if [ ! "$?" == "0" ]; then
echo "Folder: $BACKUPDIR, Time: $NOW, $1, LINENO: $2 " | mail -s "backup-typo3.sh, Fehler auf $RDESERVER" "$EMAILADR1"
fi
}
##### SCP Kennwort handshake Ende =================================
function A_bakup_folder() {
# ACHTUNG: wir sichern von jetzt an alles mit dem relativen Pfad (ab Version 2.0 !!!)
# bei der tar Zeile das -P nicht vergessen - don't strip leading `/´s from file names
echo "==== pushd nach $dompath"
pushd $dompath > /dev/null
tar -Pzcf $BACKUPDIR/$NOW-$idom-$domainroot-fileadmin.tgz "fileadmin"
test_error_email 'Error: tar fileadmin' $LINENO
echo "==== $domainroot == fileadmin gepackt"
tar -Pzcf $BACKUPDIR/$NOW-$idom-$domainroot-typo3conf.tgz "typo3conf"
test_error_email 'Error: tar typo3conf' $LINENO
echo "==== $domainroot == typpo3conf gepackt"
tar -Pzcf $BACKUPDIR/$NOW-$idom-$domainroot-uploads.tgz "uploads"
test_error_email 'Error: tar uploads' $LINENO
echo "==== $domainroot == uploads gepackt"
popd > /dev/null
# echo "==== popd ausgefuehrt"
echo "==== $domainroot == alle 3 Verzeichnisse gepackt"
# Typo3 globale Files sichern - wird nur einmal ausgefuehrt - fuer eine typo3 version -muss noch verbessert werden !!
if test ! -e "$BACKUPDIR/$NOW-typo3-globale-ext.tgz" ; then
pushd $dompath > /dev/null
tar -Pzcf $BACKUPDIR/$NOW-typo3-globale-ext.tgz "typo3/ext"
test_error_email 'Error: tar typo3-global-ext' $LINENO
echo "==== Hier wurden die globalen Typo3-Extensions gepackt"
tar -Pzcf $BACKUPDIR/$NOW-typo3-globale-werbebanner-folder.tgz "typo3/banner-vorlagen"
cp "$BACKUPDIR/$NOW-typo3"* $SCPTEMPDIR ## ist ein Klimmzug : es wird nur eine Version bei der ersten Domain mit kopiert
test_error_email 'Error: tar globale banner-vorlagen' $LINENO
echo "==== Hier werden die globalen Werbe-Banner-Vorlagen gepackt"
popd > /dev/null
fi
dummy="dummy"
}
# Funktion B - wird nicht mehr benutzt - wird demnaechst ein eigenes script werden === TEMPLATE only ===
function B_rm_temp() {
# delete all files, but not index.html - Not needed, if cache is not emptied
#rm `find $dompath/typo3temp/GB -type f \( ! -iname "." \) | grep -v index.html`
#rm `find $dompath/typo3temp/llxml -type f \( ! -iname "." \) | grep -v index.html`
#rm `find $dompath/typo3temp/pics -type f \( ! -iname "." \) | grep -v index.html`
#rm `find $dompath/typo3temp/temp -type f \( ! -iname "." \) | grep -v index.html`
dummy="dummy"
}
function C_bakup_database() {
# Flush TYPO3 All Caches === TEMPLATE only ===
#for table in $TABLES
# do
# mysql -e 'TRUNCATE TABLE '"$table"'' -h $MYSQLSERVER -D $TYPO3DB -u $TYPO3USER --password=$TYPO3PASS
# dummy="dummy"
# done
#mysqldump -h $MYSQLSERVER -u $TYPO3USER -p$TYPO3PASS $TYPO3DB | gzip >$BACKUPDIR/$NOW-$domain-DB-mysql.sql.gz
# Backup databases, ignore all cache_* tables aus Typo3 Version 4.2.6
EX1="--ignore-table=$TYPO3DB.cache_extensions"
EX2="--ignore-table=$TYPO3DB.cache_hash"
EX3="--ignore-table=$TYPO3DB.cache_imagesizes"
EX4="--ignore-table=$TYPO3DB.cache_md5params"
EX5="--ignore-table=$TYPO3DB.cache_pages"
EX6="--ignore-table=$TYPO3DB.cache_pagesection"
EX7="--ignore-table=$TYPO3DB.cache_typo3temp_log"
# Temp-Index databases index search
EX8="--ignore-table=$TYPO3DB.index_fulltext"
EX9="--ignore-table=$TYPO3DB.index_grlist"
EX10="--ignore-table=$TYPO3DB.index_grlist"
EX11="--ignore-table=$TYPO3DB.index_rel"
EX12="--ignore-table=$TYPO3DB.index_section"
EX13="--ignore-table=$TYPO3DB.index_stat_search"
EX14="--ignore-table=$TYPO3DB.index_stat_word"
mysqldump -h $MYSQLSERVER -u $TYPO3USER -p$TYPO3PASS $EX1 $EX2 $EX3 $EX4 $EX5 $EX6 $EX7 $EX8 $EX9 $EX10 $EX11 $EX12 $EX13 $EX14 $TYPO3DB | gzip >$BACKUPDIR/$NOW-$idom-$domainroot-DB-mysql.sql.gz
test_error_email 'Error: mysqldump' $LINENO
echo "==== Gesamte mysql Datenbank fuer $domainroot ist gedumpt und ist gepackt"
# Deny file access to database backups
# chmod 600 -R $BACKUPDIR/
}
function D_scp-copy-home-loop() {
if [ $SCPENA -eq 1 ]; then # wenn production - dann secure copy to target storage
# tar bakup for scp transfer
echo "transfer-file wird vorbereitet - Beginn at : "`date`>>$zz_log_file
echo "==== Es wird der scp transfer-file vorbereitet at: "`date`
cp "$BACKUPDIR/$NOW-$idom-$domainroot"* $SCPTEMPDIR
echo "==== tar files $domainroot nach $SCPTEMPDIR kopiert"
echo "==== Datenmenge im folder $SCPTEMPDIR = " "`du -s -h $SCPTEMPDIR`"
## ls -l "$SCPTEMPDIR"
## read input
tar -Pcf "$PFXBAK/$NOW-$idom-$domainroot.tar" "$SCPTEMPDIR"*
## read input
echo "==== $domainroot files in transfer-file nach $BACKUPDIR gepackt "
## echo "==== Kontrolle :"
## ls -l "$PFXBAK"
rm "$SCPTEMPDIR"/*.*
echo "==== Folder $SCPTEMPDIR muss wieder leer sein (4,0K) ""`du -s -h $SCPTEMPDIR`"
echo "==== $domainroot == SCP transfer Start at: "`date`
## ---------- hier steht die scp Zeile
## scp -pq $PFXBAK/$NOW-$idom-$domainroot.tar $SCPUSER@$SCPHOST:$SCPTARGET
## ---------- scp Uebertragung der Inhalte
## echo "==="
## Find out if scp backup file failed or not
if [ "$?" == "0" ]; then
echo "==== $domainroot == SCP transfer End at: "`date`
## cp -f $PFXBAK/$NOW-$domainroot.tar /tmp
rm -f $PFXBAK/$NOW-$idom-$domainroot.tar
echo "SCP transfer file wird wieder geloescht at : "`date`>>$zz_log_file
echo "==== $domainroot == SCP transfer file ist wieder geloescht! "
# secure copy log file addtitional to TAR archive
## finale scp Aktion Nr 3
scp -pq $zz_log_file $SCPUSER@$SCPHOST:$SCPTARGET # # soll hier stehen bleiben, falls Script-Abbruch
# email_body=/tmp/scp.ok
else
email_body=/tmp/scp.fail
echo "Date: $(date)">$email_body
echo "Hostname: $(hostname)" >>$email_body
echo "Backup Set: $BACKUPDIR" >>$email_body
echo "SCP failed" >>$email_body
mail -s "SCP FAILED - Kopiervorgang auf Buffalo Box failed " "$EMAILADR1" <$email_body
rm -f $email_body
echo 'Dieser SCP transfer Vorgang failed at: '`date`>>$zz_log_file
fi
fi
}
# Funktion E - gepackte Scripte, Logfile und /etc/ in transfer-file packen und übertragen
function E_scp-copy-home-once() {
if [ $SCPENA -eq 1 ]; then # wenn production - dann secure copy to target storage
## Nachtrag ab Version 2.6: die verwendeten Shell-Scripte müssen auch uebertragen werden !!!
## zuerst das /etc/ Verzeichnis senden
echo "/etc/ Folder und Backup Scripte werden uebertragen - Beginn at : "`date`>>$zz_log_file
echo "==== Backup Scripte werden uebertragen - Beginn at : "`date`
scp -pq $BACKUPDIR/$NOW-98-www9-folder-etc.tar.gz $SCPUSER@$SCPHOST:$SCPTARGET
echo "das gesamte $RDESERVER /etc/ Verzeichnis ist übertragen at : "`date`>>$zz_log_file
## rm $BACKUPDIR/$NOW-98-www9-folder-etc.tar.gz
scp -pq $BACKUPDIR/$NOW-97-benutzte-backup-scripte.tar.gz $SCPUSER@$SCPHOST:$SCPTARGET
echo "Gepackte Backup-Scripte übertragen at : "`date`>>$zz_log_file
scp -pq $zz_log_file $SCPUSER@$SCPHOST:$SCPTARGET # finaler Log-file nach letztem scp Transfer
fi
}
##### Funktion E Ende
# main routine -------------------------------------------------------------------------------
if [ ! -e $LOCKFILE ]; then
trap "rm -f $LOCKFILE; exit" INT TERM EXIT
touch $LOCKFILE
# make the backup folder, if not yet done - Achtung - der Jahresfolder muss bereits angelegt sein !!!
BACKUPDIR="$PFXBAK/$DATE"
if test ! -d "$BACKUPDIR" ; then
mkdir "$BACKUPDIR"
test_error_email 'Error: mkdir $BACKUPDIR' $LINENO
fi
# write start time and infos to a logfile
zz_log_file="$BACKUPDIR/$NOW-99-backup-log-datei.txt"
echo 'Sicherungsscript copy-home Backup Start at: '`date` >>$zz_log_file
test_error_email 'Error: creating log file' $LINENO
echo 'Script Version:' $VERSION >>$zz_log_file # take care about version number !
echo 'user ID :' $UID >>$zz_log_file
echo 'Linuxversion :' $LINUXVERSION >>$zz_log_file
echo 'Server :' $RDESERVER >>$zz_log_file
echo 'Mail1 :' $EMAILADR1 >>$zz_log_file
echo 'Mail2 :' $EMAILADR2 >>$zz_log_file
## ein Trick aus kosmetischen Gruenden (Kontrolle der Domain-Sicherungen und Dateinamen)
idom=10 ## NEU: Domain Zaehler startet zweistellig mit 10 !!!!!
element_count=$[$element_count+9]
while [ $idom -le $element_count ]; ## die main-routine zaehlt die Domains aus array domain.dat hoch
do
domainroot=""
domainroot="${ROOT[$idom]}"
domainname="${DOMA[$idom]}"
dompath="$PFXWWW/$domainroot"
echo "==== --------------------------------------------------------------------------------"
echo "==== --------------------------------------------------------------------------------" >>$zz_log_file
echo "XXXX Main-Loop Nr. $idom == $domainroot - Start at : "`date`
echo "XXXX Main-Loop Nr. $idom == $domainroot - Start at : "`date` >>$zz_log_file
if test -d "$PFXWWW/$domainroot"; then # test if domain folder exists
UIDROOT=0
if [ $UID -gt $UIDROOT ]; then # echo only if not user root (=cron job)
echo "Backup of domain-root: $domainroot"
fi
TYPO3DB="${DBNA[$idom]}"
TYPO3USER="${DBUS[$idom]}"
TYPO3PASS="${DBPW[$idom]}"
echo 'domain-root :'$domainroot >>$zz_log_file
echo 'domain-name :'$domainname >>$zz_log_file
echo 'typo3database :'$TYPO3DB >>$zz_log_file
echo 'typo3user :'$TYPO3USER >>$zz_log_file
echo 'typo3password :'$TYPO3PASS >>$zz_log_file
echo 'typo3version :'`ls -l $dompath | grep "typo3_src "`>>$zz_log_file # log typo3 version
## ================== Aufruf der Unterroutinen
A_bakup_folder
## B_rm_temp
C_bakup_database
D_scp-copy-home-loop
fi
idom=$[$idom+1] # increase index number
done
## ================== Ende des Main Loops - Abschluss des Scriptes mit Mail und Logfile
## ============================= write end time to the logfile
echo 'end at : '`date`>>$zz_log_file
echo "==== "
echo '==== Ende des Main-Loops at: '`date` '- jetzt wird der Logfile geschrieben.'
## read input
## sichere alle scripte in /etc/backup Verzeichnis
tar -Pzcf $BACKUPDIR/$NOW-97-benutzte-backup-scripte.tar.gz *
test_error_email 'Error: save backup-script' $LINENO
echo "das gesamte $RDESERVER /etc/ Verzeichnis wird gepackt "`date`>>$zz_log_file
echo "==== das gesamte $RDESERVER /etc/ Verzeichnis wird gepackt "
## zuerst das /etc/ Verzeichnis packen
tar -Pzcf $BACKUPDIR/$NOW-98-www9-folder-etc.tar.gz "/etc"
echo "==== das gesamte $RDESERVER /etc/ Verzeichnis ist fertig gepackt "
## ab Version 2.6: die gepackten Scripte müssen auch uebertragen werden !!!
E_scp-copy-home-once
chmod 600 -R $BACKUPDIR/
# write the contents of the backup directory into the logfile, only the last one.
ls -l $BACKUPDIR'/'$NOW* >>$zz_log_file
# send email on success, in PRODUCTION mode only
if [ $PRODUCTION -eq 1 ]; then
cd $BACKUPDIR
echo "==== Jetzt wird die Mail vorbereitet . . . . ==="
## read input
mail -s "Der copy-home-Script auf $RDESERVER ist gelaufen und SCP hat funktioniert " "$EMAILADR1" <$zz_log_file
echo "Datenmenge im folder = " "`du -s -h $BACKUPDIR`" | mail -s "Der copy-home-Script auf $RDESERVER ist gelaufen (siehe Logfile)" "$EMAILADR1"
echo "Datenmenge im folder = " "`du -s -h $BACKUPDIR`" | mail -s "Der copy-home-Script auf $RDESERVER ist gelaufen (siehe Logfile)" "$EMAILADR2"
## echo "==== "
echo '==== Beide Mails sind gesendet at : '`date`
fi
rm $LOCKFILE # following another job can start
trap - INT TERM EXIT
else
echo "==== a backup_typo3 job is already running - wait until /var/lock/bak_typo3_xxx.lock has finished."
fi
exit 0