Появилось немного времени и я решил переписать скрипт для автоматизации задачи бэкапа данных. Раньше скрипт неизбежно приходилось дублировать, если каталоги назначения различались, для локального же бэкапа приходилось немного модифицировать сценарий. Теперь этого делать не нужно, один универсальный скрипт способен проделать несложные операции за вас, нужно лишь задать необходимые параметры в файле настроек.

Итак, ZLU (ZeroLab Unisync):

#!/bin/bash

# --------------------------------------------
# ZLU (ZeroLab Unisync):
# remote & local automated backup script
# Written by ZeroChaos
# Version 1.5, 2012-11-20
# Site: https://zerolab.net/
# --------------------------------------------
# NOTE: Exclude spaces in the variable FOLDER!
# --------------------------------------------

# -------------------------------------------- begin of [Configurable settings]
# Use SSH for remote sync
export RSYNC_RSH="ssh -c chacha20-poly1305@openssh.com -o Compression=no -x"
#export RSYNC_RSH="ssh -c chacha20-poly1305@openssh.com -o Compression=no -x -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null"

# Snapshot directory
SDIR="versioning"
SNAPSHOT_dir="$SDIR/`date +'%Y-%m-%d.%Hh%Mm'`"

# Take the list from the same folder where the script is called
DIR=$(dirname "$0")
LISTFOLDERS="$DIR/backup.txt"
EXCLUSIONSDIR="$DIR"
# -------------------------------------------- end of [Configurable settings]

# -------------------------------------------- begin of [Do not touch this section]
# Backup DB
#$DIR/backup-db.sh

# If no arguments
if [ $# = 0 ]; then
	SNAPSHOT_GLOBAL="disable"
	i=0
	while read LINE; do
		if [ -n "$LINE" ]; then
			IFS=";"
			set -- $LINE
			FOLDER="$1"
			DEST_point="$2"
			LISTEXCLUSIONS="$EXCLUSIONSDIR/$3"
			SNAPSHOT="$4"
			SDAYS="$5"
			REMOTEHOST="${6}@${7}"
			FOLDERNAME="`echo "$FOLDER" | awk -F/ '{ print $NF }'`"
			# Options for rsync + array of commands to autorotation
			if [ $SNAPSHOT = "enable" ]; then
				SNAPSHOT_GLOBAL="enable"
				if [ "$6" != "" ] && [ "$7" != "" ]; then
					OPTIONS="-Sazq --bwlimit=3840 --delete --exclude-from=$LISTEXCLUSIONS --backup --backup-dir=${DEST_point}${SNAPSHOT_dir}/$FOLDERNAME"
					SAR[$i]="ssh $REMOTEHOST \"find ${DEST_point}${SDIR}/ -type d -mtime +$SDAYS -exec rm -r {} \;\" >/dev/null 2>&1"
					i=$(($i+1))
				else
					OPTIONS="-Saq --delete --exclude-from=$LISTEXCLUSIONS --backup --backup-dir=${DEST_point}${SNAPSHOT_dir}/$FOLDERNAME"
					SAR[$i]="find \"${DEST_point}${SDIR}/\" -type d -mtime +$SDAYS -exec rm -r {} \; >/dev/null 2>&1"
					i=$(($i+1))
				fi
			else
				if [ "$6" != "" ] && [ "$7" != "" ]; then
					OPTIONS="-Sazq --bwlimit=3840 --delete --exclude-from=$LISTEXCLUSIONS"
				else
					OPTIONS="-Saq --delete --exclude-from=$LISTEXCLUSIONS"
				fi
			fi
			# Sync
			if [ "$6" != "" ] && [ "$7" != "" ]; then
				eval rsync $OPTIONS "${FOLDER}/" $REMOTEHOST:"${DEST_point}${FOLDERNAME}/"
			else
				eval rsync $OPTIONS "${FOLDER}/" "${DEST_point}${FOLDERNAME}/"
			fi
		fi
	done < $LISTFOLDERS
	# Snapshot auto-rotate
	if [ $SNAPSHOT_GLOBAL = "enable" ]; then
		for ((a=0; a<${#SAR[*]}; a++)); do
			eval "${SAR[$a]}"
		done
	fi
fi

# Generate public/private rsa key pair
ssh_keygen() {
	ssh-keygen -t rsa -b 4096
}

# Copy your public key on a remote machine
ssh_copy_id() {
	# Function for remote host choice
	PS3='Select the remote host: '
	rhchoice() {
		eval set $RHARR
		select RH; {
			REMOTEHOST=$RH
			break
		}
	}

	# Read the list of remote hosts
	while read LINE; do
		IFS=";"
		set -- $LINE
		if [ "$6" != "" ] && [ "$7" != "" ]; then
			RH="${6}@${7}"
			RHARR="$RHARR $RH"
		fi
	done < $LISTFOLDERS

	# Remote host choice
	if [ "$RHARR" != "" ]; then
		rhchoice $RHARR
	else
		echo "No remote hosts, please configure the settings file: $LISTFOLDERS"
	fi

    ID_FILE="${HOME}/.ssh/id_rsa.pub"

	if [ x$SSH_AUTH_SOCK != x ] && ssh-add -L >/dev/null 2>&1; then
		GET_ID="$GET_ID ssh-add -L"
	fi

	if [ -z "`eval $GET_ID`" ] && [ -r "${ID_FILE}" ]; then
		GET_ID="cat ${ID_FILE}"
	fi

	if [ -z "`eval $GET_ID`" ]; then
		echo "$0: ERROR: No identities found" >&2
		exit 1
	fi

	if [ "$RHARR" != "" ]; then
		{ eval "$GET_ID" ; } | ssh ${REMOTEHOST%:} "umask 077; test -d .ssh || mkdir .ssh ; cat >> .ssh/authorized_keys" || exit 1
	fi
}

ME=$(basename "$0")
print_help() {
	echo
	echo "Usage: $ME [OPTION...]"
	echo
	echo "Options:"
	echo "         -g     Generate public/private rsa key pair:"
	echo "                ssh-keygen -t rsa -b 4096"
	echo "         -c     Copy your public key on a remote machine:"
	echo "                ssh-copy-id user@host"
	echo "         -h     Help."
	echo
	echo "Example: $ME -gc"
	echo "Use both options, generate & copy."
	echo
}

while getopts ":gch" opt;
do
	case $opt in
		g) ssh_keygen;
			;;
		c) ssh_copy_id;
			;;
		h) print_help;
			;;
		*) echo "Wrong option";
			echo "For help, use: $ME -h";
			exit 1
			;;
		esac
done
# -------------------------------------------- end of [Do not touch this section]

Файл настроек принял следующий вид:

backup.txt:

/home/zerochaos/www/zerolab.net;/home/zerochaos/www/;exclude.txt;enable;15;zerochaos;backup.zerolab.net
/Users/zerochaos/data;/Volumes/BACKUP/;exclude.txt;disable;30
# [source_folder][destination_folder][exclusions_list]# [source_folder][destination_folder][exclusions_list][versioning_trigger][versioning_days][user][host] -- [do not delete this string]

Как видите, в одном файле настроек можно задать параметры как для удаленной, так и для локальной синхронизации. Разберем параметры каждого столбца, что за что отвечает:

1) [source_folder]: каталог, резервную копию которого мы делаем

2) [destination_folder]: каталог, куда мы сохраняем резервную копию

3) [exclusions_list]: список с масками исключений (формат смотрите в мане rsync)

4) [snapshot_trigger]: включаем или выключаем создание снапшотов

5) [snapshot_days]: указываем количество дней для авторотации снапшотов

6) [user]: имя пользователя удаленного хоста, на который производится резевное копирование

7) [host]: имя или IP этого хоста

Разделитель: «;»

6 и 7 параметры следует указывать только в том случае, если мы производим резервное копирование на удаленную машину. Заполняем список заданий для удаленного и локального бэкапа в одном файле, скрипт выполнит цикл резервного копирования. Каждое задание на новой строке, последнюю строку с комментариями не удаляем.

Пример exclude.txt:

temp/summ/checks/*
temp/sess/*
temp/tmp/*
template_cache/*
cache/*
logs/*

Напомню про версионирование. Скрипт позволяет опционально вести версионирование, которое содержит измененные, либо удаленные файлы. Тем самым, появляется возможность откатиться на старые версии файлов, случайно удаленных, к примеру, либо ошибочно измененных. При этом сохраняется полная изначальная структура каталогов, что позволяет легко найти требуемый файл. Версии хранятся в каталогах со следующей маской в имени: ‘год-месяц-день.XXчXXм’. Есть возможность авторотации, выставляете количество дней, отвечающее вашим требованиям, и имеете возможность спокойно восстановить рассортированные данные за эти дни.

Как и ранее, имеется возможность генерации ключа для авторизации по SSH, а так же его копирования на backup-сервер. Для авторизации с backup-сервером воспользуемся беспарольным вариантом, т.е. авторизацией по ключу. Для этого сгенерируем пару public/private rsa key. Выполним скрипт с ключом -g:

./backup.sh -g

Отвечаем на заданные вопросы и переходим к копированию открытого ключа на удаленный сервер. Для этого выполним скрипт с ключом -c:

./backup.sh -c

Будет выведен список удаленных хостов. Выбираем.

Вводим пароль пользователя, под которым будет происходить дальнейшая авторизация по ключу во время резервного копирования. Две команды выше можно объединить в одну, тогда операции будут выполнены последовательно:

./backup.sh -gc

Остается добавить скрипт в cron:

#minute hour    mday    month   wday    command
0       1       *       *       *       bash /scripts/backup.sh

Скрипт будет работать в любой операционной системе, где возможна установка rsync client и имеется командный интерпретатор.