netatalk パフォーマンスチューニング

WD30EFRX を買った話 の続き。今回は netatalk のパフォーマンスチューニングです。

簡単に済ませるつもりだったのですが、バックエンドのファイルシステムの比較にまで手を出したらえらく時間がかかってしまいました。まだまだ試したいことがあるのですがきりがないので、このあたりでまとめておこうと思います。

なお netatalk はバージョン 3.0.1 が最新版ですが Debian パッケージ を使っている都合で 2.2.2 による検証となっています。

問題

これまで HP MicroServer で運用してきた netatalk 2.2.2 の AFP ボリュームでは、 iPhotoAperture のフォトライブラリを置いて使うととにかく遅くて話にならないということが問題になっており、今回のストレージ更新ではこれの改善が目標のひとつです。

調べてみると netatalk の cnid_dbd というデーモンプロセスがボトルネックとなっているらしいことがわかります。このデーモンは AFP ボリュームの全ファイル・ディレクトリに CNID という一意の数値を割り当てるため、 Berkeley DB の環境を各 AFP ボリュームの .AppleDB というディレクトリに作り、ファイル・ディレクトリの更新があると同時にこの DB も更新しているのでした。

小さなファイル・ディレクトリが多くメタデータの更新が大量にあるようなワークロードでは、本来のファイル・ディレクトリ更新の間に cnid_dbd の fdatasync() が挟まることになります。 AFP ボリュームと CNID DB が同じデバイスに存在する場合、これはひどいパフォーマンスの低下をもたらします。そこでこれらを異なるデバイスに分離するだけで、相当な速度改善が望めるのではと考え、実験してみました。

またついでなので、ファイルシステムについても従来の XFS と EXT4 でどれだけの違いが出るかテストしてみることにしました。

システム

今回の実験に使った OSX マシンは次のようなものです。

netatalk のサーバはいつもの microserver で OpenVZ コンテナとして動かします。

準備

まず microserver に 2 台の WD30EFRX を接続し mdadm で最初からデバイスがひとつ欠けた RAID1 ボリュームを 2 つ作成しました。それぞれを XFS と EXT4 で mkfs してマウントします。

microserver# mdadm -C /dev/md/m00 -n2 -l1 /dev/sdd missing
microserver# mdadm -C /dev/md/m01 -n2 -l1 /dev/sde missing
microserver# cat /proc/mdstat
Personalities : [raid1] 
md125 : active raid1 sdd[0]
      2930265424 blocks super 1.2 [2/1] [U_]
      
md126 : active raid1 sde[0]
      2930265424 blocks super 1.2 [2/1] [U_]
microserver# mkfs -t xfs -s size=4096 /dev/md/m00
microserver# mkfs -t ext4 /dev/md/m01
microserver# mkdir -p /vol/m00 /vol/m01
microserver# mount /dev/md/m00 /vol/m00
microserver# mount /dev/md/m01 /vol/m01 -o user_xattr

それぞれのボリュームに従来の環境から移行予定のデータ 1.5TB ほどを事前にコピーしておきます。パフォーマンスが悪いほうのボリュームを初期化しもう片方の RAID1 に追加する計画です。

Debian wheezy amd64 の OpenVZ コンテナを用意し netatalk 2.2.2-1 をインストールします。 mkfs したふたつのボリュームを bind mount により /vol/m00 (XFS) および /vol/m01 (EXT4) としてコンテナ内から直接アクセスできるようにしました。

netatalk コンテナ内で cnid_dbd が使う CNID DB 用のディレクトリを用意し /tmp/CNID/ には tmpfs をマウントしておきます。

netatalk# mkdir -p /var/lib/netatalk/CNID /tmp/CNID
netatalk# mount -t tmpfs tmpfs /tmp/CNID

最終的には次のような状況となりました。システムの simfs は OpenVZ ホストのシステム HDD (HGST HDP725050GLA360) が割り当てられています。

netatalk# cat /proc/mounts 
...
/dev/simfs / simfs rw,relatime 0 0
/dev/md126 /vol/m00 xfs rw,relatime,attr2,noquota 0 0
/dev/md125 /vol/m01 ext4 rw,relatime,errors=remount-ro,user_xattr,barrier=1,nodelalloc,data=ordered 0 0
tmpfs /tmp/CNID tmpfs rw,relatime 0 0

netatalk は次のような AppleVolumes でテストしました。

:DEFAULT: options:upriv,usedots dperm:2770 fperm:0660 umask:007
/vol/m00/afp-xfs-default "afp-xfs-default"
/vol/m00/afp-xfs-var "afp-xfs-var" dbpath:/var/lib/netatalk/CNID/$v
/vol/m00/afp-xfs-tmp "afp-xfs-tmp" dbpath:/tmp/CNID/$v
/vol/m01/afp-ext4-default "afp-ext4-default"
/vol/m01/afp-ext4-var "afp-ext4-var" dbpath:/var/lib/netatalk/CNID/$v
/vol/m01/afp-ext4-tmp "afp-ext4-tmp" dbpath:/tmp/CNID/$v

ファイルシステム 2 種類 (xfs, ext4) と CNID DB 設定 (dbpath) 3 種類の組み合わせで計 6 つの AFP ボリュームを設定しています。 CNID DB 設定の説明は次のとおりです。

これらのボリュームのためのディレクトリを作成しパーミッションを適切に設定しておきます。

netatalk# mkdir -p /vol/m00/afp-xfs-default
netatalk# mkdir -p /vol/m00/afp-xfs-var
netatalk# mkdir -p /vol/m00/afp-xfs-tmp
netatalk# mkdir -p /vol/m01/afp-ext4-default
netatalk# mkdir -p /vol/m01/afp-ext4-var
netatalk# mkdir -p /vol/m01/afp-ext4-tmp
netatalk# chown yaegashi.yaegashi /vol/m0*/afp-*
netatalk# chmod 2775 /vol/m0*/afp-*

netatalk をスタートしたあとに OSX 側からマウントしてみて、それぞれのボリュームに対する CNID DB の Berkeley DB 環境が .AppleDB ディレクトリで適切に初期化されていることを確認します。

netatalk# /etc/init.d/netatalk start
Starting Netatalk services (this will take a while):  cnid_metad afpd.
netatalk# ls -l /tmp/CNID/afp-xfs-tmp/.AppleDB/
合計 6396
-rw-r----- 1 root root    24576  1月  2 23:15 __db.001
-rw-r----- 1 root root   647168  1月  2 23:15 __db.002
-rw-r----- 1 root root 10493952  1月  2 23:15 __db.003
-rw-r----- 1 root root   163840  1月  2 23:15 __db.004
-rw-r----- 1 root root  9158656  1月  2 23:15 __db.005
-rw-r----- 1 root root    40960  1月  2 23:15 __db.006
-rw-r--r-- 1 root root    16384  1月  2 23:15 cnid2.db
-rw-r--r-- 1 root root       19  1月  2 23:15 db_errlog
-rw-r--r-- 1 root root        0  1月  2 23:15 lock
-rw-r----- 1 root root 10485760  1月  2 23:15 log.0000000001

OSX 側で df するとこのようになりました。

osx% df
Filesystem                                                  512-blocks       Used  Available Capacity   iused      ifree %iused  Mounted on
...
//yaegashi@netatalk._afpovertcp._tcp.local/afp-ext4-default 5475541352 2689097752 2786443600    50% 336137217  348305450   49%   /Volumes/afp-ext4-default
//yaegashi@netatalk._afpovertcp._tcp.local/afp-ext4-tmp     5475541352 2689097752 2786443600    50% 336137217  348305450   49%   /Volumes/afp-ext4-tmp
//yaegashi@netatalk._afpovertcp._tcp.local/afp-ext4-var     5475541352 2689097752 2786443600    50% 336137217  348305450   49%   /Volumes/afp-ext4-var
//yaegashi@netatalk._afpovertcp._tcp.local/afp-xfs-default  5857669264 2738758232 3118911032    47% 342344777  389863879   47%   /Volumes/afp-xfs-default
//yaegashi@netatalk._afpovertcp._tcp.local/afp-xfs-tmp      5857669264 2738758232 3118911032    47% 342344777  389863879   47%   /Volumes/afp-xfs-tmp
//yaegashi@netatalk._afpovertcp._tcp.local/afp-xfs-var      5857669264 2738758232 3118911032    47% 342344777  389863879   47%   /Volumes/afp-xfs-var

bonnie++ on netatalk

最初に XFS と EXT4 の基本的な性能差を見るため netatalk コンテナで bonnie++ を走らせてみました。

bonnie++

マウントオプションをいじるともう少し速度を向上させることができるのですが、今回のテストはこれをベースに行いました。

ファイルコピー(小)

最初に OSX マシンからのファイル・ディレクトリコピーのテストをします。

テストに使うのは RX100.aplibrary という Aperture のフォトライブラリです。総容量 8GB でファイル・ディレクトリの数が約 10,000 あり、サイズの小さなファイルもかなり多いツリーになっています。

osx% du -hs RX100.aplibrary
7.9G	RX100.aplibrary
osx% find RX100.aplibrary | wc -l
   10027

これを順番に各 AFP ボリュームのマウントポイントに rsync してかかる時間を計測します。次のようなスクリプトを走らせました。

#!/bin/sh -x
for i in xfs ext4; do
  for j in default tmp var; do
    time rsync -avP RX100.aplibrary /Volumes/afp-$i-$j
  done
done

結果:

ファイルコピー(小) 経過時間(s) 速度(MB/s)
afp-xfs-default 1566.561 5.485
afp-xfs-tmp 673.519 13.964
afp-xfs-var 911.855 9.548
afp-ext4-default 1069.797 7.980
afp-ext4-tmp 347.289 25.583
afp-ext4-var 696.824 12.334

ファイルコピー(大)

今度は次のようにして作った 100MB から 1GB まで 10 個のファイルを rsync でコピーしてみます。

osx% mkdir images
osx% for i in $(seq 10); do dd if=/dev/urandom of=images/$i.img bs=100000000 count=$i; done
osx% du -h images
5.1G	images

結果:

ファイルコピー(大) 経過時間(s) 速度(MB/s)
afp-xfs-default 107.161 50.697
afp-xfs-tmp 103.374 53.147
afp-xfs-var 104.323 52.638
afp-ext4-default 115.430 47.216
afp-ext4-tmp 119.226 46.030
afp-ext4-var 200.626 45.273

ファイル削除

最後に、これまでコピーしたファイルを OSX から削除してみます。これも単純に次のようなスクリプトを走らせて rm -rf にかかる時間を計測します。

#!/bin/sh -x
for i in xfs ext4; do
  for j in default tmp var; do
    time rm -rf /Volumes/afp-$i-$j/*
  done
done

結果:

ファイル削除 経過時間(s)
afp-xfs-default 632.086
afp-xfs-tmp 129.763
afp-xfs-var 229.681
afp-ext4-default 298.121
afp-ext4-tmp 47.351
afp-ext4-var 202.275

bonnie++ 1.96

OSX 側のベンチマークソフトとして bonnie++ を走らせてみたかったのですが、 OSX の bonnie++ 1.96 は AFP ボリュームでは途中でこけて動きませんでした。

osx% bonnie++ -d /Volumes/afp-xfs-default
Writing a byte at a time...done
Writing intelligently...done
Rewriting...done
Reading a byte at a time...done
Reading intelligently...done
start 'em...done...done...done...done...done...
Create files in sequential order...done.
Stat files in sequential order...done.
Delete files in sequential order...Bonnie: drastic I/O error (rmdir): Directory not empty
Cleaning up test directory after error.

検索すると似たような報告がちらほら見つかるので bonnie++ のバグかもしれませんが、今回は深追いせずあきらめました。

PostMark 1.51

PostMark は小さなファイル・ディレクトリを大量に作成・削除するフリーなベンチマークソフトで、これは Web サーバとかメールサーバなどのワークロードを再現しているそうです。 Homebrew でインストールできます。

次のようなファイル postmark.cfg を用意して実行してみました。

set number 5000
set transactions 5000
set buffering false
set location /Volumes/afp-ext4-default
show
run
set location /Volumes/afp-ext4-tmp
show
run
set location /Volumes/afp-ext4-var
show
run
set location /Volumes/afp-xfs-default
show
run
set location /Volumes/afp-xfs-tmp
show
run
set location /Volumes/afp-xfs-var
show
run
osx% postmark postmark.cfg
PostMark v1.51 : 8/14/01
Reading configuration from file 'postmark.cfg'
Current configuration is:
The base number of files is 5000
Transactions: 5000
Files range between 500 bytes and 9.77 kilobytes in size
Working directory: 
        /Volumes/afp-ext4-default (weight=1)
Block sizes are: read=512 bytes, write=512 bytes
Biases are: read/append=5, create/delete=5
Not using Unix buffered file I/O
Random number generator seed is 42
Report format is verbose.
Creating files...Done
Performing transactions..........Done
Deleting files...Done
Time:
        508 seconds total
        193 seconds of transactions (25 per second)

Files:
        7454 created (14 per second)
                Creation alone: 5000 files (27 per second)
                Mixed with transactions: 2454 files (12 per second)
        2524 read (13 per second)
        2476 appended (12 per second)
        7454 deleted (14 per second)
                Deletion alone: 4908 files (37 per second)
                Mixed with transactions: 2546 files (13 per second)

Data:
        13.52 megabytes read (27.26 kilobytes per second)
        42.12 megabytes written (84.91 kilobytes per second)

結果:

PostMark 1.51 txn(/s) create(/s) read(/s) append(/s) delete(/s)
afp-xfs-default 10 6 5 5 6
afp-xfs-tmp 33 16 16 16 16
afp-xfs-var 20 12 10 10 12
afp-ext4-default 25 14 13 12 14
afp-ext4-tmp 119 82 60 58 82
afp-ext4-var 40 25 20 20 25

Blackmagic Disk Speed Test 2.2

Blackmagic Disk Speed Test は映像系のストレージ性能の計測に特化した無料のベンチマークソフトで、 AFP ボリュームで 1000BASE-T のワイヤースピードに迫る値が出せるありがたいソフトです。 Mac App Store からインストールできます。

結果:

Disk Speed Test 2.2 WRITE (MB/s) READ (MB/s)
afp-xfs-default 103.5 109.7
afp-xfs-tmp 105.1 109.6
afp-xfs-var 105.5 110.0
afp-ext4-default 88.5 108.0
afp-ext4-tmp 88.4 108.3
afp-ext4-var 87.3 107.6

まとめ

netatalk において AFP ボリュームと CNID DB (.AppleDB ディレクトリ) を別デバイスに分けることでメタデータ更新性能が大きく向上することがわかりました。

CNID DB を tmpfs (RAM ディスク) に置くと非常に高速になりますが、システムが再起動したときに消えてしまうので、現実的には /var のような普通のストレージに割り当てることになるでしょう。ここを SSD にするといいかもしれまえせん。

netatalk 2.2 までではデフォルトで AFP ボリュームと同じ場所に CNID DB を作ってしまいます。 マニュアル の例にもありますが AppleVolumes のデフォルト設定として次のように dbpath を指定するのがよいでしょう。

:DEFAULT: options:upriv,usedots dbpath:/var/lib/netatalk/CNID/$v dperm:0775 fperm:0664

netatalk 3.0 以降のデフォルトでは CNID DB を :STATEDIR:/netatalk/CNID/$v/ に作成する設定 (vol dbpath) になっていますので、特になにもする必要はないようです。バイナリパッケージではビルドの設定を確認してください。

バックエンドのファイルシステムとして XFS と EXT4 を試しましたが、それぞれが評判通りの特性を示す結果となりました。

I/O WRITE I/O READ メタデータ更新性能
XFS 高い 高い 非常に低い
EXT4 そこそこ 高い 非常に高い

どちらを使うべきかはなかなか悩ましい選択といえます。動画など 10MB 単位のファイルのみであれば迷わず XFS ですが、大量の小さなファイルを大量に扱う必要があるならば、多少の I/O スループットを犠牲にしてでも EXT4 のメタデータ更新性能をとるべきかもしれません。

OSX ではアプリケーションがパッケージやバンドルのようなデータ形式 (実体はただのディレクトリとファイル) をよく作るため、そのようなケースに該当することもあるのではないでしょうか。

2013/01/03 17:52:00 JST