UMGUM.COM (лучше) 

Rsync зеркалирование ( Зеркалирование данных с помощью утилиты Rsync. )

26 марта 2010  (обновлено 31 января 2015)

OS: Linux Debian Lenny/Squeeze.
Application: Rsync.

Итак, мы уже имеем настроенный web-сервер с функциональностью нас удовлетворяющей. Озаботимся элементом надёжности. Применим "убойный" в своей простоте вариант с полным "зеркалированием" сервисной составляющей.

Применим для этого утилиту "rsync". Её алгоритм построен таким образом, что он отслеживает изменения в файловой системе и копируются только необходимые её части. Причём при использовании базового алгоритма для выявления изменений нет необходимости сравнивать эти файловые системы или её отдельные элементы. И работает это довольно быстро.

Реализация "rsync" подразумевает возможность как простого копирования, так и "проталкивающего" ("push") копирования по событию с использованием отслеживающего изменения сервера "rsyncd". Второй вариант работы предполагает отслеживания сервисом "rsync" изменений в первичной файловой системе и отсылку указаний клиенту "rsync" произвести копирование изменений во вторичную файловую систему. Вариант с "проталкивающей" методой привлекателен тем, что мы сможем держать нашу вторичную файловую систему в достаточно актуальном состоянии по отношению к первичной; но мы пока остановимся на первом варианте, а актуализацию сделаем более предсказуемой, с запуском "rsync" с помощью утилиты "cron".

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

Наш подход к "зеркалированию" заключается в том, чтобы копировать все данные пользователей (не исключая и системных, что приведет к тому, что реквизиты доступа на первичном и вторичных серверах будут полностью совпадать) с сохранением разрешений и владельцев объектов. Но для сохранения данных о владельце объектов нужно их иметь этих пользователей и их конфигурации на вторичном сервере так, как и на первичном. Вручную создавать или корректировать параметры пользователей на вторичном сервере после применения их на первичном, да еще и поспевать до процедуры "зеркалирования" нереально. Проще всего (проще не значит лучше, но для начала - сойдет), "зеркалировать" файлы описания пользователей с первичного на вторичный сервер до копирования файлов данных этих пользователей. Разумеется, такой "финт" в чистом виде возможен только в том случае, если системное и прикладное обеспечение имеет идентичные конфигурации, и мы будем придерживаться этого правила в дальнейшем при изменении этих конфигураций.

В связи с принятием нами в исполнение метода прямого копирования файлов описания пользователей возникает нюанс, очень важный для осознания и контроля за соблюдением его условий - сочетания login/UID и group/GID на первичном и вторичном серверах для системных и прикладных программ необходимо синхронизировать до настройки "зеркалирования", установки прикладного программного обеспечения и переноса данных.

Суть в том, что перенос файлов описания пользователей необходимо осуществить ещё на "свеже-поднятый" в самой минимальной конфигурации вторичный сервер до установки какого бы то ни было прикладного обеспечения. Тем самым в "чистой" системе мы обретём заготовленных заранее пользователей с требуемыми нам UIN-ами и GID-ами, которые будут элементарно использованы, как заготовленные заранее, при инсталляции программного обеспечения на вторичном сервере в дальнейшем. Разумеется, в обязательном порядке, нужно сверить, совпадают ли UID-ы и GID-ы уже имеющихся до этого пользователей с применяемыми нами; хотя, в свежеустановленных операционных системах одного дистрибутива так оно обычно и есть.

Первым делом сверим UID и GID системных пользователей первичного и вторичного пользователей в файлах "/etc/passwd" и "/etc/group". Если это так, то продолжаем работу далее, а если нет, то случилось печальное и нужно искать его истоки.


Инсталлируем rsync на первичном сервере:

# aptitude install rsync

Разрешим стартовому скрипту "/etc/init.d/rsync" запускать сервис rsyncd и зададим параметры запуска путём следующих директив в файле "/etc/default/rsync":

....
RSYNC_ENABLE=true
....
RSYNC_CONFIG_FILE=/etc/rsyncd.conf
....
RSYNC_OPTS=' --ipv4 --port=8730'

В конфигурационном файле выше мы привязали сервис rsyncd к определённому протоколу и прослушиваемому порту.

Сразу берем себе на заметку, что запускать rsyncd придётся от имени суперпользователя потому, что "зеркалироваться" будут данные, доступ к которым имеет только он. Так что оставим потуги загнать сервис в изолированное окружение или прижать его в доступе, сосредоточив усилия на ограничение доступа к сервису всех непричастных.

Создаем конфигурационный файл сервиса rsyncd в месте, положенном ему сборщиками пакета:

# touch /etc/rsyncd.conf

Приведем конфигурационный файл к следующей базовой конфигурации:

log file = /var/log/rsyncd
pid file=/var/run/rsyncd.pid

# Указываем сервису с каких IP разрешено принимать подключения (считаю, что в закрытом окружении для передачи данных строго между двумя серверами это достаточная защита от компрометации данных; в случае передачи данных в изолированном VLAN даже не вижу необходимости в шифровании трафика)
hosts allow = ip_host_secondary

# Указываем серверу не открывать более одного соединения с клиентом одномоментно (а зачем больше при единственном клиенте?)
max connections = 1

# Указываем не сжимать трафик для любых объектов, при прямой связи между серверами по 100/1000Mb Ethernet "линку" это бессмысленно
dont compress = *

# Указываем имя и группу пользователя, от имени которого rsync будет обращаться к запрашиваемым объектам (доступ к некоторым объектам возможен только от имени суперпользователя, так что это вынужденная мера)
uid = root
gid = root

# Разрешаем доступ исключительно для чтения данных с первичного сервера
read only = yes

# Разрешаем выдавать список модулей
list = yes

use chroot = no
lock file = /var/lock/rsyncd
timeout = 600

# Описываем модули, эмулирущие корни файловой системы запрашиваемых объектов
[etc]
    path = /etc
    # Вначале закрываем доступ ко всем объектам в описываемом модуле
    exclude = *
    # Далее открываем доступ к перечисленным через пробел объектам (можно применить шаблон)
    include = passwd shadow group gshadow
    transfer logging = yes
    log format = %t: host %h (%a) %o %f (%l bytes). Total %b bytes.
[apache2]
    path = /etc/apache2
    transfer logging = yes
    log format = %t: host %h (%a) %o %f (%l bytes). Total %b bytes.
[nginx]
    path = /etc/nginx
    transfer logging = yes
    log format = %t: host %h (%a) %o %f (%l bytes). Total %b bytes.
[suphp]
    path = /etc/suphp
    transfer logging = yes
    log format = %t: host %h (%a) %o %f (%l bytes). Total %b bytes.
[mysql]
    path = /etc/mysql
    transfer logging = yes
    log format = %t: host %h (%a) %o %f (%l bytes). Total %b bytes.
[www]
    path = /var/www
[db-mysql]
    path = /var/lib/mysql
[backups]
    path = /var/backups/www

Перезапускаем rsyncd:

# /etc/init.d/rsync restart

Проверяем, то ли и там ли прослушивает наш сервис:

# netstat -apn | grep tcp | grep rsync

tcp  0  0 *:8730  0.0.0.0:*  LISTEN  2702/rsync

На вторичном сервере обеспечим возможность синхронизации с данными первичного сервера.

Инсталлируем rsync на вторичном сервере:

# aptitude install rsync

Просмотреть список поддерживаемых первичным сервером модулей со вторичного сервера, которому разрешен доступ можно следующим образом:

# rsync --port=8730 ip_host_primary::

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

# rsync://[user@]ip_host_primary[:port][/object]

Вывод для запроса без точного указания объекта обращения даст нам список поддерживаемых сервисом "модулей" (тех, что мы описали в конфигурационном файле сервиса rsyncd) - что то вроде следующего:

etc apache2 nginx www

Более точный запрос с указанием объекта даст содержимое описанное запрашиваемым модулем (мы помним, что из всей директории "/etc" доступ разрешён только к четырём файлам реквизитов пользователей):

# rsync --port=8730 ip_host_primary::etc

drwxr-xr-x  4096 19:37:19 .
-rw-r--r--  1542 03:37:53 group
-rw-r-----  1326 03:37:53 gshadow
-rw-r--r--  2230 03:37:53 passwd
-rw-r-----  1768 03:37:53 shadow

Применение "/" после имени директории в описании объекта "зеркалирования" в клиентском запросе указывает "rsync" оперировать только содержимым директории, а отсутствие этого символа делает директорию, как и её содержимое, объектом проводимой операции; иначе говоря, в отсутствии "/" копируется, как сама директория, так и её содержимое, а в присутствии "/" - только содержимое.

Запрос (в "читабельном" виде) доставит нам в директорию "/tmp" изменения объекта с сохранением исходных разрешений, владельца, группы и время модификации файла:

# rsync --perms --owner --group --times rsync://ip_host_primary:8730/etc/passwd /tmp

Если, после вышеприведённой операции, мы получили файл "/etc/passwd" во временную директорию с исходными правами доступа, то настройка сервера в общих чертах завершена. Приступим к конфигурированию клиентской составляющей.

"Зеркалирование" будем осуществлять единым скриптом, поэтапно:

учитывая то, что на вторичном сервере в режиме ожидания не должны быть запущены web-сервисы, останавливаем Apache, Nginx и MySQL;
обновим конфигурацию пользователей сервера;
обновим конфигурационные файлы серверов web-сервисов;
обновим файлы данных web-сервисов.

Итак, создадим себе местечко для скриптов и сам скрипт:

# mkdir /usr/local/etc/web
# touch /usr/local/etc/web/mirroring.sh
# chown -R root:root /usr/local/etc/web
# chmod -R o-rwx /usr/local/etc/web
# chmod -R g-w /usr/local/etc/web
# chmod ug+x /usr/local/etc/web/mirroring.sh

Наполним наш скрипт следующим:

#!/bin/bash

# Укажем IP-адрес, который используется для принятия запросов к web-сервисам; адрес этот перемещаем и будет присутствовать только на сервере, обслуживающем клиентов - то есть, может служить признаком того, является ли система, на которой отрабатывает скрипт, в данный момент недоступной для принятия изменений
ACCESSIP=ip_virtual_access

# Укажем IP-адрес первичного сервера
PRIMARYIP=ip_host_primary

# Проверяем статус сервера и осуществляем прерывание операции "зеркалирования" если он является первичным
if [ "`ifconfig | grep --count --ignore-case ${ACCESSIP}`" -ne "0" ]
then
  echo "Несущий сервер находится в статусе первичного и зеркалирование не может быть осуществлено."
  exit 0
fi

# Останавливаем Apache2, если он ранее не был остановлен
/etc/init.d/apache2 stop

# Останавливаем Nginx, если он ранее не был остановлен
/etc/init.d/nginx stop

# Останавливаем MySQL, если он ранее не был остановлен
/etc/init.d/mysql stop

# Следующей командой, рекурсивно проходя каталоги с сохранением всех оригинальных разрешений и времени доступа с полной трансляцией оригинальных UID и GID в новую систему (без попыток скорректировать), проверкой изменений не по времени, а по контрольной сумме (медленнее, но увереннее), без удаления отсутствующих файлов (только обновление существующих), получаем разрешённое содержимое директории "/etc/" с первичного сервера на вторичный
#
rsync --ipv4 --recursive --numeric-ids --perms --xattrs --acls --owner --group --times --checksum rsync://${PRIMARYIP}:8730/etc/ /etc/

# Задерживаем дыхание и даем системе время принять содеянное
sleep 5

# Теперь производим более полное "зеркалирование" с удалением на вторичном сервере отсутствующих на первичном и копированием, в оригинальном виде (без попыток корректировки), символических и прямых ссылок
#
rsync --ipv4 --recursive --delete-during --hard-links --links --numeric-ids --perms --xattrs --acls --executability --owner --group --times --checksum rsync://${PRIMARYIP}:8730/apache2/ /etc/apache2/

rsync --ipv4 --recursive --delete-during --hard-links --links --numeric-ids --perms --xattrs --acls --executability --owner --group --times --checksum rsync://${PRIMARYIP}:8730/nginx/ /etc/nginx/

rsync --ipv4 --recursive --delete-during --hard-links --links --numeric-ids --perms --xattrs --acls --executability --owner --group --times --checksum rsync://${PRIMARYIP}:8730/suphp/ /etc/suphp/

rsync --ipv4 --recursive --delete-during --hard-links --links --numeric-ids --perms --xattrs --acls --executability --owner --group --times --checksum rsync://${PRIMARYIP}:8730/mysql/ /etc/mysql/

# "Зеркалируем" данные web-сервисов с использованием оригинального алгоритма rsync без проверки контрольной суммы (так гораздо быстрее)
#
rsync --ipv4 --ignore-errors --recursive --sparse --delete-during --hard-links --links --numeric-ids --perms --xattrs --acls --executability --owner --group --times rsync://${PRIMARYIP}:8730/www/ /var/www/

# "Зеркалируем" данные MySQL сервера
#
rsync --ipv4 --ignore-errors --recursive --sparse --delete-during --hard-links --links --numeric-ids --perms --xattrs --acls --executability --owner --group --times rsync://${PRIMARYIP}:8730/db-mysql/ /var/lib/mysql/

# "Зеркалируем" резервные копии данных web сервисов
#
rsync --ipv4 --ignore-errors --recursive --sparse --delete-during --hard-links --links --numeric-ids --perms --xattrs --acls --executability --owner --group --times rsync://${PRIMARYIP}:8730/backups/ /var/backups/www/

exit 0

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

Внесем в таблицу "/etc/crontab" указание на запуск скрипта "зеркалирования" с определённой периодичностью. Можно и раз в день, а можно и раз в десять минут, если не хотим потерять работу клиентов сайта в течении пары десятков часов. Учитывая то, что утилита rsync копирует только изменённые данные, нагрузка в процессе эксплуатации на оборудование не будет настолько великой, чтобы переносить время процедуры на ночное время:

# cat /etc/crontab

....
*/10  *  *  *  *  root  /usr/local/etc/web/mirroring.sh &
....

Вышеприведённой директивой в "/etc/crontab" мы указываем утилите cron на необходимость запуска скрипта "/etc/custom/mirroring.sh" каждые десять минут.

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

После того, как при первом запуске основной объем будет загружен с первичного на вторичный сервер "зеркалирование" будет отрабатывать достаточно быстро. Например, при общем объёме пользовательских данных в десять Гигабайт и общей активности обращений к сайтам в среднем в пять - пятнадцать конкурентных пользователей при запуске каждые десять минут скрипт отрабатывает за одну-две минуты.

"Зеркалирование" СУБД будет можно было бы осуществляться средствами самих СУБД, если бы оные поддерживали "красивую" репликацию или синхронизацию в полном объёме. К сожалению MySQL поддерживает репликацию только для таблиц типа MyISAM, что элементарно выбивает из ряда реплицируемых базы почти половины сайтов, использующих отличные от MyISAM "движки". Потому будем останавливать сервер MySQL (если он по какой-то причине не был остановлен при получении несущим сервером статуса вторичного) и актуализировать базы простым копированием всего содержимого, включая "бинарные" журналы.

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


Заметки и комментарии к публикации:


Оставьте свой комментарий ( выразите мнение относительно публикации, поделитесь дополнительными сведениями или укажите на ошибку )