Бэкап большого количества мелких файлов

    Рано или поздно настройкой резервного копирования рабочих файлов озадачивается любой уважающий себя современный IT специалист. После ряда опечаток/ошибок программистов нашлось время для этого и у меня.
    Специфика веб-приложения такова, что рабочий каталог занимает более 50ГБ на жестких дисках, включая в себя около 900 тысяч мелких файлов (картинки, превьюшки, ...). Поэтому в лоб решить задачу с помощью tar и аналогов не вышло. Да и хотелось бы иметь некоторую вариативность хранимых данных, а в случае с полным бэкапом реализация требовала больших затрат на хранение по сути одинаковых данных с небольшими изменениями. Плюс неплохо было бы дублировать копии на удаленном сервере бэкапов для снижения риска потери критической информации в результате краха железа. После скрупулезного анализа поисковых выдач и отбрасывания заведомо неподходящих мне методов, остановился на паре вариантов, навязываемых чаще всего в комментариях к самописным shell-велосипедам энтузиастов.


    rdiff-backup

    rdiff-backup показался с виду более подходящим и удобным. Написанный на python, он хранит данные инкрементально, позволяя получить состояние файла или директории в любой момент времени (в обозримом прошлом, учитывая время хранения снимков). Гибкое управление из консоли дает полную свободу действий и предоставляет полный контроль за ситуацией. Автоматическое создание бэкапов требует добавления в планировщик пары команд (вторая для подчистки старых снимков, не несущих какой-либо ценности в силу давности изменений).
    Но тестирование показало, что утилита весьма прожорлива и справляется с моей задачей крайне неохотно. Дело в том, что ежедневно изменяется небольшой объем данных (300МБ), но изменения затрагивают около 30 тысяч файлов. На выявление измененных файлов, судя по всему, тратится бОльшая часть времени работы программы. После часа наблюдений на увеличившийся до неприличных 20% iowait во время очередного запуска скрипта было принято решение попробовать другой софт и сравнить их между собой.

    rsnapshot

    rsnapshot, написанный на Perl, базируется на rsync. В рабочей директории программы (назовём так место, куда складываются бэкапы), создается ряд папок с индексом, который при каждом следующем запуске программы увеличивается до указанного в конфигурации значения. Затем устаревшая копия удаляется. Если пройти в любую из созданных папок, то внутри можно найти полную копию резервируемых данных. На это указывает и общий размер папки (при просмотре стандартными средствами Midnight Commander, например) — он равен сумме всех папок. На самом деле это не так. Программа создает жесткие ссылки между одинаковыми данными в пределах рабочего каталога. Таким образом, последняя актуальная копия является самой «тяжелой», а размер всех остальных составляет разницу в измененных данных.

    Тестирование

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

    Для тестов была взята случайная папка проекта размером 11ГБ, содержащая 593 подкаталога разной степени вложенности и 230911 файлов. Размер файлов плавает от 4 до 800КБ, как указано выше, это в графический материал. Обе утилиты тестировались поочередно, внешние факторы практически полностью отсутствовали (нет других пользователей, нагрузки и тяжелых процессов). С помощью утилиты time было подсчитано время выполнения каждого из тестовых заданий, а также для сравнения копирование всего каталога средствами cp:

    Первый бэкап — полное копирование в место бэкапа 11090МБ
    real user sys
    cp 6m30.885s 0m1.068s 0m24.554s
    rsnapshot 7m53.879s 1m57.299s 1m22.441s
    rdiff-backup 10m50.314s 3m26.073s 1m0.928s


    Повторный запуск (в папке не было изменений)
    real user sys
    rsnapshot 0m10.129s 0m4.936s 0m6.708s
    rdiff-backup 1m3.969s 1m0.616s 0m2.048s


    Внутри каталога одна случайная папка дублируется (общий размер увеличивается до 13267МБ)
    real user sys
    rsnapshot 0m31.175s 0m22.001s 0m17.365s
    rdiff-backup 27m53.517s 1m58.819s 0m19.005s


    Повторный запуск после увеличения размера каталога (с последнего выполнения изменений нет)
    real user sys
    rsnapshot 0m11.477s 0m5.748s 0m7.368s
    rdiff-backup 1m16.366s 1m13.713s 0m1.912s


    Удаляем сдублированную папку, уменьшая размер каталога до первоначального
    real user sys
    rsnapshot 0m13.885s 0m6.388s 0m9.077s
    rdiff-backup 52m55.794s 2m1.560s 0m21.941s


    Контрольный повторный запуск без внесения изменений
    real user sys
    rsnapshot 0m11.250s 0m5.132s 0m7.068s
    rdiff-backup 1m2.380s 1m0.088s 0m1.792s


    Итоги

    Как видно из таблиц сравнительного тестирования, rdiff-backup тяжело переваривает изменение большого количества мелких файлов, следовательно, рентабельнее использовать rsnapshot, дабы не тратить бОльшую часть времени работы сервера на пустое ковыряние в файловой системе.
    Возможно кому-то будет полезным увидеть результат тестирования и сэкономить затраченное мною время на поиск оптимального для описанных в статье случаев резервного копирования файлов.
    Поделиться публикацией
    Комментарии 45
      0
      А почему нет cp в последующих тестах? Скажем, копировать поверх только изменившиеся файлы (по дате).
        0
        Не смог сходу придумать, что там протестировать в некоторых тестах. Поэтому привел пример только полного копирования, чтобы показать примерную скорость выполнения стандартных операций по перемещению
          0
          Если только по дате, просто и удобно tar: cd source && tar -c --newer="..." * | tar -C dest -xp
            0
            Да, я это и имел в виду. Вопрос в скорости.
          +2
          Несколько удивляют цифры 3-го прохода у rshapshot.
          Согласно тесту N1 копирование 11090МБ занимает около 8 минут, или средняя скорость при текущем наборе — файлов 23мб/сек. Что в общем-то похоже на правду при большом количестве мелких файлов.
          Далее выясняем, что на «обход каталога» без самой операции копирования (только создание хардлинков) тратится около 10 сек.
          А вот на 3-м проходе требуется скопировать 13267МБ-11090МБ=2177Мб «новых данных», но на это тратится только 31 сек. Из них операция аналогичная предыдущей из N2 должна занять 10 сек. Значит на само копирование новых файлов потрачено 21 сек. И скорость получается уже 103Мб/сек, что намного превышает скорость первоначального копирования из N1.
          Либо файлы оказались в дисковом кэше, либо rshaphot определил, что в новом каталоге находятся копии файлов из соседнего каталога, и тоже создал хардлинки, не проводя фактического копирования.
          Другого объяснения так сильно подскочившей скорости копирования у меня нет. Как можете прокомментировать этот момент?
            +2
            Да, методика тестирования с дублированием папки выбрана крайне неудачная, тут вы правы. Проведу повторное тестирование с действительно новыми файлами для rsnapshot. rdiff-backup вроде можно не перепроверять, ему не полегчает
              +2
              Скорее всего — оказались в дисковом кеше.

              Алгоритм работы там простой — переименовали старую папку в более старую, сделали копирование из неё в новую хардлинками, сделали rsync --delete поверх в новую папку.

              У rsnapshot есть очень большой минус — ему нужны хардлинки. Поэтому любой бекап можно инициировать со стороны бекапного сервера с rsnapshot, но нельзя инициировать со стороны клиента и заливать на удалённое что-то по, например, sftp. Единственная альтернатива тут — маунтить sftp через sshfs, создать там образ, замаунтить его как loopback — но это решение имеет очень много накладных расходов и неудобств.
                +1
                Дублирование оказалось не при чем, повторное тестирование с 2,5ГБ реально новых файлов показало, что они кэшируются и поэтому быстро перекладываются rsnapshot'ом в папку бэкапа (предварительно дергал папки для тестов из рабочих каталогов проекта, этим и приукрасил результат)
                +2
                Из практики: ZFS (snapshot+send) позволяет бэкапить несколько миллионов мелких файлов без простоя сервиса и за разумное время.
                  +2
                  Из реальности: огромное число разношерстных серверов, перевести которые на «удобные» файловые системы или хотя бы внедрить LVM — практически невыполнимая задача, на которую никогда не будет времени
                    –1
                    это назвается «впадлу заморачиваться». Надо просто 1 раз сделать нормально (snapshot zfs или lvm) и не изобретать костыли.
                  –1
                  lsyncd + tar раз в неделю на бэкапном хранилище должны спасти галактику.
                    0
                    Во-первых, требуются ежедневные бэкапы, а не «раз в неделю»
                    В-вторых, изменений за сутки сейчас около 30 тысяч файлов. А нужен неплохой запас.
                    В-третьих, этот вариант рассматривался и был отброшен — habrahabr.ru/post/132098/
                    Минимальная продакшн-конфигурация — 584 тысячи вложенных директорий. А lsyncd навешивает inotify'и на каждую директорию. Сделать это сразу для всего дерева невозможно. Памяти, 584 тысячи нотифаев, съедают относительно немного, около 200 Мб (из 16 ГБ имеющихся), но вот процесс этот занимает 22 минуты.
                      0
                      Во-первых, можете делать ежедневные бэкапы. Про неделю я сказал к примеру.
                      Во-вторых, в указанной статье одних только директорий почти 600 тысяч. А у Вас всего файлов 900к. Так что не думаю, что вы бы столкнулись с большими проблемами в этом плане.
                        0
                        В-третьих, возможно, стоит подумать — а действительно ли вам критически важны все эти файлы? К примеру, нельзя ли бэкапить только сами большие картинки, а превьюшки генерить при восстановлении из бэкапа? Текстовые же данные удобнее хранить в какой-нибудь VCS.
                          0
                          Запас нужен. Проект из моей статьи только стартовал. Что будет дальше — мне сложно спрогнозировать. Но готовиться надо к худшему )) Задача была именно всё бэкапить и в случае краха немедленно развернуть на другом сервере. А генерить превьюшки и прочие манипуляции — лишние телодвижения для программистов. Меня за такое по голове не погладят. Текстовые данные удобно хранятся в git, вы правы
                            0
                            Если хочется в случае беды «немедленно развернуть рядом», то лучше для этого иметь хот-резерв сервер на который всё синхронизируется:)

                            А развёртывание такого кол-ва файлов, наверное, может быть сопоставимо по времени с разворачиванием основных и последующей генерацией динамических. Но тут спорить не стану — было бы неплохо протестировать.
                          –1
                          У нас на сервере 1.5 ТБ мелких файлов (картинки в основном). Полная синковка на новый сервер занимает 1 день. Lsync справляется на «ура». Данные актуализируются практически моментально на 4 серверах.
                          Но, на данный момент ищем и выбираем распределенную файловую систему для всего этого дела, а-ля mogilefs.
                            0
                            посмотрите на MooseFS. У меня на трех бекенд-нодах пространство на 156ТБ (свободно 16ТБ), 17млн файлов.
                            Довольно шустрая системка, а главное — стабильность при падении сервера, клиенты даже не замечают.
                        0
                        70млн файлов общим объемом, в сыром виде, 2Тб резервирую с помощью duplicity. Мне нравится.
                          0
                          емнип, натыкался на него во время поиска инструментария. От создателя rdiff-backup утилитка вроде.
                            0
                            Про авторство — не в курсе, тоже на python, бэкапит в dropbox, s3, etc не считая «стандартных». Полный бэкап — медленно, инкрементальный — быстро + решает проблему конского IO на запись мелких файла за счет сжатия и/или создания tar томов. С восстановлением, естественно, возни чуть больше.
                              0
                              А как восстановление, после большого количества инкрементальных бэкапов?
                                0
                                От числа diffов не зависит скорость восстановления, вместе с бэкапом хранится «индекс» того, в каком томе что находится. Не хочу думать, что случиться если его потерять.
                                  0
                                  Ну ок, а если окажется что «всё» равномерно распределено по всем томам?
                                    0
                                    Моя твоя не понимать.
                                    Есть «индекс» со списком файлов, их атрибутами и томом в котором файл лежит. При создании diff (инкремент) сравниваются текущее дерево и «индекс», изменившиеся файлы пишутся в новые тома, а «индекс» дополняется. При восстановлении, по «индексу» ищутся тома в которых лежит либо нужный файл, либо собирается список томов для воссоздания дерева по дате бэкапа.
                                      0
                                      Ну я и говорю про полное восстановление. Файлы равномерно распределены по томам (а не так что в последнем томе все файлы, т.к. все файлы изменились в последнем бэкапе).
                                        0
                                        А зачем в последнем томе все файлы? Там же tar сжатые, чтобы всё лежало в последнем томе нужно перепаковывать постоянно, что слишком затратно. А так вместо того, чтобы распаковывать архив таром, будете распаковывать самим duplicity, и это уже его забота, что из какого тома доставать.
                          +2
                          Самое лучшее решение задачи бекапа множества небольших файлов — размещения их на разделе с ZFS и использование zfsbackup. Работает очень быстро (цифры не дам, не снимал), позволяет удобно и быстро восстанавливать выборочно файлы из архива, когда использовался tar всё было на несколько порядков дольше.
                            0
                            Это всё здорово, но статья про Linux, а там вроде ZFS не stable. btrfs тоже, а LVM не все хотят ставить.
                              0
                              Я использовал под Linux, ZFS on FUSE, упоминания о нестабильности не видел, работало четыре года как часы.
                                +2
                                FUSE — уже значит что практически ни для него нельзя применять на production. имхо.
                                  0
                                  Пробовал в продакшене «ZFS on Linux» как основа ноды для MooseFS (для теста только на одной ноде, летом уже был как стейбл).
                                  через пару недель откатился на ext4.
                                  + пару процентов дает больше места с компрессией и включенной дедупликацией
                                  — много ест памяти
                                  — работает существенно медленнее
                                  — ест процессор больше
                                  — иногда посте ребутов были проблемы с поднятием
                              0
                              rsnapshot — есть ещё проблемка что после каждого инкрементальный бэкапа в системе будет появляться 900тыс новых файлов (inode'ов).

                              хорошо бы ещё Duplicity и DAR потестить, последний мне кажется понадёжнее и попродуманнее будет.
                                0
                                При созданиии хардлинков новые inode не создаются. Хардлинк указывает на тот же самый inode. Будут создаваться только директории для всех этих файлов.
                                  +1
                                  Что-то да, я напутал совсем. inode один и тот же, но место в директории занимают. Когда у меня были миллионы мелких файлов в rsnapshot'е, со временем бэкап стал очень медленным, пожирал много disk IO или disk cache, я это связывал именно с занимаемым место в файловой системе и кэше, а так же из-за операции rm -rf (unlink) которая постоянно выполняется над миллионами хардлинков, возможно она медленнеая на EXT4. Отказался от rsnapshot только из-за этого.
                                +2
                                Bacula не пробовали? У меня конечно объёмы не такие внушительные, но работает отлично, умеет инкрементально бэкапить.
                                  0
                                  Благодарю за статью. У самого подобная задача стоит, давно хотел провести исследование на предмет её решения.
                                    +1
                                    Для домашних целей думаю сделать банальный rsync на NILFS2. Версионность при этом будет обеспечиваться средствами NILFS2 и, что приятно, храниться будет не сконфигурированное число снапшотов N, а сколько влезает.

                                    Интересно было бы посмотреть сравнение с таким вариантом.
                                      +1
                                      Попробовал и разочаровался. Когда пришёл nilfs_cleanerd, то потёр все чекпоинты. Наверно, оставил бы чекпоинты моложе protection_period, но у меня этот параметр был выставлен в один час, не понять.

                                      Также, возможно, остались бы снапшоты, но в снапшоты у меня на тот момент ни один чекпоинт превращён не был.

                                      В общем на автоматические хранение влезающего на диск количества чекпоинтов с nilfs можно не рассчитывать. При таких раскладах, пожалуй, лучше rsync на btrfs и делать снапшоты его средствами.
                                      +1
                                      rsnapshot, написанный на C, базируется на rsync.

                                      о.О На perl он написан. Весь, целиком и полностью. пруф. (:

                                      rsnapshot — отличная вещь, но у него есть один нюанс, с которым пришлось столкнуться и немного повозиться. Если бэкапите rsnapshot'ом и процесс бэкапа, я надеюсь, работает у вас не под рутом, имейте в виду, что как только в данных появится директория без прав на запись, бэкап будет падать. Но это не баг, это фича.
                                        0
                                        Хмм, окей, исправлю. Находил где-то информацию, что на C…
                                        +2
                                        Как устроена дисковая система? где лежат данные, и куда делается бекап это одна и та-же дисковая система? судя по разницам между real и user+sys где-то есть ожидание и скорее всего это ожидание дисковых операций чтения.

                                        для чистоты эксперимента, перед каждой операцией надо echo 3 > /proc/sys/vm/drop_caches
                                          0
                                          При подобном кейсе (очень много мелких бинарных файлов) можно попробовать обойтись простым rsync-ом. Но это imho — нужно пробовать
                                            0
                                            С подключением. Минус rsnapshot'а в отсутствии сжатия. Можно попробовать решить подобную проблему посредством файловой системы с сжатием или использование архива в качестве фс, но тут встает вопрос динамически изменяемого размера бэкапа.

                                            Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

                                            Самое читаемое