Upgrade FreeBSD 8.3 et ZFS v28

FreeBSD 8.3 vient d’arriver, et en plus des traditionnels correctifs de sécurité, le support de ZFS v28 est annoncé. Une bonne occasion pour mettre à jour mon NAS.

Procédure d’upgrade

Rien de particulier ici, la procédure est classique :

# freebsd-update upgrade -r 8.3-RC2
# freebsd-update install
# shutdown -r now
# freebsd-update install
# shutdown -r now

Pour les diverses propositions de modification des fichiers de configuration, un merge a fonctionné sans problème.

J’en profite pour recompiler mon kernel, après avoir vérifié que les options contenues dans /etc/make.conf sont correctes :

CPUTYPE?=native
MAKEOPTS="-j2"

Puis :

# cd /usr/src
# make buildkernel
# make installkernel
# shutdown -r now

Upgrade ZFS

Une fois la machine démarrée avec le nouveau kernel, le pool ZFS peut être upgradé, comme indiqué dans le zpool status :

# zpool status
  pool: data
 state: ONLINE
status: The pool is formatted using an older on-disk format.  The pool can
    still be used, but some features are unavailable.
action: Upgrade the pool using 'zpool upgrade'.  Once this is done, the
    pool will no longer be accessible on older software versions.
  scan: scrub repaired 0 in 1h34m with 0 errors on Mon Apr 23 18:28:43 2012
config:

    NAME        STATE     READ WRITE CKSUM
    data        ONLINE       0     0     0
      raidz1-0  ONLINE       0     0     0
        ad4     ONLINE       0     0     0
        ad6     ONLINE       0     0     0
        ad8     ONLINE       0     0     0
        ad10    ONLINE       0     0     0

errors: No known data errors

Le pool est ici en ZFS v15 :

# zpool get version data
NAME  PROPERTY  VALUE    SOURCE
data  version   15       local

Et le système supporte bien ZFS v28 :

# zpool upgrade -v
This system is currently running ZFS pool version 28.

L’upgrade se fait en moins de 2 secondes et est transparante, mais elle empêche de pouvoir utiliser ce pool avec une version du système plus ancienne :

# zpool upgrade data
This system is currently running ZFS pool version 28.

Successfully upgraded 'data' from version 15 to version 28

Déduplication

L’intérêt de cette montée de version et de pouvoir utiliser, ou au moins tester, la déduplication intégrée à ZFS (ainsi que le très utile zfs diff [6]).
La déduplication évite les doublons directement au niveau des blocs [1], permettant ainsi une économie d’espace disque. Mais cela implique le référencement des blocs dédupliqués, nécessitant plus ou moins de ressources mémoire supplémentaires [2].

Une estimation de la RAM requise peut être faite à partir des données déjà présentes dans le pool [3] avec zdb -S <pool_name> :

# zdb -S data
Simulated DDT histogram:

bucket              allocated                       referenced          
______   ______________________________   ______________________________
refcnt   blocks   LSIZE   PSIZE   DSIZE   blocks   LSIZE   PSIZE   DSIZE
------   ------   -----   -----   -----   ------   -----   -----   -----
     1    9.34M   1.14T   1.14T   1.13T    9.34M   1.14T   1.14T   1.13T
     2     198K   22.3G   22.3G   22.3G     409K   45.7G   45.7G   45.6G
     4    9.05K    815M    815M    815M    45.8K   4.08G   4.08G   4.08G
     8    1.28K   80.9M   80.9M   81.0M    12.9K    817M    817M    818M
    16      323   7.62M   7.62M   7.69M    6.81K    155M    155M    156M
    32       61   1.40M   1.40M   1.41M    2.57K   65.8M   65.8M   66.4M
    64       45    581K    581K    595K    3.35K   46.5M   46.5M   47.5M
   128        3    138K    138K    138K      620   28.0M   28.0M   28.0M
   256        4     51K     51K   51.6K    1.43K   18.1M   18.1M   18.3M
    1K        1    128K    128K    128K    1.37K    176M    176M    176M
   64K        1    128K    128K    128K    79.5K   9.94G   9.94G   9.93G
 Total    9.55M   1.16T   1.16T   1.16T    9.89M   1.19T   1.19T   1.19T

dedup = 1.03, compress = 1.00, copies = 1.00, dedup * compress / copies = 1.03

Dans mon cas, la déduplication concernerait 9,55M de blocs au total, et chaque bloc dédupliqué demande environ 320 octets de mémoire. On a donc : 9,55M*320 = 3056Mo requis par la déduplication. Cette mémoire est allouée avec les metadatas de l’ARC [4], et cette zone ne peut dépasser un quart de l’allocation mémoire de l’ARC.

Dans mon cas, les limites sont les suivantes :

$ sysctl vfs.zfs.arc_max
vfs.zfs.arc_max: 3029393408
$ sysctl vfs.zfs.arc_meta_limit
vfs.zfs.arc_meta_limit: 757348352

L’ARC est ici limité à un peu moins de 3 Go, et la partie réservée aux metadatas ne peut dépasser 722 Mo.

D’après les informations données par la table de déduplication simulée, la déduplication de toutes mes données consommerait quatre fois plus de RAM que l’espace reservé. il m’est donc impossible de mettre en place la déduplication sur une telle quantité de données, sans dégrader fortement les performances.
Il est en effet nécessaire que la table de dédiplication puisse être contenue en mémoire pour éviter de ralentir considérablement les accès disque.

Pour mesurer l’utilisation de la mémoire par ZFS, les scripts arc_summary.pl et arcstat.pl sont disponibles sur cette page.

Tests

Pour comparer un peu les performances de la déduplication et des différentes méthodes de compression intégrées dans ZFS, j’ai copié des données représentant plus ou moins mon utilisation disque (images ISO de divers OS, machines virtuelles, ou encore vidéos des aventures du service de Ron Swanson).

  • Copie de référence sans compression particulière ni déduplication : 24min31s
data/tdedup                 98.8G  1.38T  98.8G  /data/tdedup
  • Avec la déduplication : 50min00s
# zfs set deduplication=on data/tdedup

L’espace disque économisé et la mémoire consommée peuvent être obtenus avec la commande zpool -D ou zpool -DD :

     dedup: DDT entries 771751, size 326 on disk, 163 in core

    bucket              allocated                       referenced         
    ______   ______________________________   ______________________________
    refcnt   blocks   LSIZE   PSIZE   DSIZE   blocks   LSIZE   PSIZE   DSIZE
    ------   ------   -----   -----   -----   ------   -----   -----   -----
         1     735K   91.8G   91.8G   91.7G     735K   91.8G   91.8G   91.7G
         2    18.4K   2.30G   2.30G   2.30G    38.3K   4.79G   4.79G   4.79G
         4      741   92.6M   92.6M   92.6M    3.06K    392M    392M    391M
         8       26   3.25M   3.25M   3.25M      270   33.8M   33.8M   33.7M
        16        5    640K    640K    640K      101   12.6M   12.6M   12.6M
        32        4    512K    512K    512K      176     22M     22M   22.0M
        64        4    512K    512K    512K      382   47.8M   47.8M   47.7M
        8K        1    128K    128K    128K      14K   1.75G   1.75G   1.75G
     Total     754K   94.2G   94.2G   94.1G     791K   98.8G   98.8G   98.8G

Ici, on peut voir que 98,8 Go sont référencés, pour 94,2 Go réellement alloués (ratio : 1.05).
La consommation mémoire est de 754K*320 = 230 Mo.

  • Avec la compression ZLE : 22min46s
# zfs set compression=zle data/tdedup
# zfs get compressratio data/tdedup
NAME         PROPERTY       VALUE  SOURCE
data/tdedup  compressratio  1.09x  -
  • Avec la compression LZJB : 25min57s
# zfs set compression=lzjb data/tdedup
# zfs get compressratio data/tdedup
NAME         PROPERTY       VALUE  SOURCE
data/tdedup  compressratio  1.42x  -
  • Avec la compression gzip : 55min14s
# zfs set compression=gzip data/tdedup
# zfs get compressratio data/tdedup
NAME         PROPERTY       VALUE  SOURCE
data/tdedup  compressratio  1.65x  -

Au final, la déduplication n’est pas du tout avantageuse avec les données testées. L’espace économisé est plus faible qu’avec n’importe quelle compression, alors que le temps pris pour la copie a doublé. S’ajoute à cela la mémoire consommée, qui ne peut plus être utilisée pour le cache ZFS.

Concernant les différentes compressions disponibles, LZJB permet d’obtenir d’assez bons résultats, c’est d’ailleurs la compression ZFS par défaut.

Annexes

Plus ou moins lié au sujet abordé :

  • Sous FreeBSD, un cp -r /source/path/.* /dest/path copie également le contenu du répertoire parent “..”. Pour éviter cela, utiliser cp -r /source/path/.[^.]* [5].
  • Pour mesurer le débit global du pool ZFS : zpool iostat 1


[1] : https://blogs.oracle.com/bonwick/entry/zfs_dedup
[2] : http://www.oracle.com/technetwork/articles/servers-storage-admin/o11-113-size-zfs-dedup-1354231.html
[3] : http://docs.oracle.com/cd/E23824_01/html/E24456/filesystem-6.html#fsdedup-1
[4] : http://hub.opensolaris.org/bin/view/Community+Group+zfs/dedup#HWhataretheperformanceimpactsofenablingdedup3F
[5] : http://forums.freebsd.org/showthread.php?t=18385
[6] : http://docs.oracle.com/cd/E19963-01/html/821-1448/gbscy.html#gkhwe