Пока делал все тесты и писал статью меня так и преследовало устойчивое выражение.

Когда коту делать нефиг, он яйца лижет.
Вот так и я.

Что имеем или наша лаба.

Для тестов я буду использовать Virtualbox 7.0.4 и установленные в ней виртуальные машины.

Host PC:
Intel Core i7-10510U CPU 1.80GHz / 2.30 GHz
4 CPU (8 CPU with HT)
32GB RAM
NVME Samsung SSD 970 EVO Plus 1TB

Windows Server 2022
2 vCPU
4GB RAM
Официальная документация по настройке deduplication доступна по ссылке:
https://learn.microsoft.com/en-us/windows-server/storage/data-deduplication/install-enable

Ubuntu Server 23.04 ZFS
2 vCPU
4GB RAM
Официальная документация по настройке ZFS deduplication доступна по ссылке:
https://ubuntu.com/tutorials/setup-zfs-storage-pool#1-overview

Ubuntu Server 23.04 BTRFS
2 vCPU
4GB RAM
Официальная документация по настройке BTRFS deduplication доступна по ссылкам:
https://btrfs.readthedocs.io/en/latest/Deduplication.html
https://github.com/Zygo/bees

Zabbix Appliance 6.4.3
2 vCPU
4GB RAM
Официальная документация доступна по ссылке:
https://www.zabbix.com/download_appliance

Размер датасета:
103.50 GB (111,144,359,353 bytes)
Внутри tar.gz архивы с бэкапами сайта.

Для мониторинга и сборки метрик мы будем использовать Zabbix Appliance, поднятый там же в VirtualBox.

Все машины объединены в общую NAT network для удобства работы Zabbix и проброса портов в хост машину.

Для минимального влияния просадки производительности хост машины все тесты я проводил поочередно, то есть включенными одновременно были только VM Zabbix и текущая VM с тестом дедупликации, другие 2 VM были выключены.

Два слова про дедупликацию как таковую:

Дедупликация данных - это процесс удаления дубликатов данных в базе данных или файловой системе. Дедупликация может помочь уменьшить размер базы данных или занимаемое место на дисковом массиве.

Под дедупликацией может подразумеваться сканирование файлов в файловой системе или записей в БД с последующим удалением одинаковых значений с или без создания ссылок на оставшееся уникальное значение.

Мне же интересна реализация уменьшения размера занимаемого места на дисках путем дедуплицирования на уровне блоков файловой системы так, что для пользователя визуально ничего не меняется, как были видны файлы на диске так и остались на тех же местах, но вот вместо условных 10GB стали занимать 5Gb.

Понеслась:

Linux ZFS

Ставим нужные пакеты:

sudo apt install zfsutils-linux

Проверяем, что бинарники были установлены в наши каталоги

~$ whereis zfs
zfs: /usr/bin/zfs /usr/sbin/zfs /etc/zfs /usr/share/zfs

Теперь добавим 2 диска - для тестов я буду использовать RAID0 пул для экономии места на ПК, в реальном использовании я крайне не рекомендую использовать RAID0, особенно для критичных данных, наподобие резервных копий.

Краткая справка о том, почему RAID0 может быть опасен:

RAID 0 (Redundant Array of Independent Disks 0) - это метод объединения нескольких физических дисков в логический объем с целью повышения производительности. Данные разбиваются на блоки и параллельно записываются на все диски в массиве, что позволяет достичь более высокой скорости передачи данных.

Однако важно отметить, что RAID 0 не предоставляет отказоустойчивости. Поскольку данные не дублируются и не используется информация о четности, отказ одного из дисков в массиве приводит к недоступности всего RAID 0 и потере данных. Если один диск выходит из строя, все данные, распределенные по всем дискам, становятся непригодными к использованию.

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

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

Прежде чем добавить диски в виртуальную машину нам нужно ее отключить.

sudo shutdown now

Я добавляю 2 диска по 60 GB (объем тестового датасета чуть больше 100GB)

1.2.2 ZFS add disks to VM
1.2.2 ZFS add disks to VM
1.2.2.1 ZFS add disks to VM
1.2.2.1 ZFS add disks to VM

Вот что у нас должно получиться:

1.2.3 ZFS add disks to VM
1.2.3 ZFS add disks to VM

Включаем машину и проверяем, что добавленные диски видны в системе:

 sudo fdisk -l

Мы должны увидеть следующий вывод:

Disk /dev/sdb: 60 GiB, 64424509440 bytes, 125829120 sectors
Disk model: VBOX HARDDISK
Units: sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes

Disk /dev/sdc: 60 GiB, 64424509440 bytes, 125829120 sectors
Disk model: VBOX HARDDISK
Units: sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes

Теперь создадим ZFS пул из 2х наших дисков.

sudo zpool create dedup-pool /dev/sdb /dev/sdc

Для создания RAID1 нужно также добавить keyword “mirror”, но для достижения нужного объема тогда потребуется N*2 количество дисков/места.

Какого-либо вывода мы не получим, но введя команду:

zpool list

Мы должны увидеть наш свежесозданный пул

~$ zpool list
NAME       SIZE ALLOC FREE CKPOINT EXPANDSZ FRAG CAP DEDUP HEALTH ALTROOT
dedup-pool 119G 110K  119G  -       -       0%    0% 1.00x  ONLINE  -

Так же мы можем проверить статус нашего пула командой:

sudo zpool status

Получаем вывод:

~$ sudo zpool status
  pool: dedup-pool
 state: ONLINE
config:

        NAME        STATE     READ WRITE CKSUM
        dedup-pool  ONLINE       0     0     0
          sdb       ONLINE       0     0     0
          sdc       ONLINE       0     0     0

errors: No known data errors

Итак, ZFS пул мы создали, проверим, включена ли там дедупликация:

~$ sudo zfs get dedup dedup-pool
NAME        PROPERTY  VALUE          SOURCE
dedup-pool  dedup     off            default

По умолчанию дедупликация выключена, поэтому ее надо включить до перемещения данных на пул!

~$ sudo zfs set dedup=on dedup-pool

Немного о том, как работает дедупликация в ZFS:

ZFS выполняет дедупликацию на уровне блоков и не дедуплицирует данные, которые уже находятся на дисках.

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

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

Если вы хотите воспользоваться дедупликацией ZFS для существующих данных, то один из вариантов - скопировать данные на новую файловую систему ZFS с включенной дедупликацией, либо скопировать на временный диск и потом снова перенести в пул с включенной дедупликацией.

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

zpool iostat dedup-pool
~$ zpool iostat dedup-pool
              capacity     operations     bandwidth
pool        alloc   free   read  write   read  write
----------  -----  -----  -----  -----  -----  -----
dedup-pool   110K   119G      0      0     32  2.09K

Закидывать датасет для дедупликации я буду используя возможности VirtualBox shared folders (не забудьте установить guest tools в VM до монтирования директории).

1.5 ZFS Dataset folder mount
1.5 ZFS Dataset folder mount

Перезагружаем нашу VM и видим нужные нам директории:

$ cd /
$ ls | grep -i "ff_bak\|dedup"
dedup-pool
ff_bak

$ ls /dedup-pool/
$ sudo ls -la /ff_bak
total 221
drwxrwx---  1 root vboxsf 131072 Jun  5 16:05 .
drwxr-xr-x 21 root root     4096 Jun  9 12:25 ..
drwxrwx---  1 root vboxsf      0 Jun  5 16:04 20221116
drwxrwx---  1 root vboxsf      0 Jun  5 16:04 20221119
…

Так же проверим размер содержимого чтобы знать как оно у нас сожмется:

$ sudo du -sh /ff_bak
104G    /ff_bak
$ sudo du -s /ff_bak
108540471       /ff_bak

Копируем все содержимое из каталога /ff_bak

И сразу видим, как подскакивает нагрузка на диски

1.5.2.1 ZFS sdb disk write rate
1.5.2.1 ZFS sdb disk write rate
1.5.2.2 ZFS sdc disk write rate
1.5.2.2 ZFS sdc disk write rate

И так датасет скопирован, смотрим что у нас со статистикой zpool:

~$ zpool list
NAME        SIZE  ALLOC FREE CKPOINT EXPANDSZ FRAG CAP DEDUP HEALTH ALTROOT
dedup-pool  119G   104G  15.4G        -     -   2%  87% 1.00x  ONLINE  -

Специально для тестирования деградации скорости копирования датасета я провел первое копирование на пул когда опция дедупликации была выключена:

Данные скопировались примерно за 18 минут, а вот с дедупликацией потребовалось уже около 30:

1.6.0 ZFS sdc Disk utilization
1.6.0 ZFS sdc Disk utilization

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

1.6.1 ZFS CPU utilization full
1.6.1 ZFS CPU utilization full

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

1.6.2 ZFS Memory utilization full
1.6.2 ZFS Memory utilization full

Но вот где те самые отличия, ради которых все затевалось:

df -h показывает странную картину, ему явно неизвестно про то, как показывать дедуплицированный диск

$ df -h
Filesystem                         Size  Used Avail Use% Mounted on
dedup-pool                         189G  104G   85G  56% /dedup-pool

А вот zpool list уже показывает корректные данные:

Мы видим, что реально занято около 33 Gb дискового пространства, коэффициент дедупликации 3.39!

$ zpool list
NAME       SIZE ALLOC FREE CKPOINT EXPANDSZ FRAG  CAP DEDUP HEALTH  ALTROOT
dedup-pool 119G 30.6G 88.4G    -      -      1%   25% 3.39x  ONLINE   -

Забавно, что в Zabbix мы видим картину похожую на df -h, с другой стороны, с чего бы ей быть другой.

1.6.3 ZFS Used space
1.6.3 ZFS Used space

Но в процентах картина ближе к правде, хотя тоже врет:

1.6.4 ZFS Used space percent
1.6.4 ZFS Used space percent

Итого дедупликация на zpool дала нам следующую статистику:
Было занято 103.5 GB, стало 30.6G.

Коэффициент дедупликации: 3.39 !!!

Инфографика не вошедшая в текст:

1.7.1 ZFS sdb disk write rate full
1.7.1 ZFS sdb disk write rate full
1.7.2 ZFS sdc disk write rate full
1.7.2 ZFS sdc disk write rate full

Выводы.

ZFS действительно может неплохо сэкономить место на дисках, вопрос конечно что дешевле - оператива или hdd, ну и тип данных, который мы собираемся там “хоронить” и дедуплицировать.

В целом оно работает и даже за вполне приемлемое время.

Имхо в качестве secondary non production backup storage подойдет, как некий домашний аналог Amazon S3 Glacier.

Плюсы и минусы.

Плюсы:

  1. zpool легко настраивается.

  2. Нет каких-либо лицензионных отчислений и платных фич.

  3. Экономит огромное количество места на дисках, особенно если данные однотипные.

Минусы:

  1. Сжимаются данные, которые будут записаны в zpool только после включения дедупликации.

  2. Требуется огромное количество RAM, так как zfs и online deduplication очень требовательны к объемам оперативной памяти - на 1TB сжимаемого объема данных рекомендуется уже 5GB RAM и более (хорошая статья на тему https://constantin.glez.de/2011/07/27/zfs-to-dedupe-or-not-dedupe/).

  3. Производительность записи на диски существенно снижается.

  4. И самое главное - снижается надежность хранения данных (поврежденную файловую систему практически невозможно восстановить).

Linux BTRFS

BTRFS - позволяет нам использовать дедупликацию на одном томе, без необходимости собирать пул из нескольких дисков.

Добавляем один диск в систему объемом 120GB.

2.1.1 BTRFS add disks to VM
2.1.1 BTRFS add disks to VM
2.1.2 BTRFS add disks to VM.jpg
2.1.2 BTRFS add disks to VM.jpg

Включаем машину и проверяем, что диск виден в системе:

~$ sudo fdisk -l

Disk /dev/sdb: 120 GiB, 128849018880 bytes, 251658240 sectors
Disk model: VBOX HARDDISK
Units: sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes

Теперь необходимо создать на новом диске файловую систему, делаем это командой:

~$ sudo mkfs.btrfs /dev/sdb -L btrfs_disk

Получаем вывод с информацией о нашей файловой системе:
Скопируем к себе UUID, он нам еще пригодится.
STDOUT:

btrfs-progs v6.2
See http://btrfs.wiki.kernel.org for more information.

NOTE: several default settings have changed in version 5.15, please make sure
      this does not affect your deployments:
      - DUP for metadata (-m dup)
      - enabled no-holes (-O no-holes)
      - enabled free-space-tree (-R free-space-tree)

Label:              btrfs_disk
UUID:               22b5c24c-621f-4f19-aa27-8a756adde9c7
Node size:          16384
Sector size:        4096
Filesystem size:    120.00GiB
Block group profiles:
  Data:             single            8.00MiB
  Metadata:         DUP               1.00GiB
  System:           DUP               8.00MiB
SSD detected:       no
Zoned device:       no
Incompat features:  extref, skinny-metadata, no-holes
Runtime features:   free-space-tree
Checksum:           crc32c
Number of devices:  1
Devices:
   ID        SIZE  PATH
    1   120.00GiB  /dev/sdb

Если по какой-то причине UUID скопировать не удалось, то его можно будет узнать командой:

$ sudo blkid /dev/sdb

/dev/sdb: LABEL="btrfs_disk" UUID="22b5c24c-621f-4f19-aa27-8a756adde9c7" UUID_SUB="48d82394-3fa4-4215-98d6-2b389bc4444b" BLOCK_SIZE="4096" TYPE="btrfs"

Создаем директорию и маунтим в нее нашу btrfs.

~$ sudo mkdir /mnt/btrfs_disk
~$ sudo mount /dev/sdb /mnt/btrfs_disk

И сразу добавляем нашу файловую систему в fstab для автоматического mount:

~$ sudo vi /etc/fstab

Последней строкой добавляем:

/dev/disk/by-uuid/22b5c24c-621f-4f19-aa27-8a756adde9c7 /mnt/btrfs_disk btrfs defaults 0 0
Нельзя просто так взять и выйти из Vim
Нельзя просто так взять и выйти из Vim

Можно
Сохраняем и выходим из файла:

:wq enter

Можем сразу же применить наш фалик fstab командой:

sudo mount -a

Теперь нам нужно установить утилиту для дедупликации, BTRFS как бы поддерживает дедупликацию, но не реализует функционал из коробки.

Есть несколько вариантов, но я остановился на BEES, так как хотел проверить именно filesystem block-level deduplication.


Установка зависимостей и сборка из сорсов:

$ cd /tmp
$ sudo apt -y install git build-essential btrfs-progs markdown uuid-runtime
$ git clone https://github.com/Zygo/bees.git
$ cd ./bees && sudo make install

Далее нам необходимо поправить конфигурационный файл:

Копируем шаблон:

$ sudo cp /etc/bees/beesd.conf.sample /etc/bees/beesd.conf

Раскомментируем две строки, 9 и 34.

$ sudo vi /etc/bees/beesd.conf

Пример моего файла конфигурации:

  1 ## Config for Bees: /etc/bees/beesd.conf.sample
  2 ## https://github.com/Zygo/bees
  3 ## It's a default values, change it, if needed
  4
  5 # How to use?
  6 # Copy this file to a new file name and adjust the UUID below
  7
  8 # Which FS will be used
  9 UUID=22b5c24c-621f-4f19-aa27-8a756adde9c7
 10
 11 ## System Vars
 12 # Change carefully
 13 # WORK_DIR=/run/bees/
 14 # MNT_DIR="$WORK_DIR/mnt/$UUID"
 15 # BEESHOME="$MNT_DIR/.beeshome"
 16 # BEESSTATUS="$WORK_DIR/$UUID.status"
 17
 18 ## Options to apply, see `beesd --help` for details
 19 # OPTIONS="--strip-paths --no-timestamps"
 20
 21 ## Bees DB size
 22 # Hash Table Sizing
 23 # sHash table entries are 16 bytes each
 24 # (64-bit hash, 52-bit block number, and some metadata bits)
 25 # Each entry represents a minimum of 4K on disk.
 26 # unique data size    hash table size    average dedupe block size
 27 #     1TB                 4GB                  4K
 28 #     1TB                 1GB                 16K
 29 #     1TB               256MB                 64K
 30 #     1TB                16MB               1024K
 31 #    64TB                 1GB               1024K
 32 #
 33 # Size MUST be multiple of 128KB
 34 DB_SIZE=$((1024*1024*1024)) # 1G in bytes

Теперь нужно скопировать датасет из host машины на наш том BTRFS.

Для этого воспользуемся возможностями VirtualBox guest tools - подключим директорию с файлами к нашей VM (не забудьте установить guest tools в VM до монтирования директории).

2.5 BTRFS Dataset folder mount
2.5 BTRFS Dataset folder mount

Копируем файлы:

$ sudo cp -R /ff_bak/ /mnt/btrfs_disk/

Посмотрим статистику по файловой системе:

$ sudo btrfs filesystem df /mnt/btrfs_disk

Data, single: total=105.00GiB, used=103.51GiB
System, DUP: total=8.00MiB, used=16.00KiB
Metadata, DUP: total=1.00GiB, used=113.28MiB
GlobalReserve, single: total=112.08MiB, used=0.00B

И чуть детальнее:

$ sudo btrfs fi usage /mnt/btrfs_disk 

Overall:
    Device size:                 120.00GiB
    Device allocated:            107.02GiB
    Device unallocated:           12.98GiB
    Device missing:                  0.00B
    Device slack:                    0.00B
    Used:                        103.73GiB
    Free (estimated):             14.47GiB      (min: 7.98GiB)
    Free (statfs, df):            14.47GiB
    Data ratio:                       1.00
    Metadata ratio:                   2.00
    Global reserve:              112.08MiB      (used: 0.00B)
    Multiple profiles:                  no

Data,single: Size:105.00GiB, Used:103.51GiB (98.58%)
   /dev/sdb      105.00GiB

Metadata,DUP: Size:1.00GiB, Used:113.28MiB (11.06%)
   /dev/sdb        2.00GiB

System,DUP: Size:8.00MiB, Used:16.00KiB (0.20%)
   /dev/sdb       16.00MiB

Unallocated:
   /dev/sdb       12.98GiB

Как видим занято 103.73GiB

Каких-либо других откровений у нас там не будет, но для дальнейшего референса интересно посмотреть, как система выглядела “ДО”.

Запускаем выполнение bees командой “beesd UUID”.

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

$ sudo beesd 22b5c24c-621f-4f19-aa27-8a756adde9c7

Создание системного сервиса для работы bees в фоне (для теста не требуется):

$ sudo vi  /etc/systemd/system/beesd-22b5c24c-621f-4f19-aa27-8a756adde9c7.service

###################################################

[Unit]
Description=Deduplication service for sdb with specific UUID

[Service]
User=root
WorkingDirectory=/usr/sbin
ExecStart=/usr/sbin/beesd 22b5c24c-621f-4f19-aa27-8a756adde9c7

[Install]
WantedBy=multi-user.target

###################################################

$ systemctl daemon-reload
$ sudo systemctl start beesd-22b5c24c-621f-4f19-aa27-8a756adde9c7.service
$ sudo systemctl status --no-pager --full beesd-22b5c24c-621f-4f19-aa27-8a756adde9c7.service


###################################################

● beesd-b5f0aeba-7fc7-4ffa-958e-0c1f42adde70.service - Deduplication service for sdb with specific UUID
     Loaded: loaded (/etc/systemd/system/beesd-b5f0aeba-7fc7-4ffa-958e-0c1f42adde70.service; enabled; preset: enabled)
     Active: active (running) since Tue 2023-06-13 08:20:24 +07; 2h 8min ago
   Main PID: 545 (bees)
      Tasks: 9 (limit: 4518)
     Memory: 1.0G
        CPU: 10.977s
     CGroup: /system.slice/beesd-b5f0aeba-7fc7-4ffa-958e-0c1f42adde70.service
             └─545 /usr/lib/bees/bees /run/bees/mnt/b5f0aeba-7fc7-4ffa-958e-0c1f42adde70

Jun 13 08:23:26 home-ubuntu beesd[545]: 2023-06-13 08:23:26 545.622<7> crawl_transid: Clearing open FD cache with size 1 to enable file delete
Jun 13 08:23:26 home-ubuntu beesd[545]: 2023-06-13 08:23:26 545.622<7> crawl_transid: Clearing resolve cache with size 0
Jun 13 08:23:26 home-ubuntu beesd[545]: 2023-06-13 08:23:26 545.622<7> crawl_transid: Polling 59.9353s for next transid RateEstimator { count = 1023, raw = 2.9701 / 178.014, ratio = 2.9701 / 178.014, rate = 0.0166846, duration(1) = 59.9355, seconds_for(1) = 59.9353 }
Jun 13 08:23:26 home-ubuntu beesd[545]: 2023-06-13 08:23:26 545.617<6> crawl_new: Crawl started BeesCrawlState 5:0 offset 0x0 transid 1022..1023 started 2023-06-13-08-23-26 (0s ago)
Jun 13 08:23:26 home-ubuntu beesd[545]: 2023-06-13 08:23:26 545.617<6> crawl_new: Crawl finished BeesCrawlState 5:0 offset 0x0 transid 1022..1023 started 2023-06-13-08-23-26 (0s ago)
Jun 13 08:23:26 home-ubuntu beesd[545]: 2023-06-13 08:23:26 545.617<6> crawl_new: Crawl started BeesCrawlState 256:0 offset 0x0 transid 1022..1023 started 2023-06-13-08-23-26 (0s ago)
Jun 13 08:23:26 home-ubuntu beesd[545]: 2023-06-13 08:23:26 545.617<6> crawl_more: Crawl finished BeesCrawlState 256:258 offset 0xffffffffffff0000 transid 1022..1023 started 2023-06-13-08-23-26 (0s ago)
Jun 13 08:23:26 home-ubuntu beesd[545]: 2023-06-13 08:23:26 545.617<6> crawl_more: crawl_more ran out of data after 59.9351s

###################################################



$ systemctl enable --now beesd@22b5c24c-621f-4f19-aa27-8a756adde9c7.service

Наблюдать за ходом выполнения можно командой:

watch -n5 sudo btrfs fi usage /mnt/btrfs_disk

STDOUT:

Every 5.0s: sudo btrfs fi usage /mnt/btrfs_disk                                                   home-ubuntu: Mon Jun 12 04:23:55 2023

Overall:
    Device size:                 120.00GiB
    Device allocated:             96.02GiB
    Device unallocated:           23.98GiB
    Device missing:                  0.00B
    Device slack:                    0.00B
    Used:                         76.05GiB
    Free (estimated):             42.10GiB      (min: 30.11GiB)
    Free (statfs, df):            42.10GiB
    Data ratio:                       1.00
    Metadata ratio:                   2.00
    Global reserve:              116.25MiB      (used: 0.00B)
    Multiple profiles:                  no

Data,single: Size:94.00GiB, Used:75.88GiB (80.73%)
   /dev/sdb       94.00GiB

Metadata,DUP: Size:1.00GiB, Used:87.27MiB (8.52%)
   /dev/sdb        2.00GiB

System,DUP: Size:8.00MiB, Used:16.00KiB (0.20%)
   /dev/sdb       16.00MiB

Unallocated:
   /dev/sdb       23.98GiB

Как видим процесс пошел, место освобождается.

Идем пить кофе.

По окончании работы beesd в логе будет сообщение (при запуске в скрине или интерактивно):

2023-06-13 10:00:33 13602.13625<6> crawl_more: crawl_more ran out of data after 7461.16s

Результаты:

$ sudo btrfs fi usage /mnt/btrfs_disk
Overall:
    Device size:                 120.00GiB
    Device allocated:            108.02GiB
    Device unallocated:           11.98GiB
    Device missing:                  0.00B
    Device slack:                    0.00B
    Used:                         82.30GiB
    Free (estimated):             35.88GiB      (min: 29.89GiB)
    Free (statfs, df):            35.87GiB
    Data ratio:                       1.00
    Metadata ratio:                   2.00
    Global reserve:               97.64MiB      (used: 0.00B)
    Multiple profiles:                  no

Data,single: Size:106.01GiB, Used:82.11GiB (77.46%)
   /dev/sdb      106.01GiB

Metadata,DUP: Size:1.00GiB, Used:100.41MiB (9.81%)
   /dev/sdb        2.00GiB

System,DUP: Size:8.00MiB, Used:16.00KiB (0.20%)
   /dev/sdb       16.00MiB

Unallocated:
   /dev/sdb       11.98GiB

Весь процесс занял более 7 часов!

До дедупликации было занято 103.73GiB, после 82.30GiB, результат дедупликации всего лишь ~20.66%, учитывая, как долго проходил процесс, как-то негусто.

Инфографика:

2.7.1 BTRFS CPU utilization full
2.7.1 BTRFS CPU utilization full
2.7.2 BTRFS Memory utilization full
2.7.2 BTRFS Memory utilization full
2.7.3 BTRFS Disk utilization full
2.7.3 BTRFS Disk utilization full
2.7.4 BTRFS Disk write rate full
2.7.4 BTRFS Disk write rate full
2.7.5 BTRFS Space utilization full
2.7.5 BTRFS Space utilization full

Выводы:

На мой взгляд дедупликация в связке BTRFS с BEES оказалась не так интересна и эффективна, как на ZFS. Да, у них разные принципы, но конечный результат на ZFS как-то поинтереснее и достигается за более короткое время.

С другой стороны BTRFS как видим по метрикам не так требовательна к RAM, хотя при таких результатах сжатия объема лично для меня это уже не так важно.

Плюсы и минусы.

Плюсы:

  1. Сжимает данные, которые уже были записаны на диск.

  2. Нет каких-либо лицензионных отчислений и платных фич.

  3. Экономит некоторое количество места на дисках, особенно если данные однотипные.

  4. Не требовательна к RAM.

Минусы:

  1. BTRFS дедупликация не совсем простая в настройке.

  2. Очень требовательна к CPU, процесс дедупликации намного дольше чем на ZFS.

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

  4. Снижает надежность данных (поврежденную файловую систему практически не возможно восстановить).

Windows NTFS

Дедупликация в Windows работает к сожалению только на файловом уровне, но довольно хитро. Подробнее можно прочитать тут: https://learn.microsoft.com/en-us/windows-server/storage/data-deduplication/understand

Для того чтобы воспользоваться дедупликацией нам потребуется установить компоненты Windows Fileserver, сделать это можно через Server Manager:

3.1 NTFS Server components
3.1 NTFS Server components

Далее нам необходимо добавить диск для датасета.

Выключаем VM.

Добавляем диск.

3.2.2.1 NTFS add disk
3.2.2.1 NTFS add disk
3.2.2.2 NTFS add disk
3.2.2.2 NTFS add disk

Теперь нужно добавить диск в самой системе, включаем VM и открываем мастер управления дисками:

3.2.3.1 NTFS disk management
3.2.3.1 NTFS disk management
  1. Нас сразу встретит мастер разметки новой партиции:

Для диска с дедупликацией какой-либо разницы в разметке MBR или GPT нет, поэтому я выбрал GPT, как более современную и надежную разметку.

Из описания:

GPT (GUID Partition Table) включает в себя избыточность, храня несколько копий таблицы разделов на всем диске, что снижает риск потери данных из-за повреждения таблицы разделов.

3.2.4 NTFS GPT partition
3.2.4 NTFS GPT partition

В отличии от разметки, тип файловой системы для дедупликации очень важен, так как Windows Server реализует дедупликацию только на NTFS файловых системах (в документации есть описание что начиная с версии Windows Server 2019 дедупликация теперь поддерживается и на ReFS, протестирую это в следующий раз), создаем раздел и форматируем его в NTFS:

3.2.5 NTFS Format partition
3.2.5 NTFS Format partition

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

В отличии от ZFS, Windows делает offline дедупликацию, а значит ему все равно включим мы дедупликацию до или после помещения данных на том.

Предлагаю включить ее сейчас.

Идем в Server Manager и настраиваем дедупликацию на диске:

3.2.7.1 NTFS Setup Dedup
3.2.7.1 NTFS Setup Dedup
3.2.7.2 NTFS Dedup Shedule
3.2.7.2 NTFS Dedup Shedule

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

Для нашего теста я поставлю 0 дней и уберу расписание оптимизации нагрузки.

Закидывать датасет для дедупликации я буду используя возможности VirtualBox shared folders.

3.2.8.1 NTFS Shared folder
3.2.8.1 NTFS Shared folder

Практически сразу в системе мы увидим “сетевой диск”:

3.2.8.2 NTFS NetworkShare
3.2.8.2 NTFS NetworkShare

Копируем датасет на наш диск с дедупликацией:

3.2.9 NTFS Copy files
3.2.9 NTFS Copy files

Пока копируются файлы можно воспользоваться PS и посмотреть статус дедупликации:

PS C:\Windows\system32> Get-DedupStatus

FreeSpace    SavedSpace   OptimizedFiles     InPolicyFiles      Volume
---------    ----------   --------------     -------------      ------
94.94 GB     0 B          0                  0                  E:


PS C:\Windows\system32> Get-DedupVolume -Volume E:

Enabled            UsageType          SavedSpace           SavingsRate          Volume
-------            ---------          ----------           -----------          ------
True               Default            0 B                  0 %                  E:



PS C:\Windows\system32> Get-DedupProperties -DriveLetter E

InPolicyFilesCount        : 0
InPolicyFilesSize         : 0
OptimizedFilesCount       : 0
OptimizedFilesSavingsRate : 0
OptimizedFilesSize        : 0
SavingsRate               : 0
SavingsSize               : 0
UnoptimizedSize           : 56822681600
PSComputerName            :

Как мы видим пока что ничего не происходит.

Но мы можем “попросить” систему начать оптимизацию данных.

Выведем информацию о дедупликации по завершении копирования всех файлов:

PS C:\Windows\system32> Get-DedupStatus

FreeSpace    SavedSpace   OptimizedFiles     InPolicyFiles      Volume
---------    ----------   --------------     -------------      ------
16.38 GB     0 B          0                  0                  E:


PS C:\Windows\system32> Get-DedupVolume -Volume E:

Enabled            UsageType          SavedSpace           SavingsRate          Volume
-------            ---------          ----------           -----------          ------
True               Default            0 B                  0 %                  E:


PS C:\Windows\system32> Get-DedupProperties -DriveLetter E


InPolicyFilesCount        : 0
InPolicyFilesSize         : 0
OptimizedFilesCount       : 0
OptimizedFilesSavingsRate : 0
OptimizedFilesSize        : 0
SavingsRate               : 0
SavingsSize               : 0
UnoptimizedSize           : 111247380480
PSComputerName            :

Также можно посмотреть статус Volume в UI:

3.2.11
3.2.11

Теперь запустим процесс дедупликации, чтобы не ждать когда система сама запустит его:

Start-DedupJob -Volume E: -Type Optimization


Type   ScheduleType    StartTime      Progress     State          Volume
----   ------------    ---------      --------      -----         ------
Optimization   Manual                   0 %       Queued            E:

Можно идти пить чай.

Итак результаты:

PS C:\Windows\system32> Get-DedupStatus

FreeSpace    SavedSpace   OptimizedFiles     InPolicyFiles      Volume
---------    ----------   --------------     -------------      ------
112.85 GB    98.11 GB     377                377                E:


PS C:\Windows\system32> Get-DedupVolume -Volume E:

Enabled            UsageType          SavedSpace           SavingsRate          Volume
-------            ---------          ----------           -----------          ------
True               Default            98.11 GB             93 %                 E:


PS C:\Windows\system32> Get-DedupProperties -DriveLetter E


InPolicyFilesCount        : 377
InPolicyFilesSize         : 111144308918
OptimizedFilesCount       : 377
OptimizedFilesSavingsRate : 94
OptimizedFilesSize        : 111144308918
SavingsRate               : 93
SavingsSize               : 105341122774
UnoptimizedSize           : 112999258326
PSComputerName            :

3.2.13 NTFS disk after dedup
3.2.13 NTFS disk after dedup

Сжатие размера аж на 93%!
Занимаемое место на диске 7.13GB (вместо 103.5 GB)

Инфографика:

3.3.1 NTFS CPU utilization
3.3.1 NTFS CPU utilization

Вот тут было неожиданно для меня - дедупликация потребовала меньше RAM чем копирование файлов.

3.3.2 NTFS RAM utilization
3.3.2 NTFS RAM utilization
3.3.3 NTFS Avg Queue
3.3.3 NTFS Avg Queue
3.3.4 NTFS Disk write rate
3.3.4 NTFS Disk write rate

Выводы:

Под мой датасет винда оказалась крайне эффективна, о таком коэффициенте сжатия данных можно было только мечтать!

Ах если бы не проприетарность и прочие ограничения и нюансы Майкрософт, но нет, дедупликация от MS доступна только на Windows Server.

Плюсы и минусы:

Плюсы:

  1. Сжимает данные, которые уже были записаны на диск.

  2. Экономит огромное количество места на дисках, особенно если данные однотипные.

  3. Не требовательна к RAM.

Минусы:

  1. Функционал доступен только в платных серверных версиях Windows Server.

  2. Снижается надежность хранения данных (поврежденную файловую систему практически невозможно восстановить)

  3. Производительность системы несколько снижается при больших объемах.

До данного тестирования я много раз пробовал погрузиться в различные реализации дедупликации данных - конечно же хотелось показать поганому Майкрософту силушку Linux ZFS, BTRFS, но, как мы видим, в этот раз Microsoft таки и правда удивил, без какого-либо стеба (где мой чемодан деняг, вносите).

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

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

Холивары и срач можно туда же ;)

И напоследок - был у меня когда-то сервер на котором со временем накопился очень неплохой датасет (сжатие 98%, вместо 5.52TB занято чуть больше 100GB):

Файловая система

Объем до дедупликации

Объем после дедупликации

Коэффициент дедупликации

ZFS

103.5 GB

30.6 GB

3.39

BTRFS

103.5 GB

82.30 GiB

1.26

NTFS

103.5 GB

7.13 GB

14.51

Комментарии (23)


  1. MountainGoat
    20.06.2023 04:38
    +1

    Если, как вы сказали, это у вас бэкапы, то используйте restic или borg, и нижележащие файловые системы вообще не будут иметь влияния, хоть на FAT храните.


    1. Iliya_karin Автор
      20.06.2023 04:38
      +1

      Если брать чисто кейс с моим дата сетом, то да согласен, но мне хотелось покопаться и сравнить именно разные варианты дедупликации.
      У меня в проде был когда то массив от PureStorage, уж не знаю на базе чего у них была построена FS, но массив дедупил на лету все и вся с очень хорошими коэффициентами, я надеялся что найду +- похожее решение на Linux.


    1. trublast
      20.06.2023 04:38
      +2

      Неудобно (хоть и не критично), что нужно использовать рестик или борг, вместо того чтобы "просто скопировать файлы"

      Плюс к тому и рестик и борг делают дедуп в рамках одного бэкап-архива. Если у вас бэкап двух почти одинаковых БД, то дедупликации не будет (между ними). В случае с ФС дедеп применяется для всех записанных данных.

      С другой стороны в borg/restic дедупликация со сжатием может более эффективная, чем дедупликация сжатых файлов на ФС. Так же при бэкапе боргом по сети сначала производится дедупликация (подсчет хэшей блоков), а потом передача измененных блоков данных. Это может быть полезно, так меньше нагружает сеть, она не будет узким местом при терабайтных бэкапах повторяющихся данных, ведь на удаленный сервер будут переданы только изменённые блоки.


  1. 13werwolf13
    20.06.2023 04:38
    +5

    позволю себе вставить свои 5 копеек по поводу bees, а то глаз дёргается когда так пишут systemd юниты.
    1) у systemd для сервисов могут быть аргументы (передаются через @ при вызове сервиса) не обязательно создавать свой юнит для каждого uuid
    2) не указан type юнита
    3) учитывая что такой сервис может знатно так нагрузить систему неплохо было бы его ограничить по ресурсам

    с этими мыслями я пошёл посмотреть а что предлагается вместе с пакетом:

    довольно большая портянка
    [werwolf@power] ~  
    ❯ rpm -ql bees | grep systemd                                                                                                                                                                                                                                              ⏎
    /usr/lib/systemd/system/beesd@.service
    
    [werwolf@power] ~  
    ❯ systemctl cat beesd@.service             
    # /usr/lib/systemd/system/beesd@.service
    [Unit]
    Description=Bees (%i)
    Documentation=https://github.com/Zygo/bees
    After=sysinit.target
    
    [Service]
    Type=simple
    ExecStart=/usr/sbin/beesd --no-timestamps %i
    CPUAccounting=true
    CPUSchedulingPolicy=batch
    CPUWeight=12
    IOSchedulingClass=idle
    IOSchedulingPriority=7
    IOWeight=10
    KillMode=control-group
    KillSignal=SIGTERM
    MemoryAccounting=true
    Nice=19
    Restart=on-abnormal
    RuntimeDirectory=bees
    StartupCPUWeight=25
    StartupIOWeight=25
    
    # Hide other users' process in /proc/
    ProtectProc=invisible
    
    # Mount / as read-only
    ProtectSystem=strict
    
    # Forbidden access to /home, /root and /run/user
    ProtectHome=true
    
    # Mount tmpfs on /tmp/ and /var/tmp/.
    # Cannot mount at /run/ or /var/run/ for they are used by systemd.
    PrivateTmp=true
    
    # Disable network access
    PrivateNetwork=true
    
    # Use private IPC namespace, utc namespace
    PrivateIPC=true
    ProtectHostname=true
    
    # Disable write access to kernel variables throug /proc
    ProtectKernelTunables=true
    
    # Disable access to control groups
    ProtectControlGroups=true
    
    # Set capabilities of the new program
    # The first three are required for accessing any file on the mounted filesystem.
    # The last one is required for mounting the filesystem.
    AmbientCapabilities=CAP_DAC_OVERRIDE CAP_DAC_READ_SEARCH CAP_FOWNER CAP_SYS_ADMIN
    
    # With NoNewPrivileges, running sudo cannot gain any new privilege
    NoNewPrivileges=true
    
    [Install]
    WantedBy=basic.target

    не скажу что в этом юните 100% то что написал бы я, но мейнтейнерам виднее

    к своему удивлению не нашёл в пакете .timer юнита для этого .service юнита но это уже лирика.


    1. Iliya_karin Автор
      20.06.2023 04:38
      +2

      Спасибо за технический комментарий.
      Я привел пример минимального рабочего варианта, честно, статья отняла сильно больше сил и времени чем я планировал, поэтому пример сервиса/юнита получился довольно кривой, я старался сфокусироваться больше на теме стати и результатах.
      Хотел еще подтащить OpenDedup, но с ним по быстрому не вышло, да и так портянка получилась не маленькая.


  1. selivanov_pavel
    20.06.2023 04:38
    +1

    Сравнивалась работа блочной и файловой дедупликации, что не совсем корректно. Хорошо бы тогда под линукс какой-нибудь sdfs или lessfs протестировать, они на файловом уровне работают.


    1. Iliya_karin Автор
      20.06.2023 04:38

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


    1. khajiit
      20.06.2023 04:38
      +3

      Ну, судя по документации, реализация файловой дедупликации на NTFS — та же блочная, вид сбоку. Напоминает разбиение рекордов в ZFS, только offline и с постобработкой.
      На ZFS, фактически, тестировалось совпадение попаданий кусков немного отличающихся файлов в txg, так как recordsize не ограничивался.


      Проблемы с дедупликацией tar-архивов заметили в Proxmox еще несколько лет назад — и запилили pxar и использующий его pbs, что позволяет достигать коэффициента дедупликации за 100.
      Ну и для дедупликации интереснее было бы передавать не архив а его содержимое, там результаты были бы наверняка веселее.
      Опять же, для дедуплицированных блоков NTFS, судя по документации, может использовать компрессию — ZFS умеет zstd, который не включался.
      Опять же, ZFS позволет дедуплицировать диски работающих ВМ, а не просто какие-то архивы.
      Ремарки про невозможность восстановления ФС тоже требуют раскрытия, особенно для ZFS с ее тремя копиями метаданных.


      В общем, модель сферического коня в вакууме вызывает сплошные вопросы…


  1. select26
    20.06.2023 04:38
    +5

    Вот ради таких стаей стоит заходить на Хабр!
    Cпасибо большое за такое детальное исследование и подробное описание результатов! Очень полезно и читается приятно и легко.


    1. Iliya_karin Автор
      20.06.2023 04:38

      Спасибо!


  1. AntonVirtual
    20.06.2023 04:38

    Интересно, сколько нужно времени, чтобы "обнаружить" специализированные дедуплицирующие СХД для резервного копирования типа DataDomain или StoreOnce?

    Или например СРК (системы резервного копирования) с встроенной дедупликацией, как например Veeam?


    1. Iliya_karin Автор
      20.06.2023 04:38

      Не совсем понимаю как трактовать ваш комментарий, если возможно ответьте более развернуто.


    1. Vasily_Pechersky
      20.06.2023 04:38

      DataDomain — это отличная дедупликация но очень дорого.
      Veeam — его эффективность достаточно хромает.
      Есть ещё Avamar — это тоже не дёшево но достаточно геморно в настройке.


      Хотя! DataDomain есть community edition — 600 gb (до дедупликации) бесплатно.


      1. AntonVirtual
        20.06.2023 04:38

        А вы данные свои оценивали, чтобы можно было утверждать что продукт Х дорого / не дорого?


        1. Vasily_Pechersky
          20.06.2023 04:38

          Всё можно поставить под сомнение. Зависит от позиционирования.
          $500 за 1 тб сырой ёмкости для виртуалки для разных бизнесов могут быть и много и мало.


          1. AntonVirtual
            20.06.2023 04:38

            Еще ни один человек, который огульно судит "это дорого, это слишком дорого" не показал мне экономических расчетов стоимости простоев или потери данных.

            Но мнение имеет.
            За 10 с лишним лет.


  1. Vasily_Pechersky
    20.06.2023 04:38

    Илья, я был бы очень рад если бы вы написали от тестировании VDO (Идут в комплекте со всеми RedHat компиляциями). В настройке сложнее ZFS но много проще BTRFS. Производительность в моих экспериментах была не высокой, но я тестировал на Centos 7. Может современные версии шустрее ?


    1. Iliya_karin Автор
      20.06.2023 04:38

      Возьму на заметку.
      Интересно будет написать вторую часть статьи, когда найду в себе силы.
      Может быть как раз добавить VDO, sdfs, lessfs.


  1. Nikitin
    20.06.2023 04:38
    +1

    Кстати, под винду есть утилита https://dupecare.com которая делает дедупликацию с компрессией для отдельной папки


    1. Merlin5004
      20.06.2023 04:38

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


  1. zzzzzzzzzzzz
    20.06.2023 04:38

    Как по мне, в статье очень не хватает краткого сравнения принципов дедупликации. Уж слишком удивляет такая разница в результатах. Может, где-то надо настройки подкрутить, или ещё что-то. Очень подозрительно.

    Также любопытно, насколько дедупликация замедляет доступ к файлам. Но тут всё очень от железа должно зависеть, поэтому чужой эксперимент - не показатель.


    1. Iliya_karin Автор
      20.06.2023 04:38

      Для сравнения алгоритмов дедупликации пришлось бы провести полноценное исследование, включая сорсы zfs/btrfs bees, для подобной статьи потребуется затратить около 2х месяцев по вечерам и выходным, в данный момент я к сожалению не располагаю таким ресурсом.

      Да и в целом мне хотелось сравнить и показать "а чё там из коробки доступно?"

      Большинство пользователей или ИТ специалистов не хотят тратить дни и недели на допиливание кода и сборку на арчах под свои нужды.

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


      1. khajiit
        20.06.2023 04:38
        +3

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


        • поиск по полному совпадению (медленно)
        • поиск по хешу (очевидно, тут все отличия будут в хеш-функции)
        • поиск по хешу И проверка на полное совпадение (можно форсировать в ZFS)

        Интерфейс доступа к хралищу (ФС) может оперировать как базовыми блоками так и цепочками блоков (экстентами, рекордами, etc).
        Архив, в отличие от, можно рассматривать как поток обработанных (или нет) данных, разделенных промежутками с метаданными самого архива — оглавлением, таблицой смещений, словарем и т.д., где минимальное смещение/длина данных принципиально снизу не ограничены (обычно кратны байту или машинному слову).
        Соответственно:


        • смещение файла внутри архива не обязано совпадать с началом блока хранилища
        • последовательность блоков контента внутри архива может иметь разные по величине гапы

        Из чего есть два следствия:


        • на хранилище соседние архивы могут лечь совершенно разными последовательностями байт — как следствие, дедуплицировать там будет нечего, блоки будут уникальными
        • при определенных условиях (архив не сильно меняющегося набора файлов в формате, хранящем сильно меняющиеся части в конце архива) можно будет получить достаточно пригодный для дедупликации архив. При этом эффективность дедупликации будет зависеть от размера блока (что и показало ваше тестирование, у NTFS размер блока — 4кБ по дефолту)

        Если вы хотите получить высокий коэффициент дедупликации — ваши данные должны быть к ней пригодны. Это значит:


        • или сырые, без контейнеров (архивов), файлы
        • или вам нужен специальный формат контейнера, который вы можете подтюнить под особенности хранилища (вроде уже упомянутого pxar)
        • или (если вы дедуплицируете ФС) размеры экстентов/рекодов/etc дочерних ФС должны соответсвовать размеру блока хранилища