Большинство статей в наш блог пишут разработчики. Мы решили исправить эту несправедливость и добавить немного DevOps. Сегодня поговорим о важном ― о бэкапах.
Так как Badoo активно развивается и количество сотрудников постоянно увеличивается, мы пришили к выводу, что централизованное резервное копирование гораздо удобнее, чем частичное копирование и хранение информации в различных местах.
В статье мы рассмотрим, как различными способами «забэкапить» довольно большое количество рабочих станций с помощью одного хранилища, не прибегая к серьёзным вложениям и избегая громоздкой реализации.
Заранее оговоримся, что бэкап не охватывает 100% сотрудников, так как не все хранят свои данные на локальных машинах, поэтому у нас не было цели сделать бэкап обязательно-принудительным.
Одной из основных сложностей централизованного бэкапа стало то, что сотрудники используют разные операционные системы.
Как же мы смогли собрать всех на одном сервере?
Примерная статистика, основанная на использовании нашей системы:
Mac OS ~ 66%;
Linux ~ 27%;
Windows ~ 7%.
А теперь подробнее, что скрывается за этими тремя ОС и каким образом мы будем настраивать их у пользователя.
1) Mac OS ― через Time Machine (в последних версиях Mac OS поддерживается из коробки, ниже 10.6 у нас просто нет), на стороне сервера — Netatalk;
2) Windows ― стандартными средствами через «Архивация и восстановление» (Backup and restore), на стороне сервера — Samba;
3) Linux ― несколько вариантов: samba, rsync (доступ по паролю); полноценный ssh отключён за ненадобностью и в целях безопасности.
Забежим вперёд и покажем, как будет выглядеть интерфейс управления пользователями, которые нуждаются в бэкапе:
Администратору нужно выбрать уже существующую учетную запись или завести новую, сгенерировать пароль и указать тип операционной системы.
Аппаратная часть
В качестве хранилища был собран сервер с 24 дисками по 3.5 дюйма и 3 TБ ― большой объём за малые деньги.
Каждый диск монтируется отдельно, RAID или LVM не используется ― при выходе из строя одного из дисков его можно быстро заменить; также решается вопрос с недостатком свободного места: из всех дисков мы выжимаем максимум объёма.
«Что будет, если умрёт один из дисков, на котором были бэкапы пользователей?» ― спросите вы. Попросим забэкапиться ещё раз после замены диска. Вероятность того, что один из 24 дисков умрёт вместе с ноутбуком сотрудника, действительно мала. В крайнем случае мы попытаемся восстановить утраченную информацию.
Главная функция сервера ― хранение информации, поэтому описывать CPU и память не имеет смысла, любые современные процессоры справятся с этим заданием.
Готовим диски
Форматируем и монтируем каждый диск:
parted /dev/sd${i} -s mklabel gpt
parted /dev/sd${i} mkpart primary 0GB 2996GB
mkfs.ext3 /dev/sd${i}1
Удаляем зарезервированное пространство суперпользователя:
tune2fs -m 0 /dev/sd${i}1
В /etc/fstab для каждого раздела прописываем
UUID=${UUID} /storage/sd${i}1 ext3 noatime,acl,user_xattr,usrquota 0 0
В итоге получаем диски, смонтированные в директорию /storage/sd${i}, где ${i} ― одна из букв нашего диска.
Монтировать диски лучше с привязкой по UUID, т.к. не исключено «перемешивание» дисков, а с таким количеством устройств нам придётся долго восстанавливать правильные пути после внезапной перезагрузки сервера.
Всех пользователей мы будем ограничивать квотой в файловой системе стандартными средствами Linux, поэтому подготовим для этого диски:
quotacheck -cu /storage/sd${i}
Программная часть
Netatalk
В данном решении самое сложное ― Mac OS X, поэтому начнём с настройки afp.
Установим необходимые пакеты:
rpm -Uvh libdb-4_8-4.8.30-18.6.x86_64.rpm
rpm -Uvh db-utils-4.8.30-18.6.x86_64.rpm
rpm -Uvh netatalk-2.2.4-3.7.x86_64.rpm
rpm -Uvh netatalk-devel-2.2.4-3.7.x86_64.rpm
Netatalk будем использовать версии 2.*, т.к. в версии 3.* не поддерживается использование переменных в указаниях путей (http://netatalk.sourceforge.net/3.0/htmldocs/afp.conf.5.html, раздел VARIABLE SUBSTITUTIONS: The use of variables in paths is not supported for now).
Стоит заметить, что пакет Netatalk мы собирали со следующими флагами:
--with-cracklib
--with-bdb
--enable-tcp-wrappers
--enable-zeroconf
В /etc/netatalk/AppleVolumes.default добавим строчку
~/TimeMachine "BackupMachine" allow:@backupuser cnidscheme:dbd options:usedots,upriv,tm volsizelimit:250000 dbpath:/local/netatalk/db/$u
в которой:
allow:@backupuser
― разрешает подключаться пользователям, находящимся в группе backupuser;
BackupMachine
― имя, которое будет отображаться как подключённый диск, физически смонтированный в ~/TimeMachine относительно пользователя;
volsizelimit:250000
― ограничит пользователя квотой в 250 гб, но только если пользователю не задана системная квота. Т.к. в нашем случае мы используем системные квоты, эта опция бесполезна;
tm
― обязательная опция, позволяет TimeMachine на стороне клиента распознать выделенный ресурс, как валидный для бэкапа;
dbpath:/local/netatalk/db/$u
― путь к информации по метаданным пользователя. Нужна в том случае, когда строится список директорий и файлов, которые мы забэкапили. Будет практичней, если это вынести на отдельный диск, например, на SSD;
переменная $u
подставляет имя пользователя, которое нам так нужно и не работает в третьей версии.
Включаем логирование afpd.
В /etc/nettalk/afpd.conf
добавим строчку
-setuplog "default log_info /var/log/afpd.log" -keepsessions -transall -savepassword
в которой:
keepsessions
― сохраняет сессии при выключении afpd;
transall
― включает оба протокола ― AFP-over-Appletalk и AFP-over-TCP;
savepassword
― позволяет запомнить пароль пользователя в локальном keygen (без этой опции TimeMachine в Mac OS 10.8 не работает).
В /etc/netatalk/netatalk.conf
увеличим максимальное количество пользователей до 100
AFPD_MAX_CLIENTS=100
С Netatalk закончили. Подключиться можно стандартным приложением Time Machine, используя адрес вида afp://%SERVERNAME%/
Samba
Samba будет использоваться для подключения как в Windows, так и в Linux.
Настройка довольно проста: в /etc/samba/smb.conf
добавляем
[global]
security = user
workgroup = Badoo
netbios name = BadooBackup
local master = no
domain master = no
preferred master = no
socket options = TCP_NODELAY IPTOS_LOWDELAY SO_KEEPALIVE SO_RCVBUF=8192 SO_SNDBUF=8192
[homes]
comment = Home Directories
valid users = %S
writable = yes
create mask = 0700
directory mask = 0700
browseable = No
read only = No
На этом настройка закончена. В Windows можно подключаться через «Проводник», в Linux ― автоматически монтировать при входе в систему.
Управление доступом
Итак, мы закончили с аппаратной частью и приложениями. Осталось разобраться с тем, как предоставлять пользователям несколько способов доступа.
Система управления пользователями у нас уже есть, значит, будем использовать привычные для нас манифесты Puppet.
Данные о пользователях будем хранить в интуитивно понятной таблице MySQL:
| backupusers | CREATE TABLE `backupusers` (
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
`uid` int(11) NOT NULL,
`username` varchar(64) COLLATE utf8_unicode_ci DEFAULT NULL,
`backup_username` varchar(64) COLLATE utf8_unicode_ci NOT NULL DEFAULT '',
`backup_server` varchar(32) COLLATE utf8_unicode_ci NOT NULL DEFAULT 'backupmsk',
`password` varchar(100) COLLATE utf8_unicode_ci NOT NULL DEFAULT '',
`password_smb` varchar(100) COLLATE utf8_unicode_ci DEFAULT NULL,
`shell` varchar(32) COLLATE utf8_unicode_ci NOT NULL DEFAULT '/bin/false',
`map_drive` varchar(6) COLLATE utf8_unicode_ci NOT NULL DEFAULT 'sdc',
`quota` int(5) NOT NULL DEFAULT '250',
`sftp` tinyint(1) NOT NULL DEFAULT '0',
`isactive` tinyint(1) NOT NULL DEFAULT '0',
`os` int(1) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci |
Фреймворк записывает данные именно в эту таблицу.
Несколько особенностей:
поле `map_drive`
― названия сервера и диска, которые будут использоваться для текущего пользователя. Не задается администратором, распределяется автоматически в зависимости от занятого места в том или ином разделе и на сервере. Если места для кого-то хватать не будет, то данные будут перенесены в другой раздел;
поле `isactive`
показывает, разрешено ли сотруднику пользоваться сервером для бэкапа. Если все ОС будут неактивны, перейдёт в значение 0. Данные пользователя при этом не удалятся (полезно, например, при потере ноутбука);
поле `sftp`
позволяет пользователю использовать rsync (об этом ниже). Этот способ позволит «продвинутым» коллегам бэкапить свои данные, используя самописные скрипты.
После обновления таблицы запускается скрипт, который генерирует манифест с данными для каждого пользователя.
Для того чтобы удобно было управлять пользовательскими директориями, добавим функцию в наш манифест:
define backupuser_dirs($name,$map_drive,$home="/home/${name}") {
file {
"$home": owner => $name, ensure => symlink, target => "/storage/${map_drive}/${name}", require => File["/storage/${map_drive}/${name}"];
"/storage/${map_drive}/${name}": owner => $name, ensure => directory, backup => false, mode => 0711;
"$home/TimeMachine": owner => $name, ensure => directory, backup => false, mode => 0711, require => File["$home"];
}
}
А вот отрывок манифеста для Puppet одного из пользователей:
@user { "i.ableev":
ensure => $hostname ? {
/^%servername%$/ => present,
# создаем пользователю доступ именно на этом сервере, где он явно объявлен
default => absent,
# во всех остальных случаях доступ, даже если он и был, отзывается
},
home => "/home/i.ableev",
# указываем домашнюю директорию; (!) является симлинком на /storage/$map_drive/$name
uid => "1217",
groups => ['backupuser'],
# указываем, в какую группу добавить пользователя
password => 'V2UgYXJlIGhpcmluZyEgaHR0cDovL2NvcnAuYmFkb28uY29tL2pvYnMvCg==',
# пароль используется при использовании Netatalk и rsync
shell => "/bin/false",
# отключаем пользователю shell, чтобы ограничить доступ по ssh
}
@backupuser_dirs { "i.ableev":
name => "i.ableev",
map_drive =>"sdh",
# директория, в которой будет располагаться директория бэкапов пользователя вида /storage/$map_drive/$name
require => User["i.ableev"];
}
@exec { "i.ableev_quota":
command => "/usr/sbin/setquota -u i.ableev 262144000 262144000 0 0 -a",
# выделяем пользователю 250 гб …
path => "/usr/sbin",
onlyif => "/usr/bin/test `/usr/sbin/repquota -ua | /usr/bin/egrep '^i.ableevs*' | /usr/bin/awk {'print $4'}` -ne "262144000"",
# … но выделяем её в том случае, если квота не равна 250 гб.
}
@line { "i.ableev_smb":
# добавляем в файл /etc/samba/smbpasswd строчку вида:
# @user:$uid:$hash
file => '/etc/samba/smbpasswd',
line => 'i.ableev:1217:XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX:5CDA711BBD899465D8F57D12BDF2BF68:[U ]:LCT-5058462B:',
# хеш пароля генерируется на этапе формирования манифеста скриптом
}
Теперь у пользователей есть возможность подключаться к серверу, используя Mac OS, Windows или Linux.
rsync
Для предоставления доступа через rsync (но не ssh), мы будем использовать chroot плюс ограниченный по возможностям shell. Манифест и функция в таком случае будут выглядеть несоколько иначе:
define backupuser_dirs_sftp($name,$map_drive,$home) {
file {
"/home/${name}": owner => $name, ensure => symlink, target => "${home}", require => File["$home"];
"$home": ensure => directory, owner => root, mode => 0755;
"$home/sftp": owner => $name, ensure => directory, backup => false, mode => 0711, require => File["$home"];
}
}
@user { "i.ableev":
ensure => $hostname ? {
/^backupmsk$/ => present,
default => absent,
},
home => "/storage/sdh/i.ableev",
uid => "1217",
groups => ['backupuser'],
password =>
'V2UgYXJlIGhpcmluZyEgaHR0cDovL2NvcnAuYmFkb28uY29tL2pvYnMvCg==',
# задаём пароль для входа
shell => "/bin/badooshell",
# ограниченный shell позволяет использовать только rsync
}
@backupuser_dirs_sftp { "i.ableev":
name => "i.ableev",
map_drive =>"sdh",
home => "/storage/sdh/i.ableev",
# путь к директории ~/sftp ― туда будут складываться бэкапы
require => User["i.ableev"];
}
@file { "/storage/sdh/i.ableev/bin":
# путь к chroot’у ― здесь будет лежать наш badooshell
ensure => directory,
recurse => true,
purge => true,
force => true,
backup => false,
owner => root,
group => root,
source => "puppet:///modules/officebackup/bin/";
# отсюда он скачивается
}
@file { "/storage/sdh/i.ableev/lib64":
# минимальный набор библиотек, необходимый для работы rsync: ld-linux-x86-64.so.2 libc.so.6 libpopt.so.0
ensure => directory,
recurse => true,
purge => true,
force => true,
backup => false,
owner => root,
group => root,
source => "puppet:///modules/officebackup/lib64/";
}
Все манифесты готовы.
Результаты
Что мы получили в итоге:
- недорогой сервер с 72 терабайтами полезных данных;
- на одном сервере находятся бэкапы всех типов операционных систем пользователей;
- предоставлен доступ всем, кто хочет делать бэкапы.
Enjoy!
Илья Аблеев, ableev, сотрудник эксплуатации Badoo Development
Оригинал комикса: dilbert.com/strips/comic/2007-11-21/
Автор: ableev