воскресенье, 24 апреля 2016 г.

ZRescue: ZFS restore, часть II

Для восстановления резервных копий, полученных утилитой, описанной в предыдущей части, используется столь же простая утилита:

 #!/sbin/sh  
   
 #  
 # ZRescue - ZFS Backup/Restore  
 #  
 # ZFS archives uses for bare-metal restore  
 # and systems cloning.  
 #  
 # Archive names can be incremental, like that:  
 # [hostname].[pool|pool%dataset].n.zfs<.gz>, n=0,1,2...  
 # Note: Do not rename archive files! Filenames will  
 #    use to recovery purposes.  
 #  
 # Version 2.0 (C) 2009,2016 Y.Voinov  
 #  
 # If you not specify pool/dataset name in command line,  
 # it will be read from archive name.  
 #  
 #pragma ident "@(#)zfsrestore.sh  2.0  10/22/09 YV"  
 #  
   
 #############  
 # Variables #  
 #############  
   
 # Environment variables   
 PATH=/usr/local/bin:/sbin:/usr/bin:$PATH  
   
 # Copyright and version  
 PKG_NAME="ZRescue"  
 PKG_VERSION="2.0"  
 COPY="Copyright (C) 2009, Yuri Voinov"  
   
 # Archiver name  
 ARC_NAME="gzip"  
 # Archive file suffix  
 ARC_SUFFIX=".gz"  
 # GZip default compression level  
 COMP_LEVEL="9"  
 # Default archive extension  
 ext="zfs"  
   
 # OS utilities   
 BASENAME=`which basename`  
 CAT=`which cat`  
 CUT=`which cut`  
 DATE=`which date`  
 ECHO=`which echo`  
 EXPR=`which expr`  
 GETOPT=`which getopt`  
 FILE=`which file`  
 GREP=`which grep`  
 GZIP=`which gzip`  
 HOSTNAME=`which hostname`  
 ID=`which id`  
 PRINTF=`which printf`  
 SED=`which sed`  
 SSH=`which ssh`  
 UNAME=`which uname`  
 WHOAMI=`which whoami`  
 ZFS=`which zfs`  
   
 OS_VER=`$UNAME -r|$CUT -f2 -d"."`  
 OS_NAME=`$UNAME -s|$CUT -f1 -d" "`  
 OS_FULL=`$UNAME -sr`  
   
 # System name  
 system=`$HOSTNAME`  
   
 # Snapshots extension by default   
 # Correct it if these snapshots already exists in the system  
 SNAP_EXT="$system""_""$PKG_NAME""-""$PKG_VERSION""_snapshot"  
   
 ###############  
 # Subroutines #  
 ###############  
   
 check_os ()  
 {  
  # Check OS  
  $PRINTF "Checking OS... "  
  if [ "$OS_NAME" = "SunOS" -a "$OS_VER" -lt "10" ]; then  
  $ECHO "ERROR: Unsupported OS: $OS_FULL"  
  $ECHO "Exiting..."  
  exit 1  
  else  
  $ECHO "$OS_FULL"  
  fi  
 }  
   
 check_root ()  
 {  
  # Check if user root  
  $PRINTF "Checking super-user... "  
  if [ -f /usr/xpg4/bin/id ]; then  
  WHO=`/usr/xpg4/bin/id -n -u`  
  elif [ "`$ID | $CUT -f1 -d" "`" = "uid=0(root)" ]; then  
  WHO="root"  
  else  
  WHO=$WHOAMI  
  fi  
   
  if [ ! "$WHO" = "root" ]; then  
  $ECHO  
  $ECHO "ERROR: You must be super-user to run this script."  
  exit 1  
  fi  
  $ECHO "$WHO"  
 }  
   
 check_fs_exists ()  
 {  
  # Check filesystem exists  
  arg_fs=$1  
   
  ret=`$ZFS list -H -o name $arg_fs > /dev/null 2>&1; $ECHO $?`  
  if [ "$ret" != "0" ]; then  
  $ECHO "ERROR: ZFS pool/dataset $arg_fs does not exist."  
  $ECHO "    Please specify another ZFS."  
  $ECHO "Exiting..."  
  exit 1  
  fi  
 }  
   
 check_hostname ()  
 {  
  # Check if backup hostname is the same target hostname  
  arg_backup_hostname=$1  
  arg_remote_hostname=$2  
    
  if [ "$arg_backup_hostname" != "$arg_remote_hostname" ]; then  
  $ECHO "WARNING: Backup was taken from $arg_backup_hostname host,"  
  $ECHO "     target host is $arg_remote_hostname."  
  fi  
 }  
   
 copyright_and_version ()  
 {  
  # Print package and copyright info  
  $ECHO  
  $ECHO "$PKG_NAME $PKG_VERSION $COPY"  
  $ECHO  
 }  
   
 usage_note ()  
 {  
  copyright_and_version  
  $ECHO "Usage: `$BASENAME $0` [-v] [-n] [-r] [-f] [/mntpoint/archive|local fs] [pool|dataset] [host]"  
  $ECHO  
  $ECHO "Note: Compression will use if GZip installed,"  
  $ECHO "   both for local archives or remote streams transfer."  
  $ECHO "Beware: GZip must be installed in both nodes in case of remote backup."  
  exit 1  
 }  
   
 archive_type ()  
 {  
  # Check archive type using extension and header check  
  arg_file=$1  
   
  if [ ! -z "`$ECHO $arg_file | $GREP $ARC_SUFFIX$`" -a \  
    ! -z "`$FILE "$arg_file" | $GREP $ARC_NAME`" ]; then  
  $ECHO "$ARC_NAME"  
  elif [ ! -z "`$ECHO $arg_file | $GREP $ext$`" -a \  
     ! -z "`$FILE "$arg_file" | $GREP $ext`" ]; then  
  $ECHO "$ext"  
  else  
  $ECHO "unknown"  
  fi  
 }  
   
 archive_exists ()  
 {  
  # Check archive exist and it readable  
  arg_arc=$1  
   
  # First check archive exists and readable  
  if [ ! -f "$arg_arc" -a ! -r "$arg_arc" ]; then  
  $ECHO "ERROR: Archive $arg_arc does not exist"  
  $ECHO "    or you haven't permissions to read."  
  $ECHO "Exiting..."  
  exit 1  
  fi  
  # Second we'll check archive type  
  if [ "`archive_type $arg_arc`" = "$ARC_NAME" -a \  
    "`archive_type $arg_arc`" = " $ext" ]; then  
  $ECHO "ERROR: Archive $arg_arc it has wrong type."  
  $ECHO "Exiting..."  
  exit 1  
  fi   
 }  
   
 destroy_fs ()  
 {  
  # Destroy filesystem(s) recursively  
  arg_fs=$1  
   
  $ZFS destroy -r "$arg_fs" > /dev/null 2>&1  
   
  # Check exit code  
  if [ "`$ECHO $?`" != "0" ]; then  
  $ECHO "WARNING: Filesystem $arg_fs does not exists."  
  fi  
 }  
   
 create_snap ()  
 {  
  # Create snapshot recursively  
  arg_filesystem=$1  
   
  $ZFS snapshot -r "$arg_filesystem@$SNAP_EXT"  
 }  
   
 zfs_receive ()  
 {  
  # Receive filesystem(s) from archive  
  arg_fs=$1  # Archive or fs to receive  
  arg_filesys=$2 # Target fs  
  arg_remote_host=$3 # Remote host  
   
  # Verbose output flag set in interactive mode  
  if [ "$verbose" = "1" ]; then  
  verb="v"  
  fi  
   
  # When use compression, restore fs with archiver  
  if [ "$remote_mode" != "1" -a "$remote_file_archive" != "1" ]; then  
  if [ "$compress" = "1" -a "`archive_type $arg_fs`" = "$ARC_NAME" ]; then  
   # Send from local file with archiver  
   $GZIP -d -c $arg_fs | $ZFS receive -dF"$verb" "$arg_filesys"  
  elif [ "`archive_type $arg_file`" = "$ext" ]; then  
   # Send from local file without archiver  
   $ZFS receive -dF"$verb" "$arg_filesys" < "$arg_fs"  
  fi  
  elif [ "$remote_mode" = "1" -a "$remote_file_archive" != "1" ]; then  
  if [ "$compress" = "1" ]; then  
   # Send to remote from local fs with compression  
   create_snap $arg_fs  
   $ZFS send -R$verb $arg_fs@$SNAP_EXT|$GZIP -$COMP_LEVEL -|\  
   $SSH $arg_remote_host "/bin/gzip -c -d -|/sbin/zfs receive -dF$verb $arg_filesys"  
  else  
   # Send to remote from local fs without compression  
   create_snap $arg_fs  
   $ZFS send -R$verb $arg_fs@$SNAP_EXT|\  
   $SSH $arg_remote_host "/sbin/zfs receive -dF$verb $arg_filesys"  
  fi  
  # Destroy local and remote snapshots  
  $SSH $arg_remote_host "/sbin/zfs destroy -r `/sbin/zfs list -H -o name -t snapshot|\  
  /bin/grep $arg_filesys@$SNAP_EXT`> /dev/null 2>&1"  
  destroy_fs $arg_fs@$SNAP_EXT  
  elif [ "$remote_mode" = "1" -a "$remote_file_archive" = "1" -a "`archive_type $arg_fs`" = "$ARC_NAME" ]; then  
  check_hostname `$ECHO "$arg_fs"|$CUT -f1 -d"."` $arg_remote_host  
  if [ "$compress" = "1" ]; then  
   # Send to remote from local archive with compression  
   $CAT $arg_fs|$SSH $arg_remote_host "/bin/gzip -c -d -|/sbin/zfs receive -dF$verb $arg_filesys"  
  else  
   # Send to remote from local archive without compression  
   $CAT $arg_fs|$SSH $arg_remote_host "/sbin/zfs receive -dF$verb $arg_filesys"  
  fi  
  # Destroy remote snapshots  
  $SSH $arg_remote_host "/sbin/zfs destroy -r `/sbin/zfs list -H -o name -t snapshot|\  
  /bin/grep $arg_filesys@$SNAP_EXT`> /dev/null 2>&1"  
  fi  
 }  
   
 ##############  
 # Main block #  
 ##############  
   
 # Checking OS  
 check_os  
   
 # Checking root  
 check_root  
   
 # Check command-line arguments  
 if [ "x$*" = "x" ]; then  
  # If arguments list empty, show usage note  
  usage_note  
 else  
  arg_list=$*  
  # Parse command line  
  set -- `$GETOPT fFrRnNvVhH: $arg_list` || {  
  usage_note  
  }  
   
  # Read arguments  
  for i in $arg_list  
  do  
   case $i in  
   -f|-F) remote_file_archive="1";;  
   -r|-R) remote_mode="1";;  
   -n|-N) compress_off="1";;  
   -v|-V) verbose="1";;  
   -h|-H|\?) usage_note;;  
   *) shift  
     archive=$1  
     filesystem=$2;  
     remote_host=$3  
     break;;  
   esac  
   shift  
  done  
   
  # Remove trailing --  
  shift `$EXPR $OPTIND - 1`  
 fi  
   
 # Check filesystem exists  
 check_fs_exists $filesystem  
   
 $ECHO "*** BEGIN: ZFS restore for $filesystem at `$DATE`."  
   
 # Check archiver  
 if [ ! -f "$GZIP" -a ! -x "$GZIP" -o "$compress_off" = "1" ]; then  
  $ECHO "INFO: Compression will NOT be used. GZip not found or compression disabled."  
  compress="0"  
 elif [ -f "$GZIP" -a -x "$GZIP" -a "$compress_off" != "1" ]; then  
  $ECHO "INFO: Data will be compressed with gzip -$COMP_LEVEL."  
  compress="1"  
 fi  
   
 # Check archive file exists and correct type when non-remote operation  
 if [ "$remote_mode" != "1" -o "x$remote_file_archive" != "x" ]; then  
  # If filesystem not specified, let's get it from file name  
  if [ "x$filesystem" = "x" ]; then  
  # Set initial archive file name  
  # Replase slashes with % if dataset specified in non-remote mode  
  filesystem=`$ECHO "$archive" | $CUT -f2 -d"." | $SED -e 's/%/\//g' | $CUT -f1 -d"/"`  
  fi  
  archive_exists $archive  
 fi  
   
 # First destroy all snapshots recursively  
 destroy_fs "$filesystem@$SNAP_EXT"  
   
 # Restore ZFS pool/dataset  
 zfs_receive $archive $filesystem $remote_host  
   
 # Finally destroy all snapshots recursively  
 destroy_fs "$filesystem@$SNAP_EXT"  
   
 $ECHO "*** DONE: ZFS restore for $filesystem at `$DATE`."  

Для чего это было написано:

 Восстановление  архивов  или некомпрессированных стримов  
 или  с удаленного сервера выполняется   при   помощи  
 команды  zfsrestore  .  Хотя действия, необходимые для  
 восстановления, достаточно просты, тем не менее при их  
 выполнении в спешке возможны ошибки, с целью минимизации  
 которых и был написан данный пакет.  
   
 Команда  подобна  команде  zfsbackup,  и  также может  
 выполняться в локальном режиме:  
   
 # zfsrestore [-v] [-n ] /mntpt/arc [local_fs]  
   
 где  первым  аргументом  (обязательным) указывается имя  
 компрессированного или некомпрессированного (распакованного)  
 стрима (возможно с абсолютным или относительным путем),  
 второй аргумент указывает целевой пул/датасет (пул либо  
 файловая система должны существовать на момент выполнения  
 восстановления), в который будет выполняться восстановление.  
   
 Второй аргумент (ZFS-пул/датасет) не является обязательным.  
 Если  он  опущен,  имя целевого  пула  либо  датасета  
 извлекается из имени файла архива,   например,   для  
 архива   с   именем server5.data%work2.0.zfs.gz   имя  
 пула  будет  data соответственно.  
   
 или вы можете выполнять восстановление в удаленном режиме:  
   
 # zfsrestores [-v] [-r] [-f] /mntpt/arc|local_fs \  
                [remote_fs] [user@]host  
   
 Remote restore can be done from local ZFS filesystem (option  
 -r, backed up previously by zfsbackup or manual zfs send  
 command) or from compressed or uncompressed (option -n)  
 archive (o, created by zfsbackup (locally or remote).  
   
 Удаленное  восстановление может выполняться с локальной  
 файловой системы ZFS (опция -r, созданной ранее командой  
 zfsbackup или ручным выполнением команды zfs send) или из  
 компрессированного  либо некомпрессированного (опция -n)  
 архива (опция -f), созданного zfsbackup (локально либо  
 удаленно).  
   
 В  случае,  если  для  восстановления  указывается  
 компрессированный стрим, на целевой машине должен быть  
 установлен архиватор (GZip), в случае его недоступности  
 следует  распаковать  стрим  вручную  на другой машине  
 и выполнять восстановление уже с некомпрессированного стрима  
 (его расширение должно быть .zfs).  
   
 При необходимости выборочного восстановления рекомендуется  
 предварительно  создать  целевой  датасет,  в  который  
 и  выполнять  восстановление стрима. При восстановлении  
 bare-metal указыватся предварительно воссозданый ZFS-пул  
 с именем, совпадающим с именем архивированного пула либо  
 датасета.  
   
 Если  восстановление выполняется в совпадающую файловую  
 систему  с  имеющимися данными (непустую), производится  
 перезапись  существующих  данных  и  дозапись  данных,  
 отсутствующих в целевой файловой системе.  

Если     при     выполнении    команды   zfsrestore    точка
монтирования  /usr недоступна, выполнение команды невозможно
и необходимо выполнять

Ручное восстановление
---------------------

Ручное  восстановление  архивов  выполняется в случаях, если
по   какой-либо   причине   невозможно   выполнение  команды
zfsrestore    (система   в  single-user при корневой системе
на   UFS   в   случае  нахождения  точки  монтирования  /usr
на  недоступном  слайсе,  восстановление  корневого пула при
загрузке с внешнего носителя итп.).

Процедура  ручного  восстановления достаточно проста, однако
требует некоторого внимания администратора.

Если  целевой  ZFS-пул  отсутствует,  его  требуется создать
до  начала  восстановления  данных. Затем следует убедиться,
что  пул  исправен  командой  zpool  status  <целевой  пул>.
В  случае  восстановления  данных  на  произвольный  датасет
(с  целью выборочного восстановления данных) он также должен
быть создан до начала восстановления.

Пример восстановления целого ZFS-пула:

# zpool create data c0t0d0s6
# gzcat server5.data.0.zfs.gz | zfs receive -vdF data

После  подобной  операции  никаких  дополнительных  действий
не   требуется,  восстановление  на  самый  верхний  уровень
иерархии выполняется в одно действие.

Пример восстановления датасета:

# gzcat server5.data%work2.0.zfs.gz | zfs receive -vdF data
# zfs destroy -r data/work2@snapshot

При   восстановлении   датасета   целевой   пул,  в  котором
находилась   (находится)  восстанавливаемая  система,  также
должен   существовать   до   начала   восстановления.  После
восстановления   датасета   необходимо   рекурсивно  удалить
снапшот   верхнего   уровня,   восстановленный   из  архива,
поскольку  в  данном  случае  он остается после операции zfs
receive в наборе файловых систем.

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

# zfs receive -vdF data < server5.data%work2.0.zfs.gz

Вы  также  можете  выполнить  данную  процедуру  через сеть,
с использованием SSH Или любого другого подходящего сетевого
сервиса для передачи стримов.  

Обратите внимание - мы говорим о восстановлении некорневых (non-root) пулов. Восстановление root pool - чуть более сложная задача, требующая несколько большего количества телодвижений:

 Восстановление root pool  
 ------------------------  
   
 Процедура восстановления root pool  в случае, например,  
 полного отказа системного диска требует несколько большего  
 внимания администратора и должна выполняться вручную.  
   
 При замене поврежденного системного диска выполняется ряд  
 обязательных процедур, связанных с заменой дисков. Вместе  
 с тем, при наличии удаленной резервной копии корневого пула  
 процедура  восстановления  позволяет сравнительно быстро  
 привести систему в загружаемое состояние.  
   
 Прежде всего, для восстановления корневого пула необходима  
 его резервная копия - в виде архива (компрессированного или  
 нет) или сохраненная в виде файловой системы на резервном  
 сервере.  
   
 Последующие действия описаны в виде алгоритма ниже.  
   
 1. Необходимо загрузить восстанавливаемую систему с внешнего  
 носителя - DVD, CD, USB или сети:  
   
 ok boot cdrom  
   
 или  
   
 ok boot net  
   
 2.  На резервном сервере, содержащем бэкап (архив или  
 файловую  систему) необходимо сконфигурировать удаленный  
 доступ для R-утилит с восстанавливаемой машины и запустить  
 соответствующие сервисы:  
   
 backup_server# netservices open  
 backup_server# vi /.rhosts  
 backup_server# vi /etc/hosts_equiv  
   
 При наличии сконфигурированного IPF необходимо разрешить  
 доступ на сервер восстановления c восстанавливаемой машины.  
   
 3. При физическом отказе и замене системного диска новый  
 диск необходимо пометить и разбить на слайсы. Метка диска  
 должна иметь тип SMI.  
   
 4. На восстанавливаемой машине пересоздается корневой пул на  
 новом диске, например:  
   
 # zpool create -f -o failmode=continue -R /a \  
  -m legacy rpool c0t0d0s0  
   
 5. На новый диск устанавливается загрузчик:  
   
 # installboot -F zfs /usr/platform/`uname -i`/lib/fs/zfs/bootblk \  
                      /dev/rdsk/c0t0d0s0  
   
 или  
   
 # installgrub /boot/grub/stage1 /boot/grub/stage2 \  
                 /dev/rdsk/c0t0d0s0  
   
 6.  Далее  выполняется  прием  рекурсивного  снапшота  
 и восстановление содержимого пула с удаленного резервного  
 сервера:  
   
 # rsh backup_server zfs send backup_data/rpool|zfs receive -Fdv rpool  
   
 либо из компрессированного архива, находящегося на удаленном  
 резервном сервере:  
   
 # rsh backup_server gzip -c -d pegasus.rpool.0.zfs.gz| zfs receive -Fdv rpool  
   
 Можно  также  выполнить  восстановление  root  pool  
 непосредственно с локальной архивной копии, находящейся,  
 например, на USB-устройстве (неархивированный стрим, для  
 компрессированного архива требуется предварительно выполнить  
 декомпрессию ввиду возможной недоступности gzip в miniroot  
 загрузочного дистрибутива):  
   
 # cd /usbmount  
 # cat pegasus.rpool.0.zfs|zfs receive -dFv rpool  
   
 Операция восстановления содержимого пула может потребовать  
 некоторого времени.  
   
 7. Необходимо убедиться, что корневой пул был успешно  
 восстановлен в полном объеме командой zfs list.  
   
 8. Необходимо установить свойство bootfs корневого пула  
 в правильное (оригинальное) значение:  
   
 # zpool set bootfs=rpool/ROOT/s10x_u8wos_08a rpool  
   
 Пересоздание  dump и swap разделов не требуется, если  
 резервная  копия корневого пула создавалась при помощи  
 zfsbackup.  Рекурсивный снапшот корневого пула содержит  
 данные разделы и они будут восстановлены автоматически.  
   
 9. Перезагрузить систему с восстановленного пула:  
   
 # init 6   
   
 Замечание: Вместо пересоздания пула и его восстановления  
 можно  также  использовать  (начиная  с  версии 10/09)  
 функциональность  zflash  и  выполнить  восстановление  
 средствами JumpStart flash install с некомпрессированного  
 стрима (архива), созданного утилитой zfsbackup.  
   

Однако, ничего невозможного. :)

Что касается совместимости (а мне точно известно, что до сих пор эксплуатируются весьма старые версии Solaris):

 Пакет работает на Solaris 10 10/08 и выше и OpenSolaris  
 начиная с релиза 2008.11.  
   
 Пакет поддерживает создание архивов ZFS и их восстановление  
 начиная с Solaris 10 8/07 (поддерживаются только некорневые  
 пулы ZFS), начиная с релиза 10/08 поддерживаются также  
 корневые ZFS-пулы (ZFS root pools).  
   

Техническая поддержка

Автор не оказывает техническую поддержку ни на какой основе. Вы используете предоставленный код на свой собственный страх и риск.

Лицензия

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

Скачать

Вы можете скачать вышеописанные утилиты в виде скриптов плюс man-страницы к ним в формате man, плюс процитированные readme с краткими руководствами по применению здесь.

ZRescue: ZFS backup, часть I

Давным-давно, в далекой-предалекой галактике, в 2009м году, я начал писать о резервировании и восстановлении ZFS: здесь и здесь.

С тех пор утекло немало воды, сам Solaris поменял место жительства. ZFS отныне поддерживается FreeBSD. Появилось множество коммерческих, тяжелых и дорогих, решений по резервированию и восстановлению. И складывается впечатление, что это очень сложно, страшно дорого и ужасно больно.

На самом деле это не так. Дабы доказать это, вытащим для всеобщего обозрения и применения финальную версию того решения, на начало которого я сослался выше и которое началось в далеком 2009м году.

Итак, сперва утилита zfsbackup. Просто и со вкусом написанная на шелле.
 #!/sbin/sh  
   
 #  
 # ZRescue - ZFS Backup/Restore  
 #  
 # ZFS archives uses for bare-metal restore  
 # and systems cloning.  
 #  
 # Archive names will be incremental, like that:  
 # [hostname].[pool|pool%dataset].n.zfs<.gz>, n=0,1,2...  
 # Note: Do not rename archive files! Filenames will  
 #    use to recovery purposes.  
 #  
 # Version 2.0 (C) 2009,2016 Y.Voinov  
 #  
 #pragma ident "@(#)zfsbackup.sh  2.0  10/22/09 YV"  
 #  
   
 #############  
 # Variables #  
 #############  
   
 # Environment variables   
 PATH=/usr/local/bin:/sbin:/usr/bin:$PATH  
   
 # Copyright and version  
 PKG_NAME="ZRescue"  
 PKG_VERSION="2.0"  
 COPY="Copyright (C) 2009, Yuri Voinov"  
   
 # Archive file suffix  
 ARC_SUFFIX=".gz"  
 # GZip default compression level  
 COMP_LEVEL="9"  
 # Default archive extension  
 ext="zfs"  
 # Initial archive sequence  
 initial_arc_seq="0"  
   
 # OS utilities  
 BASENAME=`which basename`  
 CUT=`which cut`  
 DATE=`which date`  
 ECHO=`which echo`  
 EXPR=`which expr`  
 GETOPT=`which getopt`  
 GZIP=`which gzip`  
 HOSTNAME=`which hostname`  
 ID=`which id`  
 PRINTF=`which printf`  
 SED=`which sed`  
 SSH=`which ssh`  
 UNAME=`which uname`  
 WHOAMI=`which whoami`  
 ZFS=`which zfs`  
   
 OS_VER=`$UNAME -r|$CUT -f2 -d"."`  
 OS_NAME=`$UNAME -s|$CUT -f1 -d" "`  
 OS_FULL=`$UNAME -sr`  
   
 # System name  
 system=`$HOSTNAME`  
   
 # Snapshots extension by default   
 # Correct it if these snapshots already exists in the system  
 SNAP_EXT="$system""_""$PKG_NAME""-""$PKG_VERSION""_snapshot"  
   
 ###############  
 # Subroutines #  
 ###############  
   
 check_os ()  
 {  
  # Check OS  
  $PRINTF "Checking OS... "  
  if [ "$OS_NAME" = "SunOS" -a "$OS_VER" -lt "10" ]; then  
  $ECHO "ERROR: Unsupported OS: $OS_FULL"  
  $ECHO "Exiting..."  
  exit 1  
  else  
  $ECHO "$OS_FULL"  
  fi  
 }  
   
 check_root ()  
 {  
  # Check if user root  
  $PRINTF "Checking super-user... "  
  if [ -f /usr/xpg4/bin/id ]; then  
  WHO=`/usr/xpg4/bin/id -n -u`  
  elif [ "`$ID | $CUT -f1 -d" "`" = "uid=0(root)" ]; then  
  WHO="root"  
  else  
  WHO=$WHOAMI  
  fi  
   
  if [ ! "$WHO" = "root" ]; then  
  $ECHO  
  $ECHO "ERROR: You must be super-user to run this script."  
  exit 1  
  fi  
  $ECHO "$WHO"  
 }  
   
 archive_exists ()  
 {  
  # Check archive file exist  
  if [ "$compress" = "1" -a -f "$file.gz" ]; then  
   $ECHO "1"  
  elif [ "$compress" = "0" -a -f "$file" ]; then  
  $ECHO "1"  
  else  
  $ECHO "0"  
  fi  
 }  
   
 set_file ()  
 {  
  # Check archive name exists  
  # and create new name if needful  
  remote_host_arg=$1  
  attempt=0  
   
  while [ "`archive_exists`" = "1" ]; do  
  file=`$ECHO $file|$CUT -f2 -d"."`  
  if [ -z "$remote_host_arg" ]; then  
   # Localhost file name  
   file="$dest/$system.$file.$attempt.$ext"  
  else  
   # Remote file name  
   file="$dest/$remote_host_arg.$file.$attempt.$ext"  
  fi  
  if [ "`archive_exists`" != "1" ]; then  
   break  
  fi  
  attempt=`expr $attempt + 1`  
  done  
 }  
   
 check_fs_exists ()  
 {  
  # Check filesystem exists  
  arg_fs=$1  
   
  ret=`$ZFS list -H -o name $arg_fs > /dev/null 2>&1; $ECHO $?`  
  if [ "$ret" != "0" ]; then  
  $ECHO "ERROR: ZFS pool/dataset $arg_fs does not exist."  
  $ECHO "    Please specify another ZFS."  
  $ECHO "Exiting..."  
  exit 1  
  fi  
 }  
   
 check_dest_dir ()  
 {  
  # Check directory exist and it writable  
  arg_dest=$1  
   
  if [ ! -d "$arg_dest" or ! -w "$arg_dest" ]; then  
  $ECHO "ERROR: Directory $arg_dest does not exist"  
  $ECHO "    or you haven't permissions to write."  
  $ECHO "Exiting..."  
  exit 1  
  fi   
 }  
   
 copyright_and_version ()  
 {  
  # Print package and copyright info  
  $ECHO  
  $ECHO "$PKG_NAME $PKG_VERSION $COPY"  
  $ECHO  
 }  
   
 usage_note ()  
 {  
  copyright_and_version  
  $ECHO "Usage: `$BASENAME $0` [-v] [-n] [-r] [-f] [pool|dataset] [/mntpoint|local fs] [host]"  
  $ECHO  
  $ECHO "Note: Compression will use if GZip installed,"  
  $ECHO "   both for local archives or remote streams transfer."  
  $ECHO "Beware: GZip must be installed in both nodes in case of remote backup."  
  exit 1  
 }  
   
 destroy_fs ()  
 {  
  # Destroy filesystem(s) recursively  
  arg_fs=$1  
   
  $ZFS destroy -r $arg_fs > /dev/null 2>&1  
   
  # Check exit code  
  if [ "`$ECHO $?`" != "0" ]; then  
  $ECHO "WARNING: Filesystem $arg_fs does not exists."  
  fi  
 }  
   
 create_snap ()  
 {  
  # Create snapshot recursively  
  arg_filesys=$1  
   
  $ZFS snapshot -r "$arg_filesys@$SNAP_EXT"  
 }  
   
 zfs_send ()  
 {  
  # Send filesystem to the destination  
  arg_filesys=$1  
  arg_dest=$2  
  arg_host=$3  
  # Verbose output flag set  
  if [ "$verbose" = "1" ]; then  
  verb="v"  
  fi  
  # When use compression, send fs with archiver  
  if [ "$compress" = "1" ]; then  
  if [ "$remote_mode" = "1" -a "$remote_file_archive" != "1" ]; then  
   # Send from remote with compression  
   $SSH $arg_host "/sbin/zfs snapshot -r $arg_filesys@$SNAP_EXT &&\  
   /sbin/zfs send -R$verb $arg_filesys@$SNAP_EXT|/bin/gzip -c -$COMP_LEVEL"|\  
   $GZIP -c -d -|$ZFS receive -dF$verb $arg_dest  
  elif [ "$remote_mode" = "1" -a "$remote_file_archive" = "1" ]; then  
   # Send from remote to file with compression  
   $SSH $arg_host "/sbin/zfs snapshot -r $arg_filesys@$SNAP_EXT &&\  
   /sbin/zfs send -R$verb $arg_filesys@$SNAP_EXT|/bin/gzip -c -$COMP_LEVEL">$file$ARC_SUFFIX  
  else  
   # Send to local with compression  
   $ZFS send -R"$verb" "$arg_filesys@$SNAP_EXT"|$GZIP -c "-$COMP_LEVEL">"$file$ARC_SUFFIX"  
  fi  
  else  
  if [ "$remote_mode" = "1" -a "$remote_file_archive" != "1" ]; then  
   # Send from remote without compression  
   $SSH $arg_host "/sbin/zfs snapshot -r $arg_filesys@$SNAP_EXT &&\  
   /sbin/zfs send -R$verb $arg_filesys@$SNAP_EXT"|\  
   $ZFS receive -dF$verb $arg_dest  
  elif [ "$remote_mode" = "1" -a "$remote_file_archive" = "1" ]; then  
   # Send from remote to file without compression  
   $SSH $arg_host "/sbin/zfs snapshot -r $arg_filesys@$SNAP_EXT &&\  
   /sbin/zfs send -R$verb $arg_filesys@$SNAP_EXT">$file$ARC_SUFFIX  
  else  
   # Send to local without compression  
   $ZFS send -R"$verb" "$arg_filesys@$SNAP_EXT">"$file"  
  fi  
  fi  
  # In remote mode destroy target snapshot on local machine and on remote machine  
  if [ "$remote_mode" = "1" ]; then  
  destroy_fs "`/sbin/zfs list -H -o name -t snapshot|/bin/grep $arg_dest`"  
  $SSH $arg_host "/sbin/zfs destroy -r `/sbin/zfs list -H -o name -t snapshot|\  
  /bin/grep $arg_filesys`> /dev/null 2>&1"  
  fi  
 }  
   
 ##############  
 # Main block #  
 ##############  
   
 # Checking OS  
 check_os  
   
 # Checking root  
 check_root  
   
 # Check command-line arguments  
 if [ "x$*" = "x" ]; then  
  # If arguments list empty, show usage note  
  usage_note  
 else  
  arg_list=$*  
  # Parse command line  
  set -- `$GETOPT fFrRnNvVhH: $arg_list` || {  
  usage_note  
  }  
   
  # Read arguments  
  for i in $arg_list  
  do  
   case $i in  
   -f|-F) remote_file_archive="1";;  
   -r|-R) remote_mode="1";;  
   -n|-N) compress_off="1";;  
   -v|-V) verbose="1";;  
   -h|-H|\?) usage_note;;  
   *) shift  
     filesystem=$1  
     dest=$2;  
     remote_host=$3  
     break;;  
   esac  
   shift  
  done  
   
  # Remove trailing --  
  shift `$EXPR $OPTIND - 1`  
 fi  
   
 # Check filesystem exists  
 check_fs_exists $filesystem  
   
 $ECHO "*** BEGIN: ZFS backup for $filesystem at `$DATE`."  
   
 # Check archiver  
 if [ ! -f "$GZIP" -a ! -x "$GZIP" -o "$compress_off" = "1" ]; then  
  $ECHO "INFO: Compression will NOT be used. GZip not found or compression disabled."  
  compress="0"  
 elif [ -f "$GZIP" -a -x "$GZIP" -a "$compress_off" != "1" ]; then  
  $ECHO "INFO: Data will be compressed with gzip -$COMP_LEVEL."  
  compress="1"  
 fi  
   
 if [ "$remote_mode" != "1" ]; then  
  # Check destination directory when file archive operation  
  check_dest_dir $dest  
  # Set initial archive file name  
  # Replase slashes with % if dataset specified in non-remote mode  
  file="$dest/$system.`$ECHO $filesystem | $SED -e 's/\//%/g'`.$initial_arc_seq.$ext"  
  # Set file name if incremental naming using  
  # (if archive with the same name already exists)  
  set_file  
 else  
  # Replase slashes with % if dataset specified in non-remote mode  
  file="$dest/$remote_host.`$ECHO $filesystem | $SED -e 's/\//%/g'`.$initial_arc_seq.$ext"  
  set_file $remote_host  
 fi  
   
 # First destroy snapshots recursively if it exists  
 destroy_fs "$filesystem@$SNAP_EXT"  
   
 # Second create recursive snapshots  
 if [ "$remote_mode" != "1" ]; then  
  create_snap $filesystem  
 fi  
   
 # Third send all snapshots recursively to archive  
 zfs_send $filesystem $dest $remote_host  
   
 # Finally destroy all snapshots recursively  
 if [ "$remote_mode" != "1" ]; then  
  destroy_fs "$filesystem@$SNAP_EXT"  
 fi  
   
 $ECHO "*** DONE: ZFS backup for $filesystem at `$DATE`."  

Небольшая цитата из readme:

 Пакет предназначен для создания компрессированных резервных  
 копий ZFS-пулов (включая root pool) и отдельных датасетов  
 с целью последующего восстановления bare-metal, для целей  
 ординарного  резервирования  и  восстановления  и  для  
 клонирования систем (в том числе с целью создания физических  
 standby-систем).  
   
 Он  реализован  в  виде  двух программ, zfsbackup  и  
 zfsrestore,  могущих использоваться как интерактивно, так  
 и в режиме командной строки (в том числе из cron), как  
 на  автономных  системах,  так  и в составе комплексных  
 решений.  
   
 По умолчанию, и при наличии в системе архиватора  GZip,  
 архивы  файловых  систем  создаются компрессированными  
 с   максимальной  степенью  компрессии,  для  экономии  
 пространства хранения и уменьшения объема передачи данных.  
   
 Пакет может резервировать данные либо локально или на NFS (в  
 виде файлового архива, компрессированного или нет, целого  
 пула данных либо отдельного датасета), либо c удаленного  
 бэкап-сервера.  Восстановление  файловых  систем  также  
 выполняется либо из файлового архива, взятого с локальной  
 системы либо NFS, либо с удаленного сервера резервных копий.  
   
 Вы также можете использовать пакет для резерврирования  
 принятой файловой системы на сервере резервного копирования  
 далее, в файловый архив.  
   
 Также вы можете создавать бэкап удаленной файловой системы на  
 сервер резервного копирования непосредственно в архивный файл  
 на диске или ленте.  
   

Как этим пользоваться? Снова readme:

 Команда  zfsbackup  создает рекурсивный снапшот ZFS от  
 уровня иерархии файловой системы, заданной в виде аргумента  
 (интерактивно или в командной строке) и создает на его  
 основе компрессированный с максимальной степенью сжатия  
 архив  (стрим)  (если  находит в системе установленный  
 архиватор,  в  данной версии GZip) на локальной точке  
 монтирования или на NFS.  
   
 Архив  может  быть распакован (в случае необходимости,  
 например, использования архива ZFS root pool при выполнении  
 flash-установок) вручную с использованием gzip или gzcat.  
   
 В удаленном режиме (опция -r) снапшот посылается посредством  
 SSH  с заданного удаленного хоста на сервер резервного  
 копирования  в  заданную  файловую  систему  (пул).  
 Стрим,посылаемый   по  сети,  так  же  может  быть  
 компрессирован или нет, как и в случае локальной архивации.  
   
 Команда  zfsrestore  используется  при  восстановлении  
 архивов  или  удаленных  файловых  систем,   созданных  
 программой   zfsbackup  .   Она  может  выполнять  
 восстановление  как  пулов  целиком  (в  первоначальном  
 состоянии), так и отдельных датасетов, в первоначальные или  
 произвольно заданные файловые системы.  
   
 Команда   zfsrestore   распознает    вид   архива  
 (компрессированный  или  без  компрессии) в локальном  
 режиме, и, в соответствие с  видом  архива  использует  
 соответствующий  метод восстановления. В удаленном режиме  
 (опция   -r)   ранее  посланная  файловая  система  
 восстанавливается на удаленной машине.  
   
 Команды могут использоваться как на автономных системах  
 с периодическим сохранением резервных копий на внешних  
 носителяХ, так и в системах масштаба предприятия, при  
 использовании ленточных носителей или NFS.  
   
 Резервное копирование  
 ---------------------  
   
 Для резервного копирования ZFS-пулов и датасетов (из любого  
 уровня иерархии) используется команда zfsbackup.  
   
 Команда может выполняться в двух режимах:  
   
 1. Резервирование в точку монтирования (локальную или NFS).  
 2. Резервирование с удаленной машины на сервер резервного  
 копирования.  
   
 Команда  автоматически  компрессирует  данные (стрим)  
 в случае обнаружения утилиты GZip с мааксимальной степенью  
 компрессии (gzip -9).  
   
 Команда   выполняет  создание  полного  рекурсивного  
 снапшота  ZFS-пула   либо  датасета,  с  последующей  
 автоматической компрессией  и  записью  файла в целевую  
 точку  монтирования  (локальную  либо NFS), в которую  
 суперпользоваль имеет право записи либо посылает сжатый  
 стрим с удаленной машины на резервный сервер в заданную  
 файловую систему или файловый архив через SSH.  
   
 По умолчанию имена архивов образуются по инкрементальному  
 принципу в следующем формате:  
   
 [hostname].[pool|pool%dataset].n.zfs<.gz>, n=0,1,2...  
   
 где  hostname  -  имя  хоста,  на котором выполняется  
 архивирование, pool|dataset - имя ZFS-пула или датасета, при  
 этом для датасетов, с целью корректного формирования имен  
 файлов,  осуществляется  замена  символов  "/" на "%".  
 Некомпрессированные  ZFS-стримы  имеют  расширение .zfs,  
 компрессированные - .gz .  
   
 Первый архив с одинаковым расширением в целевом каталоге  
 будет иметь номер 0, последующие, соответственно, 1,2,3  
 и так далее. Нумерация архивов с разными расширениям (.gz  
 или .zfs) будет выполняться независимо, от текущего номера  
 каждого типа архивов.  
   
 ВНИМАНИЕ! Не переименовывайте архивные файлы, поскольку  
 имена  архивов  используются  командой zfsrestore  для  
 восстановления в случае, если не задано имя целевой файловой  
 системы.  
   
 Выполнение  резервного  копирования производится вызовом  
 команды в локальном режиме:  
   
 # zfsbackup [-v] [-n] local_fs /mntpt  
   
 или в удаленном режиме:  
   
 # zfsbackup [-v] [-n] -r [-f] remote_fs /mntpt|local_fs \  
                     [user@][host]  
   
 В обеих случаях аргументы являются обязательными. Первым  
 аргументом  является локальный или удаленный архивируемый  
 ZFS-пул  либо датасет, вторым - локальная директория или  
 точка монтирования NFS с правами записи либо локальная  
 файловая  система  для  приема стрима, третий аргумент  
 в удаленном режиме представляет  имя  машины с которой  
 выполняется резервное копирование пула/датасета.  
   
 Команда выполняет  протоколирование  начала и окончания  
 резервного  копирования,  а  также  (в verbose режиме)  
 протоколирует  работу  команды  zfs  send. Все ошибки,  
 возникающие  в  процессе  резервного копирования, также  
 направляются  в  STDOUT  и  могут  быть перенаправлены  
 в журнальный файл.  
   
 Возможно также включение вызова команды в cron, как показано  
 на примере:  
   
 # Automated data backup job with zfs  
 # Running weekly at 00:00 Saturday  
 0 0 * * 6 [ -x /usr/local/bin/zfsbackup ] && \  
 rm -f /backup/*.data.* > /dev/null 2>&1; \  
 /usr/local/bin/zfsbackup data /backup >> /var/log/backup.log  
   
 # Automated system backup job with zfs  
 # Running weekly at 01:00 Saturday  
 0 1 * * 6 [ -x /usr/local/bin/zfsbackup ] && \  
 rm -f /backup/*.rpool.* > /dev/null 2>&1; \  
 /usr/local/bin/zfsbackup -r rpool root@backup backup \  
 >> /var/log/backup.log  
   

Как видите, никаких сложностей, никаких гуёв, никаких чудес. Простой и обыкновенный UNIX-way. Только штатные средства и никаких лицензий за тысячи долларов. Просто немного работы системного администратора. Ну и сервер резервного копирования, разумеется, который штатно строится из обычной машины Solaris с достаточным объемом СХД.

О восстановлении из созданных бэкапов мы поговорим в следующей статье.

Squid: Prefork для хелперов

Обычно стандартные умолчания предзапуска (prefork) хелперов Squid достаточно низкие либо вообще равны нулю:

 #Default:  
 # sslcrtd_children 32 startup=5 idle=1  
   
 #Default:  
 # sslcrtvalidator_children 32 startup=5 idle=1 concurrency=1  
   
 #Default:  
 # url_rewrite_children 20 startup=0 idle=1 concurrency=0  
   
 #Default:  
 # store_id_children 20 startup=0 idle=1 concurrency=0  
   

При первоначальном запуске стартует минимальное число процессов, и, после установления соединения с клиентами, производится последующий запуск хелперов по мере необходимости (on demand).

Плюс такого подхода в том, что первоначально расходуется весьма мало памяти (однако в дальнейшем потребление памяти резко возрастает).

Однако, последующий запуск необходимых хелперов (это прежде всего касается sslcrtd, url_rewrite и store_id) вызывает достаточно длительные задержки в отклике, на уровне клиента зачастую наблюдаются задержки реакции прокси от 5 до 30 секунд (!). Это время зависит от особенностей используемой ОС, наличия/отсутствия виртуализации, настроек ОС, степени загруженности сервера, и многих других факторов - таких, например, как быстродействие подсистемы ввода-вывода и даже количества контроллеров IO.

Кроме того, регулярно выполняемая ротация логов (даже без выполнения собственно ротации, при использовании внешних средств и при установке параметра logfile_rotate 0) останавливает процессы хелперов до значения idle, в каждом цикле, и, после ее завершения, запуск начинается заново (в текущих версиях Squid не существует штатной возможности остановки дочерних процессов в случае длительного бездействия, только средства ротации).

Таким образом, дабы ускорить реакцию прокси на изменения внешней нагрузки сразу после запуска и в дальнейшем, есть смысл повысить стартовые значения количества запускаемых хелперов и величины idle (в зависимости от характера нагрузки).

Например, некрупный прокси бизнес-центра с числом клиентов 100-200 рабочих станций должен иметь (внимание, это хорошие начальные значения, не следует их воспринимать как окончательное ультимативное решение!) измененные значения процессных параметров используемых хелперов примерно следующего вида:

 sslcrtd_children 32 startup=10 idle=5  
   
 url_rewrite_children 64 startup=4 idle=4 concurrency=2  
 store_id_children 32 startup=4 idle=4 concurrency=4  
   

Эти примеры приведены для Squid 3.5.x.

Как я сказал выше, это весьма приблизительные рекомендации, в вашем случае значения могут быть совсем иными, вы должны их определить сами по данным мониторинга ваших собственных прокси. Кстати, после изменения параметров и перезапуска Squid также требуется продолжать мониторинг для возможной последующей корректировки параметров и/или дальнейшего тюнинга.

Я лишь рекомендую не превышать дефолтные максимумы, поскольку это не рекомендуется разработчиками.

понедельник, 18 апреля 2016 г.

ОАО "Фриктелеком"

Я просто оставлю это здесь:


PS. Я еще мог бы понять блокировку imgur.com - там можно запостить картинки некрасивые. Но адоб-то чем не угодил? Там реклама ИГ (запрещено в России)?
PPS. Через тор открывается все - и адоб и imgur.
PPPS. В других поддиапазонах ОАО "Фриктелеком" адоб открывается.