この記事はVarnish Cache Advent Calendar 2015の3日目の記事になります。
Varnishは4.0まではfile-storageを使用する場合は特にpre-allocateをしないため、フラグメントによるパフォーマンス低下を防ぐためにdd(1)を使って先に確保してしまうのを推奨していました。
4.1でも事前確保するのを推奨しているのですが、実はbetaまではfallocateを利用してpre-allocateするようになっていました。
正式版ではこの機能はext4限定になってしまい他のファイルシステムではdisableになりました。
何故かxfsだと既に領域を確保しているにもかかわらずfallocateを使おうとするとENOSPCがでるケースがあったためです。
このバグの報告をしたのは自分なのですが、その際にfallocateについて調べてたら割と興味深い動きだったので今回書きます。
fallocateとは
すごく簡単にいうとめっちゃ高速にディスク領域の割当ができます。
どれぐらい速いかというと
# time fallocate -l 10GB varnish_storage.bin real 0m0.001s user 0m0.000s sys 0m0.001s
と気にならないレベルです。
割と便利だと思います。
fallocateの不思議な動き
xfsとext4でfallocateの動きを確認した際に以下のような動きをしました。
xfs-fallocate
root@varnish-trunk:/mnt/xfs# df -T . Filesystem Type 1K-blocks Used Available Use% Mounted on /dev/xvdh xfs 15718400 32928 15685472 1% /mnt/xfs root@varnish-trunk:/mnt/xfs# fallocate -l 10GB varnish_storage.bin root@varnish-trunk:/mnt/xfs# df -T . Filesystem Type 1K-blocks Used Available Use% Mounted on /dev/xvdh xfs 15718400 9798556 5919844 63% /mnt/xfs root@varnish-trunk:/mnt/xfs# fallocate -l 10GB varnish_storage.bin fallocate: varnish_storage.bin: fallocate が失敗: デバイスに空き領域がありません root@varnish-trunk:/mnt/xfs# ls -ltah varnish_storage.bin -rw-r--r-- 1 root root 9.4G 12月 3 23:55 varnish_storage.bin
ext4-fallocate
root@varnish-trunk:/mnt/ext4# df -T . Filesystem Type 1K-blocks Used Available Use% Mounted on /dev/xvdi ext4 15350768 38384 14509568 1% /mnt/ext4 root@varnish-trunk:/mnt/ext4# fallocate -l 10GB varnish_storage.bin root@varnish-trunk:/mnt/ext4# df -T . Filesystem Type 1K-blocks Used Available Use% Mounted on /dev/xvdi ext4 15350768 9804016 4743936 68% /mnt/ext4 root@varnish-trunk:/mnt/ext4# fallocate -l 10GB varnish_storage.bin root@varnish-trunk:/mnt/ext4# ls -ltah varnish_storage.bin -rw-r--r-- 1 root root 9.4G 12月 3 23:56 varnish_storage.bin
なぜかxfsは同じ容量を確保しようとしてるのに失敗する。
xfs-stat
root@varnish-trunk:/mnt/xfs# fallocate -l 1GB varnish_storage.bin root@varnish-trunk:/mnt/xfs# stat varnish_storage.bin File: `varnish_storage.bin' Size: 1000000000 Blocks: 1953128 IO Block: 4096 通常ファイル Device: ca70h/51824d Inode: 131 Links: 1 Access: (0644/-rw-r--r--) Uid: ( 0/ root) Gid: ( 0/ root) Access: 2015-12-03 23:58:42.851567000 +0900 Modify: 2015-12-03 23:58:47.475567000 +0900 Change: 2015-12-03 23:58:47.475567000 +0900 Birth: - root@varnish-trunk:/mnt/xfs# fallocate -l 1GB varnish_storage.bin root@varnish-trunk:/mnt/xfs# stat varnish_storage.bin File: `varnish_storage.bin' Size: 1000000000 Blocks: 1953128 IO Block: 4096 通常ファイル Device: ca70h/51824d Inode: 131 Links: 1 Access: (0644/-rw-r--r--) Uid: ( 0/ root) Gid: ( 0/ root) Access: 2015-12-03 23:58:42.851567000 +0900 Modify: 2015-12-03 23:59:15.123567000 +0900★変わった Change: 2015-12-03 23:59:15.123567000 +0900★変わった Birth: -
ext4-stat
root@varnish-trunk:/mnt/ext4# fallocate -l 1GB varnish_storage.bin root@varnish-trunk:/mnt/ext4# stat varnish_storage.bin File: `varnish_storage.bin' Size: 1000000000 Blocks: 1953136 IO Block: 4096 通常ファイル Device: ca80h/51840d Inode: 12 Links: 1 Access: (0644/-rw-r--r--) Uid: ( 0/ root) Gid: ( 0/ root) Access: 2015-12-03 23:59:35.871567000 +0900 Modify: 2015-12-03 23:59:35.871567000 +0900 Change: 2015-12-03 23:59:35.871567000 +0900 Birth: - root@varnish-trunk:/mnt/ext4# fallocate -l 1GB varnish_storage.bin root@varnish-trunk:/mnt/ext4# stat varnish_storage.bin File: `varnish_storage.bin' Size: 1000000000 Blocks: 1953136 IO Block: 4096 通常ファイル Device: ca80h/51840d Inode: 12 Links: 1 Access: (0644/-rw-r--r--) Uid: ( 0/ root) Gid: ( 0/ root) Access: 2015-12-03 23:59:35.871567000 +0900 Modify: 2015-12-03 23:59:35.871567000 +0900★変わってない Change: 2015-12-03 23:59:35.871567000 +0900 Birth: -
xfsは同じサイズでfallocateを叩くとタイムスタンプが変わる。
xfs-filefrag
root@varnish-trunk:/mnt/xfs# fallocate -l 1GB varnish_storage.bin root@varnish-trunk:/mnt/xfs# filefrag -v varnish_storage.bin |md5sum 8a5e67e82aa00af5af01c6d825bd8142 - root@varnish-trunk:/mnt/xfs# fallocate -l 1GB varnish_storage.bin root@varnish-trunk:/mnt/xfs# filefrag -v varnish_storage.bin |md5sum 8a5e67e82aa00af5af01c6d825bd8142 -
ext4-filefrag
root@varnish-trunk:/mnt/ext4# filefrag -v varnish_storage.bin |md5sum 9785097e3ce0e6eb3dd703fbd54e5d69 - root@varnish-trunk:/mnt/ext4# fallocate -l 1GB varnish_storage.bin root@varnish-trunk:/mnt/ext4# filefrag -v varnish_storage.bin |md5sum 9785097e3ce0e6eb3dd703fbd54e5d69 -
両方共、physical_offsetに変化なし
ちなみにext4の場合は割とフラグメントしますがxfsの場合はあまりしませんでした。
(10GB確保時にext4=12~50extents xfs=3~5extents)
これらを見てみると恐らくxfsは毎回空き容量と確保したい容量を付きあわせていて(既にallocateされてる容量は無視?)
ext4は最初にコマンド実施が必要かどうかをチェックしてるように見えます。
なんでこうなるのかxfsのコードを少し追ったのですが流石にキツかったので途中で諦めました。
まとめとか
fallocateは速くて便利なんでdd使ってpre-allocateしてる人はfallocate使うと幸せになれると思います(とは言えext3とかはサポートしてませんが)
そしてvarnish4.1をext4で動かす場合は内部でfallocateするので、今まで手動でやっていたpre-allocateは不要です。
もし、自分でfallocateを使ったコードを書く場合は実行前にfstat等で実際に使用している容量を調べるなどして使うと良いと思います。
そのまま利用してたら謎のIO Waitが発生し悩まされてましたが、こちらで解決しました。
これはどのバージョンを利用したとしても、XFSの場合はDDで全領域割り当てをしないといけないということでしょうか?
いいえddである必要はなくfallocateで問題無いです。
ただxfsの場合は既に容量が確保されているケースでも確保するための残容量がない(ENOSPC)が返されるので注意しましょうぐらいです。
また、なおpre-allocate自体は特に必須ではないのと(パフォーマンスを考える時は推奨)
やる場合はファイルシステムにかかわらずにやって置くとよいでしょう。
記事でも触れたとおり4.1.xでext4で動く場合はVarnish側でやるので特に必要はありません。
なお、他のfsでやる場合でも起動の度にやる必要はなく、一度やればOKです。
また4.0.xではpre-allocate関連ではないですが容量計算に問題があって
指定ストレージサイズが空き容量を超えるサイズを指定していると
既にサイズが確保されている場合(pre-allocate)に起動失敗するバグがありますのでご注意ください。(4.0.3でも起きるはず)
https://www.varnish-cache.org/trac/ticket/1682