Pull to refresh
91.66

Как мы «бэкапим» серверы в Amazon и отбиваемся от пираний

Reading time8 min
Views18K
Многие владельцы веб-проектов, размещенных в популярном облачном провайдере Amazon, наверняка задумываются о том, как создать эффективную и надежную конфигурацию веб-решения, как проводить резервное копирование машин, как действовать в случае коллапса датацентра, в котором размещены ваши серверы. К сожалению, официальная документация облачного хостинга «несколько» скупа на тему надежности и внутренней реализации веб-сервисов — поэтому приходится полагаться на собственный боевой опыт эксплуатации и армейскую смекалку.

Ситуацию усугубляет наблюдаемое ныне противостояние распространенного заблуждения, что в облаке все очень надежно и можно крепко спать: жесткие диски «из титана», сетевой трафик течет «по золотым проводам», а инженеры там крутые парни из Челябинска, рассекающие писсуар пополам — с рекомендациями облачного провайдера на тему «мы предоставляем сервисы достаточной надежности и быстрые каналы между ДЦ, а задача архитектора проекта — комбинировать наши технологии, доводя девяточки справа до нужного количества».

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


Надежность EBS-дисков — RAID1 или «хуже»?


Если честно, из официальной документации Amazon по EBS-дискам я так и не понял, надежнее ли они RAID1 или нет — всякие разговоры про функциональную зависимость объема диска, времени последнего снепшота диска, числа волос в бороде волшебника и фазы луны не внушают оптимизм:
«The durability of your volume depends both on the size of your volume and the percentage of the data that has changed since your last snapshot. As an example, volumes that operate with 20 GB or less of modified data since their most recent Amazon EBS snapshot can expect an annual failure rate (AFR) of between 0.1% – 0.5%, where failure refers to a complete loss of the volume»

Во время недавней аварии в европейском ДЦ Амазона у нас из софтварного RAID10 вылетели сразу 2 диска из-за отключения питания в датацентре. В сети также иногда появляются жутковатые сообщения о том, что диск EBS внезапно «сломался», клиенту прислали письмо на тему «ваши данные потерялись, к сожалению» и официально рекомендуется сделать снепшот как раз за минуту перед отказом диска :-) Такое впечатление, что механизм снепшотов появился именно для «подстраховки» от выхода из строя не очень надежных EBS-дисков.

Публичные виртуальные машины, которые используются для создания собственных приватных образов, имеют только один EBS-диск, который не в рейде и на котором корневой раздел. А загрузить машину с корневым разделом на софтварном рейде — мягко сказать, без напильника непросто и требует времени.

Тем не менее, за полгода в Амазоне у нас не вылетел ни один диск (у нас их около 50), не считая отключения питания после удара молнии, что позволяет относиться к ним как к более-менее надежным «RAID1» дискам (тук, тук; репликация на одно устройство, расположенное в том же датацентре), уступающим в надежности s3 (реплицируется дополнительно на 2 устройства в разных датацентрах, и вообще это не блочное устройство), но в 10 раз более надежным чем обычные жесткие диски.


Софтварный рейд и невысокая производительность EBS-дисков


После миграции в Амазон в начале этого года мы столкнулись с достаточно невысокой производительностью EBS-дисков, что было особенно заметно на машине с MySQL. «Подсмотрев» идею в RDS (Амазон автоматически, в зависимости от суммарного объема EBS-дисков, размещают информацию базы данных на софтварном рейде) и использовав армейскую смекалку мы перенесли данные на созданный из EBS-дисков софтварный RAID10 (mdadm в Linux):
cat /proc/mdstat
Personalities : [raid10]
md0 : active raid10 sdo[0] sdj[8](S) sdi[7] sdh[6] sdg[5] sdn[4] sdm[3] sdl[2] sdk[1]
629145344 blocks 64K chunks 2 near-copies [8/8] [UUUUUUUU]

Ах, да, spare-disk, как вы видите, мы используем :-) 'sdj[8](S)'. Решили все таки поставить в рейды для увеличения уровня отказоустойчивости.
После миграции данных на рейды проблемы с производительностью дисковых подсистем на машинах перестали беспокоить.


Кластерные файловые системы и EBS-диски


Т.к. наш основной сайт (www.1c-bitrix.ru) работает в веб-кластерной конфигурации: две веб-машины, две машины с базой данных (мастер+слейв) — за балансировщиком (nginx), мы очень, очень хотели использовать кластерную файловую систему типа OCFS2 или GFS2. Но EBS-диски оказалось нельзя одновременно смонтировать на несколько виртуальных машин. Поэтому мы используем для кластеризации контента утилиту csync2, форсированную надстройкой для «быстрой» синхронизации часто обновляемых тяжелых папок на базе inotify (если интересно, расскажем подробнее).


Бэкапим EBS-диски


Диски виртуальной машины совсем несложно бэкапить, т.к. именно для этого создан инструмент создания снепшотов блочного устройства:
ec2-create-snapshot vol-a5rtbvwe
Операция стартует очень быстро (секунды), а загрузка снепшота в s3 продолжается определенное время, в зависимости от объема диска и загруженности Amazon — до десятков минут.

Мы взрослые люди и понимаем, что целостный снепшот диска может быть если:
  1. Диск предварительно отмонтирован: «umount -d /dev/sdh».
  2. Сервер предварительно остановлен.
  3. Файловая система «заморожена». Хотя в этом случае нужно еще приложениям сказать, чтобы сбросили данные на диск.


Иначе мы получаем снепшот диска «как бы после внезапного выключения питания» и диск нужно будет полечить (fsck) при загрузке машины — что, благодаря, современным журналируемым файловым системам (ext3, ext4, xfs и др.) происходит незаметно и часто быстро за счет проигрывания журнала. Мы тестировали создание снепшота диска с «живой» (без размонтирования) ext3 и xfs — после некоторой паузы для проигрывания журнала диск (созданный из снепшота) монтируется и работает.

«Замораживание» и «отмораживание» файловой системы

В качестве файловой системы, поддерживающей «приостановку», мы выбрали xfs. Эта ФС рекомендуется Amazon и используется утилитами создания целостного снепшота типа ec2-consistent-backup:
xfs_freeze -f | -u mount-point
Действительно, если после «xfs_freeze -f» сделать снепшот диска, он монтируется без проигрывания журнала очень быстро.

В CentOS6 (и видимо в других дистрибутивах с «обновленным» ядром) стало возможным «фризить» также дефолтные файловые системы типа ext3/ext4 командой fsfreeze. Мы планируем хорошенько потестировать эту утилиту в контексте создания целостных снепшотов EBS-дисков.

Бэкап софтварного рейда


Если мыслить логически, то нужно а) «остановить» файловую систему рейда, б) запустить создание снепшота ДЛЯ КАЖДОГО физического диска рейда, в) «отпустить» файловую систему рейда.
К сожалению, в документации к API Amazon нет информации, сколько времени должно пройти между б) и в). Однако опытным путем было найдено (и аналогично делается в данной утилите), что достаточно получить идентификаторы снепшотов из вызова АПИ для каждого диска рейда и файловую систему рейда можно «разморозить» — и при сборке рейда он подхватится без ребилдинга. Иначе — начнет ребилдиться. Вот так сделали мы:
bxc_exec("/usr/bin/ssh remote_xfs_freezer@hostname".escapeshellarg("sudo /usr/sbin/xfs_freeze -f /xfs_raid_path"));
...
ec2-create-snapshot - для каждого диска рейда
...
bxc_exec("/usr/bin/ssh remote_xfs_freezer@hostname".escapeshellarg("sudo /usr/sbin/xfs_freeze -u /xfs_raid_path"));
...
ec2-create-volume --snapshot id_of_snapshot
...
ec2-attach-volume volume_id -i instance_id -d device
...
mdadm --assemble /dev/mdN /dev/sd[a-z]


Снепшот MySQL?


Хотя можно одной рукой сбросить данные MySQL на диск: FLUSH TABLES WITH READ LOCK, левой ногой зафризить файловую систему: xfs_freeze -f /path, а носом нажать Enter для выполнения API вызова снятия с дисков снепшота: ec2-create-snapshot… нас пока полностью устраивает асинхронная репликация MySQL в другой ДЦ :-). Интересным предоставляется создание снепшотов базы с использованием xtrabackup — без вышеописанных танцев с бубнами.


Можно бэкапить всю машину сразу!


Если отдельно бэкапить диски, группы дисков в рейдах, то, при наличии конфига — процесс работает стабильно и прозрачно. Однако, если вам нужно иногда (например, в ДЦ ударила на выходных молния :-) ) переезжать между AZ (датацентрами) Amazon — проще ввести уровень абстракции выше идшников инстансов, снепшотов и дисков и оперировать категориями ролей и образов (AMI — Amazon Machine Image) машин. Через АПИ можно назначать объектам Amazon тэги и фильтровать по ним выборки.

Мы определили роли: веб-машина, СУБД, балансировщик, машина мониторинга и наши скрипты бэкапов работают примерно так:
  1. Найди машину с тегом «СУБД» и получи ее ИД
  2. «Заморозь» диски машины
  3. Сделай снепшот дисков. Задай снепшоту тэг «СУБД».
  4. «Разморозь» диски машины
  5. Для очистки устаревших снепшотов можно сделать выборку по тэгу и времени и удалить лишние.

Однако, гораздо проще бэкапить работающую виртуальную машину в образ (AMI image). При этом в образе сохраняются ее настройки и пути к создаваемым снепшотам, из которых будут созданы диски машины, присоединяемые к ней при старте:
ec2-create-image id_of_instance [--no-reboot]
...
ec2-run-instances ami_image_id


Почувствуйте разницу! Я создаю образ с машины с рейдом10 из 9 дисков — одной командой. И не парюсь с идшниками ее дисков и снепшотов — вся необходимая для воссоздания машины информация записывается в образ AMI автоматически. Единственный минус — неясно, на сколько времени нужно лочить диски/рейды работающей машины, чтобы создать из нее образ AMI. Опытно нашли, что достаточно 2-5 секунд — если меньше, то диски/рейды начинают восстанавливаться при создании новой машины. Также очень удобно то, что поднять машину из бэкапа в образ (AMI) можно в другом датацентре (AZ).
Сейчас мы создаем два раза в сутки бэкап в образ AMI всех машин нашей кластерной конфигурации (сотни гигабайт). Очистка «устаревших» образов, вместе с их снепшотами производится скриптом:
function bxc_delete_old_instance_backups( AmazonEC2 $ec2, $instanceRole = '', $bkpsMaxCount = 3) {

    if (!$instanceRole || !is_object($ec2) || $bkpsMaxCount<1 ) {
        bxc_log_error("Bad input params: ".__FUNCTION__);
        return false;
    }

    $response = $ec2->describe_images(array(
        'Filter' => array(
            array('Name' => 'tag:Role', 'Value' => 'Image:'.$instanceRole),
            array('Name' => 'state', 'Value' => 'available'),
        )
    ));

    if ( !$response->isOK() ) {
        bxc_log_amazon_error($response, __FUNCTION__.":".__LINE__);
        return false;
    }

    if ( isset($response->body->imagesSet->item) ) {

        $images = array();
        $snaps = array();

        foreach ( $response->body->imagesSet->item as $image ) {
            $images[ (string) $image->name ] = (string) $image->imageId;
            if ( isset($image->blockDeviceMapping->item) ) {
                foreach ( $image->blockDeviceMapping->item as $disk ) {
                    $snaps[ (string) $image->name ][] = $disk->ebs->snapshotId;
                }
            }
 
        }

        if ($images) {
            krsort($images);
        } else {
            bxc_log("No images with tag:Role=Image:$instanceRole");
            return true;
        }

        if ( count($images) <= $bkpsMaxCount ) {
            bxc_log("Nothing to delete");
            return true;
        }

        $imagesToDelete = array_slice($images, $bkpsMaxCount, count($images)-$bkpsMaxCount, true);
 
        foreach ($imagesToDelete as $imageName=>$imageId ) {
            $r = $ec2->deregister_image($imageId);
            if ( $r->isOK() ) {
                bxc_log("Image deleted ok: ".$imageId);

                sleep(5);//Important. Wait to ami really be deleted ;-)

                foreach ( $snaps[$imageName] as $snapId ) {
                    $rr = $ec2->delete_snapshot( $snapId );
                    if ( $rr->isOK() ) {
                        bxc_log("Snapshot deleted ok: ".$snapId);
                    } else {
                        bxc_log_amazon_error($rr, __FUNCTION__);
                        bxc_log_error("Cannot delete snapshot: ".$snapId);
                    }
                }

                
            } else {
                bxc_log_amazon_error($r, __FUNCTION__);
                bxc_log_error("Cannot delete image: ".$imageId);
            }
        }

        return true;

    } else {
            bxc_log("No images with tag:Role=Image:$instanceRole");
            return true;
    }

    return false;
}



Админка, консоль или API


Amazon AWS имеют неплохую админку, однако многие вещи можно сделать только через API. Для Linux можно установить инструменты командной строки для работы с API Amazon, которые довольно хорошо документированы. Мы используем такие инструменты:
ls /opt/aws/
AutoScaling-1.0.39.0
ElasticLoadBalancing-1.0.14.3
ec2-api-tools-1.4.4.1
env-set.sh

Последний скрипт устанавливает переменные окружения для работы инструментов. Обратите внимание, практически каждый веб-сервис Amazon имеет свой набор утилит, которые нужно скачать и установить отдельно. Для управления машинами, например, документация находится тут, а утилиты командной строки — тут.
Пока неплохо себя показывает в работе официальный SDK для работы с API Amazon для PHP (вышеприведенный скрипт использует именно эту библиотеку).

Выводы


С EBS-дисками Amazon можно эффективно работать, объединив их в софтварный рейд. К сожалению, нельзя замонтировать EBS-диск к нескольким виртуальным машинам, для работы с кластерной файловой системой типа OCFS2, GFS2. Бэкапы как дисков, так и рейдов EBS делаются несложно, необходимо только понять основные принципы и обойти подводные камни. Удобно бэкапить машины целиком, а не по отдельному диску — это позволит быстро развернуть конфигурацию в соседнем датацентре если в ваш ДЦ ударит молния или врежется самолет. Всем удачи!
Tags:
Hubs:
+54
Comments22

Articles

Information

Website
www.bitrix24.ru
Registered
Founded
2012
Employees
201–500 employees
Location
Россия