Amazon EFSのメタデータの読み書きサイズが気になったので検証してみた

数バイトの小さいファイルが大量にある環境でも頻繁に読み書きを行うと、想定よりも課金が発生しそう
2023.10.13

ls や rsync するときにどのぐらいメタデータの読み込みが発生するのか気になる

こんにちは、のんピ(@non____97)です。

皆さんは「Amazon EFSファイルシステム上の領域でls や rsync するときにメタデータのどのぐらい読み込まれるのか気になる」と思ったことはありますか? 私はあります。

2022年のre:Invent期間中にEFSにElastic Throughputモードが追加されました。

こちらのスループットモードは転送量に基づいて従量課金されます。東京リージョンの価格は以下の通りです。

  • Elastic スループットリクエスト - Data and Metadata 読み取り (転送 1 GB あたり) : 0.04USD
  • Elastic スループットリクエスト - Data and Metadata 書き込み (転送 1 GBあたり) : 0.07USD

抜粋 : 料金 - Amazon EFS | AWS

ここでData and MetadataMetadataが実際どのぐらいの量になるのか気になりました。

lsやrsyncなど通常利用でファイルにアクセスしたときに、発生するメタデータのIOが大きいのであれば気を使いたいところです。

定常的に読み書きが多い環境において、Elastic Throughputモードを使用すると想定よりも課金が発生しがちです。

実際にlsやrsyncをしてどのぐらいメタデータの読み込みが発生するのか確認します。

いきなりまとめ

  • 大量のファイルやディレクトリがある環境において、rsyncやfindなどファイル走査を行うような操作を頻繁に行うのは注意しよう
  • ファイルの読み込みをすると、1ファイルあたり4KiBのメタデータのIOが発生する
    • データの読み書きは32KiB単位で計算される
  • ファイルの大小でメタデータのサイズは変わらない

検証環境

検証環境は以下の通りです。

Amazon EFSのメタデータの読み書きサイズが気になったので確認してみた検証環境構成図

EC2インスタンスからEFSファイルシステムをNFSでマウントしているだけです。

検証環境はAWS CDKでデプロイしました。使用したコードは以下リポジトリに保存しています。

12,500個のファイルを作成したときのメトリクス

まず、検証用に12,500個のファイルを作成します。

# マウントポイントの作成
$ sudo mkdir -p /mnt/efs

# EFSのマウント
$ sudo mount -t nfs fs-0af7d4648f5d78663.efs.us-east-1.amazonaws.com:/ /mnt/efs

# NFSv4でマウントできていることを確認
$ df -hT -t nfs4
Filesystem                                         Type  Size  Used Avail Use% Mounted on
fs-0af7d4648f5d78663.efs.us-east-1.amazonaws.com:/ nfs4  8.0E     0  8.0E   0% /mnt/efs

# 5つのディレクトリに5つのサブディレクトリを作成し、各サブディレクトリに512KiBのファイルを500個作成する
$ cnt=0
$ for i in {1..5}; do
    sudo mkdir "/mnt/efs/dir_${i}"

    for j in {1..5}; do
        sudo mkdir "/mnt/efs/dir_${i}/dir_${j}"

        for k in {1..500}; do
            sudo dd if=/dev/zero of=/mnt/efs/dir_${i}/dir_${j}/file_${k} bs=512K count=1 status=none
            echo $((cnt++))
        done
    done
done

20分ほどで作成完了しました。

CloudWatchメトリクスでEFSファイルシステムのメトリクスを確認しましょう。

EFSファイルシステムのCloudWatchメトリクスは以下公式ドキュメントにまとまっています。

1分間の平均のメトリクスは以下の通りです。

12,500個のファイルを作成したときのメトリクス_1分平均

MetadataIOBytesMetadataWriteIOBytesMetadataReadIOBytesはいずれも4,096でした。統計を平均にしているのでMetadataIOBytesMetadataWriteIOBytesMetadataReadIOBytesの合計の平均だと考えます。

また、TotalIOBytesMeteredIOBytesのサイズが一致していました。それぞれ以下のように計算されます。

  • TotalIOBytes : データ読み取り + データ書き込み + メタデータ操作
  • MeteredIOBytes : データ読み取り × 1/3 + データ書き込み + メタデータ操作

2つのメトリクスの数値が同じということはデータ読み取りは発生していないということですね。

次に1分間の合計メトリクスです。

12,500個のファイルを作成したときのメトリクス_1分合計

毎分おおよそ354MBの書き込みが発生しているようです。

MetadataWriteIOBytesMetadataReadIOBytesはどちらも2.6MBで、MetadataIOBytesは5.2MBとなっています。MetadataIOBytesMetadataWriteIOBytesMetadataReadIOBytesの合計であることが分かりますね。

12,500個のファイルをrsyncしたときのメトリクス

12,500個のファイルをEFSファイルシステムからEC2インスタンスのEBSボリュームの領域にrsyncします。

$ rsync -a --stats /mnt/efs/ /home/ec2-user/

Number of files: 12,531 (reg: 12,500, dir: 31)
Number of created files: 12,530 (reg: 12,500, dir: 30)
Number of deleted files: 0
Number of regular files transferred: 12,500
Total file size: 6,553,600,000 bytes
Total transferred file size: 6,553,600,000 bytes
Literal data: 6,553,600,000 bytes
Matched data: 0 bytes
File list size: 0
File list generation time: 0.039 seconds
File list transfer time: 0.000 seconds
Total bytes sent: 6,555,918,383
Total bytes received: 237,831

sent 6,555,918,383 bytes  received 237,831 bytes  51,420,833.05 bytes/sec
total size is 6,553,600,000  speedup is 1.00

2分ほどで完了しました。

1分間の合計メトリクスは以下の通りです。

12,500個のファイルをrsyncしたときのメトリクス_1分合計

MetadataIOBytesは常に約50MBでした。ファイル作成時の1/10の時間で完了して、MetadataIOBytesもファイル作成時の1/10のサイズなので、1ファイルあたりのメタデータサイズは変わらなさそうです。

また、EFSファイルシステムrsyncの送信元ですがMetadataWriteIOBytesも発生しています。

1分間の平均メトリクスは以下の通りです。

12,500個のファイルをrsyncしたときのメトリクス_1分平均

こちらはファイル作成時のメトリクスとほとんど変わりありません。

もう一度rsyncを実行します。

$ rsync -a --stats /mnt/efs/ /home/ec2-user/

Number of files: 12,531 (reg: 12,500, dir: 31)
Number of created files: 0
Number of deleted files: 0
Number of regular files transferred: 0
Total file size: 6,553,600,000 bytes
Total transferred file size: 0 bytes
Literal data: 0 bytes
Matched data: 0 bytes
File list size: 0
File list generation time: 0.003 seconds
File list transfer time: 0.000 seconds
Total bytes sent: 230,834
Total bytes received: 90

sent 230,834 bytes  received 90 bytes  65,978.29 bytes/sec
total size is 6,553,600,000  speedup is 28,379.90

5秒ほどで完了しました。

そのときの1分間の合計メトリクスは以下の通りです。

12,500個のファイルをrsync_2回目したときのメトリクス_1分合計

IOBytesのメトリクスが全て21.5MBでした。つまりはメタデータの読み込みで21.5MBの転送が発生していることになります。

もう何回か試してみます。

3回ほど試しましたがいずれもIOBytesのメトリクスの1分間の合計はいずれも51MBでした。

12,500個のファイルをrsync_n回目したときのメトリクス_1分合計

$ rsync -avh --stats /mnt/efs/ /home/ec2-user/
sending incremental file list

Number of files: 12,531 (reg: 12,500, dir: 31)
Number of created files: 0
Number of deleted files: 0
Number of regular files transferred: 0
Total file size: 6.55G bytes
Total transferred file size: 0 bytes
Literal data: 0 bytes
Matched data: 0 bytes
File list size: 0
File list generation time: 0.004 seconds
File list transfer time: 0.000 seconds
Total bytes sent: 230.83K
Total bytes received: 90

sent 230.83K bytes  received 90 bytes  35.53K bytes/sec
total size is 6.55G  speedup is 28,379.90

51MBをファイルの総数12,500で割ると4.17KBです。

AWS公式ドキュメントにはメタデータは2KiBという記載がありました。

Amazon EFS システムで顧客に表示されるオブジェクトには、通常のファイル、ディレクトリ、シンボリックリンク、特殊ファイル (FIFO とソケット) があります。これらの各オブジェクトは、メタデータ (inode) の 2 キビバイト (KiB) と 4 KiB のデータの 1 つ以上の増分に対して計測されます。次の一覧は、さまざまなタイプのファイルシステムオブジェクトの測定データサイズの説明です。

計測: Amazon EFS がファイルシステムとオブジェクトサイズをレポートする仕組み - Amazon Elastic File System

一方で、同じドキュメントにメタデータのIOは4KiB単位で計算されると記載がありました。

エラスティックスループットの測定

あるファイルシステムで Elastic Throughput が有効になっている場合は、ファイルシステムから読み書きされたメタデータとデータの量に対してのみ支払いが発生します。エラスティックスループットモードを使用する Amazon EFS ファイルシステムでは、メタデータの読み取りをデータ読み取りオペレーションとして、メタデータの書き込みをメタデータの書き込みオペレーションとして計測し、請求します。

Amazon EFS ファイルシステムから読み書きされるメタデータは、4 KiB 単位で測定されます。Amazon EFS ファイルシステムから読み書きされたデータは、32 KiB 単位で計測されます。

EFSファイルシステム上で保存されるメタデータのサイズとしては1ファイルあたり2KiBだが、そのメタデータの読み書きをする場合は4KiB単位で行われるということでしょうか。

1ファイルあたりのサイズが変わった場合のメタデータの読みこみ量の確認

「メタデータの読み込み量が51MBとなったのは、12,500個のファイルが全て512KBだから」という可能性も0ではないかと思うので、一部ファイルのサイズを2MBにした上でrsyncします。

12,500個のファイルのうち、2,500個のファイルを2MBに書き換えます。

$ cnt=0
$ for i in {1..1}; do
    for j in {1..5}; do
        for k in {1..500}; do
            sudo dd if=/dev/zero of=/mnt/efs/dir_${i}/dir_${j}/file_${k} bs=2M count=1 status=none
            echo $((cnt++))
        done
    done
done

5分ほどで完了しました。

rsyncを行います。

$ rsync -av --stats /mnt/efs/ /home/ec2-user/
sending incremental file list
dir_1/dir_1/file_1
dir_1/dir_1/file_10
dir_1/dir_1/file_100
dir_1/dir_1/file_101
dir_1/dir_1/file_102
dir_1/dir_1/file_103
dir_1/dir_1/file_104
dir_1/dir_1/file_105
dir_1/dir_1/file_106
dir_1/dir_1/file_107
dir_1/dir_1/file_108
dir_1/dir_1/file_109
.
.
(中略)
.
.
dir_1/dir_5/file_95
dir_1/dir_5/file_96
dir_1/dir_5/file_97
dir_1/dir_5/file_98
dir_1/dir_5/file_99

Number of files: 12,531 (reg: 12,500, dir: 31)
Number of created files: 0
Number of deleted files: 0
Number of regular files transferred: 2,500
Total file size: 10,485,760,000 bytes
Total transferred file size: 5,242,880,000 bytes
Literal data: 5,242,880,000 bytes
Matched data: 0 bytes
File list size: 0
File list generation time: 0.006 seconds
File list transfer time: 0.000 seconds
Total bytes sent: 5,244,488,334
Total bytes received: 47,602

sent 5,244,488,334 bytes  received 47,602 bytes  62,808,813.60 bytes/sec
total size is 10,485,760,000  speedup is 2.00

90秒ほどで完了しました。

このときの1分間の合計メトリクスは以下の通りです。

ファイルの一部サイズを変更してrsync

10,000個のファイルは変更がないので、全て差分となった初回転送と比べてMetadataWriteIOBytesの量が少ないですね。

rsyncを何回か繰り返します。

いずれもMetadataReadIOBytesは51MBほどでした。

一部ファイルのサイズを変更してrsync_2回目

ということでファイルの大小でメタデータのサイズは変わらないことを確認しました。

ls で一部ディレクトリにアクセスしたときのメタデータの読み込み量の確認

rsyncだけでなく、lsでファイルの一覧を表示したとき、どのぐらいのメタデータ読み込み量になるのか確認します。

1つのサブディレクトリについて、lsをします。

$ ls -l /mnt/efs/dir_1/dir_1/ | grep file | wc -l
500

そのときのメタデータ読み込み量は約2MBでした。

lsをしたときのメタデータ

12,500個のファイルをrsyncで読み込んだときは51MBで、500個のファイルをlsで読み込んだときは2MBということで、読み込んだファイル数に比例して1/25となっています。

このことから、やはり1ファイルあたりのメタデータの読み込み量は4KiBとして計算されるようです。

そのため、1つのディレクトリに大量のファイルがある状態でlsを頻繁に叩く場合や、1分間隔など高頻度でrsyncをする場合、findをEFSファイルシステムのrootから行う場合などファイル走査の処理を行うと、メタデータの読み込みでそれなりに課金が発生しそうです。

仮に、こちらの検証環境において1分間隔で毎日rsyncを実行すると51MB × 60 min × 730 h / 1,024 = 2,181 GB分もメタデータの読み込みのみで課金されます。

IOが一定なのであればプロビジョンドスループットモードを使う方がお安くなりそうです。

数バイトの小さいファイルが大量にある環境でも頻繁に読み書きを行うと想定よりも課金が発生しそう

Amazon EFSのメタデータの読み書きサイズが気になったので確認してみました。

メタデータは4KiB単位で、データは32KiB単位で計算されるというのがポイントですね。

データサイズが小さくとも大量のIOが発生すると、Elastic Throughputモードを使っているとそれなりの課金が発生しそうです。

「EFSには数GBしか保存していない」という場合でもファイルが大量にあれば、ファイル走査を頻繁に行うと予想外の課金となるかもしれません。

この記事が誰かの助けになれば幸いです。

以上、AWS事業本部 コンサルティング部の のんピ(@non____97)でした!