Diferencialna kumulativna zaloha v BASHi

20.07.2006 16:40 | blackhole_ventYl

Jeden vtip hovori o tom, ze boh moze nad diablom vyhrat v programovani len z toho dovodu, ze "Jesus saves". Zalohovanie je pomerne dolezitym aspektom behu snad kazdeho pocitaca, pretoze dokaze ochranit pred (ne)chcenym vymazanim dat, alebo vypadkom hardware.

Kumulativne zalohy by sa dali popisat, ako postupne zalohy rovnakeho obsahu, pricom tie najstarsie z nich su vzdy zmazane.

Diferencialne zalohovanie je zalohovanie, pri ktorom sa zalohuju len tie subory, ktore od posledneho zalohovania presli zmenou, popripade oproti poslednej zalohe pribudli.

Kumulativne diferencialne zalohovanie by potom mohlo oznacovat zalohovanie, pri ktorom su vytvarane bodove zalohy zmeneneho obsahu zalohovanych adresarov, ktore je doplnene o vytvaranie kompletnych zaloh v mensom casovom useku.

Vyhodou tohto druhu zalohovania je to, ze moze byt spustane castejsie, ako kompletne zalohovanie a v pripade, ze zalohovany obsah nepodlieha sustavnej komplexnej zmene v sirokom rozsahu (databazy) znamena znacne znizenie narokov na diskovu kapacitu potrebnu pre zalohy.

Vedlajsim efektom vytvarania zaloh tohto druhu je, ze vznikaju akesi restore pointy. Je to sposobene tym, ze starsie verzie zalohovanych suborov sa pri opatovnom zalohovani nemazu. Dava to teda moznost vratit sa k niektorej z predoslych verzii suboru a jemne supluje pritomnost CVS alebo SVN systemu, ktory ale aj tak plnohodnotne nenahradi.

Uvediem listingy skriptov pre kumulativnu zalohu, kompletnu zalohu a skript na restore poslednej backupovanej verzie z kumulativnych backupov. Skripty nie su nijako super idealne odladene a niektore casti (hlavne sed skripty) by si zasluzili nejake vylepsenie, ale ako zaklad moze posluzit dobre.

complete.sh:

#!/bin/sh                                                                                 
. ./backup.conf                                                                           
# kompletna zaloha                                                                       
DIRS=`cat ./backup.dirs | sed -e 'H;x;s/\n/ /;x;G;' | tail -n 1`         
FILELIST="";                                                                               
for dir in $DIRS; do
    FILELIST=$FILELIST`find $dir -type f`
done                                                                                       
ARCHIVE_NAME=`date +%Y-%m-%d`.tar.gz;
tar -czvvf $BACKUP_ROOT/complete/$ARCHIVE_NAME $FILELIST > /dev/null 2>&1                 
chmod 0600 $BACKUP_ROOT/complete/$ARCHIVE_NAME
chown $BACKUP_USER:$BACKUP_GROUP $BACKUP_ROOT/complete/$ARCHIVE_NAME

Teraz mozno pristupit k popisu cinnosti. Prvy riadok prevedie vlozenie konfiguracneho suboru backup.conf, ktory je ulozeny v rovnakom adresari, z ktoreho je spusteny backup. Nasleduje komentar a za nim riadok, ktory do premennej DIRS nacita zoznam adresarov, ktore sa spolu so vsetkymi podadresarmi maju zalohovat. sed -e 'H;x;s/\n/ /;x;G;' je prepisovacie pravidlo pre sed, ktore sposobi zamenu vsetkych znakov noveho riadka za medzery. Nasleduje cyklus for, ktory ziska zoznam vsetkych suborov v danych adresaroch.
Potom sa vygeneruje nazov pre archiv a vsetky subory sa pomocou tar-u odarchivuju. Posledne 2 riadky zmenia prava a majitela zalohy tak, aby k archivom nemohol pristupit nikto iny, ako uzivatel, ktoremu zalohy patria. Po zbehnuti tohto skriptu vznikne subor s kompletnou zalohou adresarov.

cumulative.sh:

#!/bin/sh                                                                                 
. ./backup.conf                                                                           
# kumulativna zaloha                                                                       
DIRS=`cat ./backup.dirs | sed -e 'H;x;s/\n/ /;x;G;' | tail -n 1`         
FILELIST="";                                                                               
for dir in $DIRS; do
    FILELIST=$FILELIST`find $dir -type f -mtime -1`"\n"
done                                                                                       
ARCHIVE_NAME=`date +%Y-%m-%d`.tar.gz;                                                     
echo -e -n "$FILELIST" | sed -e "s/\$/ $ARCHIVE_NAME/" >> $BACKUP_ROOT/filelist.bak       
tar -czvvf $BACKUP_ROOT/cumulative/$ARCHIVE_NAME $FILELIST > /dev/null 2>&1               
chmod 0600 $BACKUP_ROOT/cumulative/$ARCHIVE_NAME
chown $BACKUP_USER:$BACKUP_GROUP $BACKUP_ROOT/cumulative/$ARCHIVE_NAME

Tento skript obsahuje oproti skriptu complete.sh len niekolko malych zmien. Prvou zmenou je pridanie parametru -mtime -1 prikazu find, ktory sposobi, ze do zoznamu FILELIST sa dostanu len subory, ktore boli zmenene pred menej, ako 24 hodinami. Druhou zmenou je pridanie riadka echo -e -n ..., ktory sposobi, ze kazdy z tychto suborov, ktore boli zmenene za posledny den bude spolu s nazvom archivu, ktory sa vytvoril, zapisany do logu backupu. Tento log kumulativneho backupu je vyuzity v skripte restore.sh, ktory umoznuje obnovenie poslednej verzie suboru z kumulativnych zaloh:

restore.sh:

#!/bin/sh                                                                                 
. ./backup.conf                                                                           
if [[ "$1" = "" ]]; then
    echo "$0: Je potrebne specifikovat, ktory subor sa ma obnovit"
    exit 1
fi                                                                                         
# zisti v ktorom archive s kumulativnymi zalohami sa nachadza posledna verzia suboru       
ARCHIVE=`cat $BACKUP_ROOT/filelist.bak | grep $1 | tail -n 1`
ARCHIVE=`echo -n "$ARCHIVE" | sed -s 's/^.*[ ]//'`
ARCHNAME=`echo -n "$1" | sed -s 's/^\///'`                                                 
# extrahuje subor z kumulativneho archivu                                                 
PWD=`pwd`
cd /
tar -zxvvf $BACKUP_ROOT/cumulative/$ARCHIVE $ARCHNAME > /dev/null 2>&1
cd $PWD                                                                                   
# prida zaznam o obnoveni suboru z archivu do logu                                         
DATE=`date '+%d. %m. %Y %H:%M:%S'`                                                         
echo "[$DATE] Subor $1 obnoveny z $ARCHIVE uzivatelom `whoami`!" >> $RESTORE_LOG

Ucelom tohto suboru je obnovenie suboru z kumulativnych zaloh v pripade jeho straty, alebo znicenia.

pociatocny blok if ... fi sluzi na osetrenie situacie, kedy nebol zadny nazov suboru, ktory sa ma obnovit a vyhlasi chybu. Za nim nasledujuci prikaz zisti, v ktorom archive kumulativnych zaloh sa nachadza najnovsia verzia predmetneho suboru. Kedze nazov suboru sa vo fileliste uvadza vo forme:

...
nazov_suboru nazov_archivu
...

nasledujuci riadok vrati cisty nazov tar archivu. Posledny prikaz v bloku odstrani z nazvu suboru pociatocne lomitko, pretoze tar pri ukladani suborov pociatocne lomitka odstranuje. Nasleduje prechod do korenoveho adresara a extrahovanie suboru, potom navrat do povodneho pracovneho adresara a zapis informacie o vykonani obnovenia zo zalohy do logu.

Pre uplnost este uvediem ukazkovy obsah suboru backup.conf:

cd /home/backup                                                                           
BACKUP_ROOT=/usr/backup
RESTORE_LOG=/var/log/restore
BACKUP_USER=backup
BACKUP_GROUP=backup

Prikaz cd vykona prechod do adresara, kde je backupovaci skript nainstalovany (u mna /home/backup).
BACKUP_ROOT urcuje adresar, pod ktory sa budu zalohy ukladat.
BACKUP_USER a BACKUP_GROUP urcuju uzivatela a skupinu, na ktoru budu chownnute vsetky zalohovacie archivy.

Subor bakcup.dirs musi obsahovat zoznam zalohovanych adresarov vo forme 1 adresar na riadok.

Uvedene skripty je mozne nastavit v crone tak, aby sa spustali napriklad kazdy den (cumulative.sh) a kazdy tyzden (complete.sh). Skript restore.sh mozno pouzit na rucne obnovenie suboru zo zaloh.

Nie je to sice najoptimalnejsie vyriesene zalohovacie riesenie, ale pomoze ako rychly a pomerne efektivny zalohovaci nastroj tam, kde je zalohovanie nutne rozbehnut v kratkom case.

    • Huh? 24.07.2006 | 09:20
      pichi   Návštevník

      Boha jeho, co to jako má být? To si jako někdo dělá srandu, ne? Napsat
      DIRS=`cat ./backup.dirs | sed -e 'H;x;s/\n/ /;x;G;' | tail -n 1`
      Místo
      DIRS=`sed -n 'H;${x;s/\n/ /g;p}' ./backup.dirs`
      To chce velký kus idiocie. Někdo, kdo si dokázal přečíst v manuálu co dělá H a G a nepřečte si už část o adresách je pěkná trubka a nebo je ještě možnost, že autor nemá ani šajn o programování. Ale jděme dále:

      FILELIST="";
      for dir in $DIRS; do
          FILELIST=$FILELIST`find $dir -type f`
      done

      Hmm tak to už mi fakt dává smysl, teda pokud mě náhodou nenapadne udělat
      FILELIST=`find $DIRS -type f`
      a když už o tom přemýšlím, tak proč ne rovnou
      FILELIST="$(find $(cat ./backup.dirs) -type f)"
      , že? Předávat taru parametry -vv abych ten výstup vzápětí poslal do /dev/null heh? A vůbec spoléhat na to, že bash pobere seznam souborů na příkazové řádce a nepoužít -T? Jako odstrašující příklad dobré, ale pak bych ten edukativní záměr zdůraznil tučným TAKHLE NE!!! na konci začátku a ještě bych to zdůraznil před a za každým uvedeným kouskem zdrojáku.

      • co sa tyka tych sed 24.07.2006 | 10:19
        Avatar blackhole_ventYl   Používateľ

        co sa tyka tych sed skriptov, mozem ti tuna verejne napisat priznanie, ze tie sed skripty som napisal viacmenej nahodou a sam sa cudujem, ze funguju ;-) a niektore ine casti boli riesene systemom takto to poznam, takto to zapisem a potom opatchujem ;-)

        Nie je to sice najoptimalnejsie vyriesene zalohovacie riesenie, ale pomoze ako rychly a pomerne efektivny zalohovaci nastroj tam, kde je zalohovanie nutne rozbehnut v kratkom case.

        ale diky za upozornenie :) konkretne sedove skripty nie su prave tym, co by som vedel najlepsie

        ---
        Cuchat s nadchou, to je ako sniffovat bez promiscu.

        --- Cuchat s nadchou, to je ako sniffovat bez promiscu.
        • kooperacia 22.08.2006 | 14:32
          noface   Návštevník
          vidis, niekedy sa oplati ist na to jednoduchsie.