catch-img

ZFS上のファイルを削除しても空き容量が増えずにサイズが減ってしまう?

概要

「ファイルを削除したんだけど空き容量が増えない」
「ファイルを削除すると、ストレージ容量が減ってしまう」
「最初は500TBくらいのストレージだと思っていたのに400TBくらいになってしまっている」
このような問い合わせを受けることがあります。
一見するとそんなことがあるはずが無いように思いますが、ZFSではそのような挙動は実際に起こり得ます。
こちらのブログではその理由と正しく容量を確認するための方法について説明したいと思います。

ZFSの特徴

ZFSは、Linuxの一般的なext4やXFSとは異なる特徴を持ったファイルシステムです。  
ファイルシステムと書きましたが、実際にはファイルシステムだけでなくストレージのボリュームマネージャの機能も持っています。  
今回のテーマに関係する部分に限った特徴だけでも以下のようなものがあります。

  1. ストレージプール(zpool)からファイルシステムを切り出して利用する。
  2. ファイルシステムのsnapshot機能を持っている。
  3. 圧縮や重複排除の機能を持っている。

    1つ目の特徴は、パーティションで分割することなく複数のファイルシステムを持てることを意味しています。
    これは、ストレージプール上の空きスペースは、各ファイルシステム共通の空き領域であることを意味するため、dfの出力が従来通りにはいかなくなります。

    2つ目、3つ目の特徴により、各ファイルシステム毎に使用しているサイズや全体のサイズの表記が従来通りだと難しくなります。



  • ZFSで使われる用語

    ZFSの構成要素と各レイヤーの用語については下記の図をご覧ください。  
    下から、RAIDを構成している各HDDがあり、構成された各RAIDのことをZFSではvdevと呼びます。
    (非RAID構成の場合は、ディスク1本1本が各vdevに相当します)
    1つ、または複数のVDEVから構成されるストレージプールが、zpoolです。  
    zpoolからZFSの各ファイルシステムを切り出し、ファイルシステム毎にマウントポイント、NFS/SMBの共有設定や圧縮、スナップショットの設定を決めることができます。



ZFSのファイルシステムとdfの出力について

ext4やXFSなどのファイルシステムの場合、固定サイズで区切られたパーティションの上にファイルシステムをフォーマットして利用します。
このため、dfの出力は"Size"を固定値となるため直感に沿った形の表示となりますが、ZFSにおいては主に以下の理由からそれが難しくなります。
(ZFSのQuotaを使う場合、下記の状況とは異なるケースが出てきます。)

  • 各ファイルシステムは固定サイズのパーティションで区切られていないため、ファイルシステム専用の空き容量という概念が無い
  • snapshotが使用しているサイズをどのように反映すればよいのか
  • 圧縮や重複排除については占有ブロックでカウントすべきか、あるいは圧縮前サイズでカウントすべきか


具体的な例を挙げてみます。
容量100TBのストレージプール(zpool)上に、zfs/home, zfs/share, zfs/data の3つのZFSファイルシステムがあり、
各ZFSのファイルシステムはsnapshotがある状態で、未使用領域が20TBある状況を図で示すと以下のようになります。


また、この場合のdfの出力は下記のようになるでしょう。
ext4やXFSに慣れている場合、色々と違和感を覚える出力になっているはずです。

# df -TH -t zfs   
Filesystem            Type  Size  Used Avail Use% Mounted on
zfs/home              zfs   50T   30T   20T  60% /home
zfs/share             zfs   40T   20T   20T  50% /share
zfs/data              zfs   30T   10T   20T  40% /data


違和感がありそうな点を挙げると以下のような点ではないでしょうか。

  • 全体のサイズ、ストレージプール全体のサイズがわからない
    • 全体サイズが120TB(50TB + 40TB + 30TB)あるわけではありません。
  • 空き容量は全て同じで20TBとなっている
    • 一見すると60TBの空き容量がありそうに見えますが、実際には20TBしか空き容量はありません。
  • 使用サイズの合計と共通空き容量を足しても100TBにならない
    • snapshotが占有しているサイズは、dfの出力には表示されないことが原因です。
    • Usedの合計60TB(30TB + 20TB + 10TB)と共通空き容量20TBを足しても80TBにしかならない。


なぜ"Size"を固定サイズにできないのか

いくつか理由が考えられますが、ここでは一つだけ挙げます。

理由 : サイズを固定にすると空き容量の表示がおかしくなってしまうから
実際には下記のような出力は得られませんが、仮にSizeを固定の100TBにしてみると以下のようになります。
Size - Used が Avail と一致しなくなるため上記の表記以上に違和感を覚えます。  
また、逆に "Size" - "Used" = "Avail" を一致させようとすると、zfs/homeの空き容量が70TBとなり、これでは空き容量が嘘になってしまいます。

$ df -TH -t zfs   
Filesystem            Type  Size  Used Avail Use% Mounted on
zfs/home              zfs   100T   30T   20T  60% /home
zfs/share             zfs   100T   20T   20T  50% /share
zfs/data              zfs   100T   10T   20T  40% /data

注意 : 説明のために加工した出力で、実際に上記のような出力になることはありません。
  • ではサイズを確認するにはどうしたらよいのか ⇒ 下記のように $ zpool list で確認してください。
    • SIZE: zpoolのサイズ = 固定サイズ
    • ALLOC : 物理的に占有しているサイズ。snapshotで占有しているサイズを含めて表示されます。
    • FREE : Availの相当する空き容量
$ zpool list zfs
NAME   SIZE  ALLOC   FREE  CKPOINT  EXPANDSZ   FRAG    CAP  DEDUP    HEALTH  ALTROOT
zfs    100T    80T    20T        -         -     0%    80%  1.00x    ONLINE  -
  • ZFSの領域に関しては、Sizeは基本的に [ "Size" = "Used" + "Avail" ] の式で求められる値となります。

詳しくは下記フローチャートの通り、ZFSでファイルシステムが1つだけでsnapshotも撮っていない場合は、[ "Size" - "Used" = "Avail" ] でカウントして問題ないのですが、こののようなケースは少ないため、 [ "Size" = "Used" + "Avail" ] となるケースがほとんどです。

ZFS上のファイルを削除したのに、空き容量が増えず、"Size"が減ってしまうケース

上で挙げた下記の図の状態から、zfs/home上のファイルを10TB削除した場合、そしてその削除されたファイルはsnapshotにあった場合はどのようになるでしょうか。

以下のようになります。
ZFSのsnapshotは、本体のファイルシステムに同じファイルが有る場合は容量を消費しないのですが、
本体のファイルシステムの上からファイルが削除や変更された場合にその差分が容量として消費されます。
したがって、snapshotにあるデータを削除した場合、本体のファイルシステム(zfs/home)のUsedは10TB分減り、snapshotの容量がその分増える形となります。

dfの出力の違いをBefore/Afterで見ると以下のようになります。(zfs/homeのみ抜粋)
snapshotの使用量はもともとdfの出力に表示されないこと、
Sizeは、[ "Size" = "Used" + "Avail" ] によって求められる値であること、
ファイルの実態はsnapshot上に残っていること、
以上の3つの理由から、空き容量は増えず、"Size"が減ってしまう、ということが起こります。

[Before : 10TBを削除する前のdfの出力]

# df -TH -t zfs
Filesystem            Type  Size  Used Avail Use% Mounted on
zfs/home              zfs    50T   30T   20T  60% /home



[After : 10TBを削除した後のdfの出力]

# df -TH -t zfs
Filesystem            Type  Size  Used Avail Use% Mounted on
zfs/home              zfs    40T   20T   20T  50% /home

正しい容量を確認する方法

プール全体の容量と占有量を表示するには、$ zpool list を実行してください。
SIZE, ALLOC, FREEの該当領域は下の図もご覧ください。


$ zpool list zfs
NAME   SIZE  ALLOC   FREE  CKPOINT  EXPANDSZ   FRAG    CAP  DEDUP    HEALTH  ALTROOT
zfs    100T    80T    20T        -         -     0%    80%  1.00x    ONLINE  -

また、ファイルシステム毎の使用量、Snapshotが占有している領域を表示するには $ zfs list -o space と実行します。
こちらのコマンドの場合、dfとは少し異なる表示となりそれぞれの列は以下を表しています。

  • AVAIL : これはdfと同じく、空き容量です
  • USED : df ではsnapshotを除く使用量が表示されていましたが、$ zfs list -o spaceではsnapshotを含んだ使用量が表示されます。
  • USEDSNAP : これはsnapshotによって占有されているサイズとなります。言い換えると、そのファイルシステムのsnapshotを全て削除した場合に増加する空き容量を表しています。
  • USEDDS : これは本体のファイルシステムで使用しているサイズとなり、dfの出力のUsedに相当します。
$ zfs list -o space
NAME       AVAIL   USED  USEDSNAP  USEDDS  USEDREFRESERV  USEDCHILD
zfs          20T    80T        0B    128K             0B        80T
zfs/home     20T    38T        8T     30T             0B         0B
zfs/share    20T    28T        8T     20T             0B         0B
zfs/data     20T    14T        4T     10T             0B         0B  

空き容量を増やす方法

実際に容量が逼迫してきた時に空き容量を増やすためには、本体のファイルシステムからファイルを削除した上で、そのファイルが含まれているsnapshotも削除する、といったことが必要になります。
現実的にはファイル毎にそれを調べるのは手間がかかるため、以下のように行って頂くのがよいと思います。 

  • まず、$ zfs list -o space の USEDSNAPを確認し、snapshotが占めている容量があれば、古いsnapshotから順に削除していく
  • USEDSNAPが無い場合は、本体のファイルシステムから不要なファイルを削除した上で、snapshotを古い方から順に削除していく

snapshotを削除するには、$ zfs destroy [snapshotの名前] のコマンドを実行するのですがここでは詳細は触れません。ただし、本体のZFSを削除してしまわないよう慎重に実行する必要があります。

その他の注意点

  • snapshotを削除しても、占有されていた領域が即座に開放されるわけではありません。占有していた容量に応じて少しずつ空き容量が増えていきます。
  • ある特定のsnapshotを削除した場合に開放される空き容量がどれほどかを見積もる方法はありますが、上述の通り本体のファイルシステムとの差分で常に変化します。


まとめ

長くなってしまいましたがいかがでしたでしょうか。
年に何度かこの旨のご質問を頂くケースがあるため、ブログとしてまとめてみました。

dfの出力表示についてはこのように少し戸惑うケースがありますが、
パーティションを区切らなくて済むということは事前の容量計画に基づくパーティション分割が不要になることを意味しますので
将来にわたって無駄なくストレージを活用することができます。
その他、ファイルシステムの信頼性だけでなく、圧縮やスナップショットの機能などはとても有用ですのでご活用頂ければと思います。


k-ohki
k-ohki