S3 Filesで消えるアーキテクチャ層、生まれるアーキテクチャ
2026年4月7日、AWSがAmazon S3 Filesを一般提供(GA)しました。S3バケットをNFS v4.1/v4.2のファイルシステムとしてマウントできる機能です。EC2、EKS、ECS、Lambdaのいずれからでも利用できます。
発表直後から多くのセットアップ記事や速報が出ていますので、この記事では「何が設定できるか」ではなく「何が不要になり、何が可能になるか」を整理します。
対象読者は、S3を日常的に使っていて「すごそうだけど、自分たちのアーキテクチャにどう影響するのか」を知りたいエンジニアです。
S3 Filesが解こうとしている問題
まず出発点の認識を合わせておきます。
たとえば、MLチームが学習データの前処理をするとしましょう。元データはS3に置いてあります。pandasで読み込んで加工したい。しかし pd.read_csv("s3://my-bucket/data.csv") とは書けますが、内部ではboto3がGETリクエストを発行してメモリに読み込んでいます。処理結果を書き戻すにもPUTが必要です。手元のスクリプトで open("./data.csv") と書くのとは根本的に異なるI/Oモデルで動いています。
もう少し規模が大きくなると、この問題は「パイプラインのアーキテクチャ課題」に格上げされます。以下のような構成を多くの組織が運用しているはずです。
S3からEFS/EBSにコピーして、処理して、結果をS3に書き戻す。この「中間のコピー層」は本来やりたい処理そのものではなく、ストレージのI/Oモデルの違いを埋めるためだけに存在しています。データ同期のスクリプト維持、コピー中の一貫性管理、EFSのプロビジョニングといった運用負荷はすべてこのギャップから生まれています。
S3 Filesはこのギャップそのものを解消しようとしています。
アプリケーションから見ると、S3のデータはローカルのディレクトリに見えます。 pd.read_csv("/mnt/s3files/data.csv") と書けば、裏側でS3のオブジェクトが読み込まれ、 df.to_csv("/mnt/s3files/result.csv") と書けば、変更は自動的にS3にコミットされます。
仕組みの全体像は公式ドキュメントにまとまっています。
設計判断から読み解く「単なるマウント機能ではない」理由
「S3をマウントできる」と聞いて、AWSのMountpoint for Amazon S3やGoogle CloudのCloud Storage FUSE(gcsfuse)を思い浮かべる方も多いでしょう。S3 Filesは内部構造がまったく異なります。
FUSEベースのツールとの違い
FUSEベースのツールは、S3のAPIの上にファイルシステムの振る舞いを「エミュレーション」するアプローチです。たとえばMountpoint for Amazon S3では、ファイルの上書きは「古いオブジェクトを削除して新しいオブジェクトをPUT」として実装されます。ファイルの一部だけを書き換える、というファイルシステムでは当たり前の操作がサポートされていません。ディレクトリが実体として存在しないため、空ディレクトリの扱いに不整合が出ることもあります。
S3 Filesはエミュレーションではなく、EFS(Elastic File System)という本物のNFSファイルシステムをS3に接続しています。ファイルシステム側で見える操作は本物のNFSセマンティクスであり、S3側で見えるデータは本物のS3オブジェクトです。二つの異なるシステムが共存し、その間に明示的な同期レイヤーがある構造です。
この違いが実際に効いてくるのは、たとえばデータベースのWAL(Write-Ahead Log)のようにファイルの末尾に追記していく操作や、設定ファイルの一部を書き換える操作です。FUSEベースではオブジェクト全体の再PUTになりますが、S3 Filesではファイルシステム上でバイト単位の書き込みがそのまま実行され、定期的にS3側へオブジェクトとして反映されます。
「stage and commit」とは何をしているのか
AWSのVP/Distinguished EngineerであるAndy Warfieldは、All Things Distributedに公開したポストでこの同期モデルをGitになぞらえて「stage and commit」と表現しています。「バージョン管理システムから借りた用語」とも明記されています(公式ドキュメントではこの用語は使われておらず、「synchronization」と呼ばれています)。
ファイルシステム上での変更は、Gitでいう「ワーキングディレクトリの変更」にあたります。これらの変更はすぐにS3に反映されるわけではなく、約60秒ごとにまとめてS3へPUTされます。これが「commit」です。逆に、S3側でオブジェクトが更新された場合(別のアプリケーションがPutObjectした場合など)、公式ドキュメントによると「通常は数秒以内」にファイルシステム側に反映されます。DevelopersIOの検証では約30秒で反映が確認されています。
もし両方から同時に同じファイルを変更した場合、S3が正(source of truth)になります。ファイルシステム側の変更はlost+foundディレクトリに退避され、CloudWatchメトリクスで競合の発生を検出できます。
これは明確なトレードオフです。「リアルタイムに同期される共有ファイルシステム」ではなく、「数十秒の遅延を許容する代わりに、ファイルとオブジェクトの両方のセマンティクスを壊さない」設計です。
Warfieldのポストによると、チームは当初ファイルとオブジェクトの境界を「見えなくする」ことを目指していたが、どちらかの特性を犠牲にする妥協にしかならず、最終的に「境界そのものを明示的に設計する」方針に転換したとのことです。設計経緯の詳細が書かれたこのポストは、S3 Filesの「なぜこうなっているのか」を理解するうえで必読です。
消えるアーキテクチャ層
ここからが本題です。S3 Filesの登場によって、具体的にどんな構成が不要になるのかを見ていきます。
1. S3 → EFS/EBSのステージングパイプライン
冒頭で触れたパターンの解消です。
具体的な場面を想像してみましょう。ECサイトのレコメンデーションモデルを日次で再学習する処理があるとします。購買ログはS3に蓄積されており、前処理としてデータクレンジング → 特徴量生成 → 学習データフォーマット変換を行います。
従来の構成では、EC2やSageMaker Processing Jobが起動するたびに、まずS3からEBSにデータをダウンロードします。100GBの学習データなら、インスタンスのネットワーク帯域にもよりますが、ダウンロードだけで数分かかります。処理が終わったら結果をS3にアップロードし、EBSボリュームをクリーンアップします。この「ダウンロード → 処理 → アップロード → クリーンアップ」の4ステップのうち、実際にやりたい処理は「処理」の1ステップだけです。
S3 Filesでは、S3のプレフィックス(たとえば s3://ml-data/purchase-logs/)をマウントするだけで、処理スクリプトは /mnt/s3files/purchase-logs/ にあるファイルをそのまま読み書きします。ダウンロード、アップロード、クリーンアップのステップが消えます。
ただし、前処理の結果を即座に別のジョブがS3 API経由で取得する構成になっている場合、約60秒のコミット遅延を考慮する必要があります。ジョブ間の受け渡しもファイルシステム経由で行うなら問題は起きません。S3 API経由での取得が必要なケースでは、処理完了後に明示的な待機を入れるか、S3のイベント通知で後続を起動する設計にする必要があります。
2. Lambdaの「/tmp にダウンロードしてから処理」パターン
Lambda関数でS3のデータを処理する場面を考えます。
たとえば、画像がS3にアップロードされたらサムネイルを生成するLambda関数。従来の実装はこうなります。
# 従来: S3からダウンロード → 処理 → アップロード
import boto3
from PIL import Image
s3 = boto3.client('s3')
def handler(event, context):
bucket = event['Records'][0]['s3']['bucket']['name']
key = event['Records'][0]['s3']['object']['key']
# /tmp にダウンロード(最大10GB、デフォルト512MB)
download_path = f'/tmp/{key.split("/")[-1]}'
s3.download_file(bucket, key, download_path)
# 処理
img = Image.open(download_path)
img.thumbnail((128, 128))
thumb_path = f'/tmp/thumb_{key.split("/")[-1]}'
img.save(thumb_path)
# S3にアップロード
s3.upload_file(thumb_path, bucket, f'thumbnails/{key}')
S3 Filesでマウントした場合、こうなります。
# S3 Files: マウントされたパスでそのまま操作
from PIL import Image
def handler(event, context):
key = event['Records'][0]['s3']['object']['key']
# /mnt/s3files 経由で直接アクセス
img = Image.open(f'/mnt/s3files/{key}')
img.thumbnail((128, 128))
img.save(f'/mnt/s3files/thumbnails/{key}')
boto3のインポートすら不要になります。「ファイルを開いて、処理して、保存する」というローカル開発と同じコードがそのまま動きます。
この差は単にコード量の話だけではありません。より大きい意味は、Lambda関数が/tmpの容量制約から解放されることです。たとえば、推論時に数GBの機械学習モデルを参照する関数。従来は /tmp の容量(デフォルト512MB、最大でも10GB)に収まるか気にする必要がありましたし、コールドスタート時にモデルをダウンロードする時間がレイテンシに直結していました。S3 Filesでは、設定可能なサイズ閾値(デフォルト128KB)以下のファイルはメタデータと一緒にプリフェッチされ、大きなファイルもアクセス時にオンデマンドで取得されます。Warfieldのポストではこれを「lazy hydration」と表現しており、数百万オブジェクトのバケットでもマウント直後から作業を開始できます。
LambdaへのS3ファイルシステムのマウント手順は公式ドキュメントにまとまっています。
3. EFS+S3同期の自前運用
もう少し大きな構成の話です。
「データレイクはS3だが、リアルタイム処理やインタラクティブな分析にはEFSが必要」という理由で、S3とEFSの両方を使い、間にDataSyncやStep Functions、あるいはcronで同期スクリプトを走らせている構成があるとしましょう。
この構成の苦しさは、同期ロジックの維持にあります。新しいオブジェクトの検出、差分の特定、同期失敗時のリトライ、同期中のデータ一貫性の保証、不要になったEFS側のファイルの削除。これらをすべて自前で管理する必要があります。
S3 Filesではこの同期レイヤーがマネージドに置き換わります。公式ドキュメントによると、S3からファイルシステムへの反映(import)は最大2,400オブジェクト/秒の速度で自動的に行われ、ファイルシステムからS3へのコミット(export)は約60秒のバッチウィンドウで実行されます。アクセスのないファイルデータはファイルシステムのキャッシュから自動evictionされますが、S3からは削除されません。evictionの期間は1日〜365日の範囲で設定可能で、デフォルトは30日です。つまり、ファイルシステムのストレージコストはアクティブな作業セットに比例し、全データのコピーを常時保持する必要がなくなります。
4. レガシーアプリのためのアダプタ層
ファイルシステム前提で書かれたアプリケーションをクラウドに移行する場面です。
ログ集約ツールがローカルの /var/log/ を監視してパースする。ビルドシステムがソースコードを /src/ から読む。設定管理ツールが /etc/ 配下のファイルを書き換える。こうしたアプリケーションは open() / read() / write() を前提にしており、S3 SDKへの書き換えは現実的でないことが多いでしょう。
従来は「EFSにファイルを置いて、必要に応じてS3にもバックアップする」という構成が現実解でした。S3 Filesでは、S3をプライマリストレージとしたまま、アプリケーションからはNFSマウント経由でアクセスします。コード変更なしで、マウントポイントを切り替えるだけで移行できる可能性があります。POSIXパーミッションやファイルロック(flock)にも対応しているため、これらに依存するアプリケーションでも問題は起きにくいでしょう。
生まれるアーキテクチャ
次に、S3 Filesによって「従来は現実的でなかったが、新たに可能になるパターン」です。
1. 二層構造による読み込みの自動最適化
S3 Filesの読み込み性能を理解するうえで重要なのは、内部が二層構造になっている点です。
一層目は「high-performance storage」と呼ばれる低レイテンシのストレージ層です。頻繁にアクセスされる小さなファイルがここにキャッシュされ、公式ドキュメントによると1ミリ秒未満から1桁ミリ秒のレイテンシで読み込めるとされています。設定ファイルやメタデータファイルの読み込みなど、ランダムアクセスが多い処理に効きます。
二層目はS3そのものです。1MB以上のリードは、たとえデータがhigh-performance storageにキャッシュされていても、S3から直接ストリーミングされます。S3はスループットに最適化されているため、ファイルシステム経由よりも速くなるという判断です。しかもこの場合、ファイルシステムのアクセス料金は発生せず、S3のGETリクエスト料金のみです。
公式ドキュメントに記載された性能仕様を抜粋します。
| 指標 | 値 |
|---|---|
| 1クライアントあたり最大リードスループット | 3 GiB/s |
| ファイルシステムあたり合計リードスループット | テラバイト/秒級 |
| ファイルシステムあたり最大リードIOPS | 250,000 |
| ファイルシステムあたり合計ライトスループット | 1〜5 GiB/s(リージョンによる) |
| ファイルシステムあたり最大ライトIOPS | 50,000 |
1クライアントあたり3GiB/sという数字の意味を考えてみましょう。EBSのgp3ボリュームのデフォルトスループットは125MiB/sで、追加課金で最大2,000MiB/s(約2GB/s)まで引き上げられます。io2 Block Expressでも最大4GB/sです。S3 Filesは、EBSの最高性能に近いリードスループットを、ボリュームのプロビジョニングなしで得られることを意味します。
スペック値から単純計算すると、100GBのデータセットをシーケンシャルに読み込む場合、gp3デフォルト(125MiB/s)では約13分、S3 Filesの最大値(3GiB/s)では約33秒です。実際のスループットはワークロードやインスタンスタイプに依存しますが、桁が違うという点が重要です。しかもボリュームサイズやIOPSの設計が不要で、使った分だけの課金です。さらに、公式ドキュメントによると1MB以上のリードはS3のGET料金のみでファイルシステムのアクセス料金が発生しないため、大量のシーケンシャルリードにおいてはファイルシステムの利用料金がほぼかからない構造になっています。
2. Lambdaでの大規模参照データの直接利用
従来、Lambda関数が大きな参照データを使うには三つの選択肢がありました。
- コンテナイメージにモデルファイルを含める(最大10GB、デプロイのたびに再ビルド)
- EFSをマウントする(VPC設定が必要、コールドスタートが長くなる傾向)
-
/tmpにS3からダウンロードする(最大10GB、コールドスタートにダウンロード時間が加算)
S3 Filesは四つ目の選択肢になります。S3にモデルファイルを置いたまま、Lambda関数からファイルシステム経由で直接読み込めます。モデルの更新はS3側へのアップロードだけで完了し、Lambda関数のコードや設定を変更する必要がありません。
EFSマウントと異なる点として、S3 Filesのバックエンドがあくまでもユーザー自身の通常のS3バケットであることが挙げられます。S3のバージョニングやライフサイクルポリシー、クロスリージョンレプリケーションといったS3ネイティブの機能がそのまま使えます。
3. AIエージェントのS3データアクセス
Claude Code、Codex、Kiro、Cursorといったコーディングエージェントは、ファイルシステム操作を基本的なデータアクセス手段として使います。 ls でファイルを一覧し、 cat で中身を読み、エディタで編集して保存する。Unixのツールチェーンそのものです。
もちろん、エージェントがS3にアクセスする手段は存在します。aws cliのコマンドを実行する、MCPサーバーやSkill/Power経由でS3 APIを呼ぶ、boto3のコードを生成して実行する、といった方法です。しかし、いずれもファイル操作に比べると間接的で、エージェントの推論ステップが増えます。たとえばS3上のログを調べたい場合、ファイルシステムなら grep -r "ERROR" /mnt/s3files/logs/ の一行で済むところを、S3 APIではオブジェクトの一覧取得、個別のダウンロード、ローカルでの検索というステップを踏む必要があります。
S3 Filesでバケットをマウントすると、この間接性がなくなります。エージェントから見ると、S3のデータは /mnt/s3files/ 以下の普通のディレクトリです。S3のAPIを意識する必要がなく、ファイル操作だけでデータにアクセスできます。
AWSの内部でも、KiroやClaude Codeを使うエンジニアリングチームが、エージェントのコンテキストウィンドウが圧縮されてセッション状態が失われる問題に悩んでいたとWarfieldのポストで述べられています。S3 Filesによって、エージェントが調査メモやタスクサマリを共有ディレクトリに書き込み、別のエージェントがそれを読む。セッションが途切れても、ファイルシステム上に状態が残るため、次のセッションで続きから作業できます。
ファイルロック(flock)が使えるため、複数のエージェントやプロセスが同じファイルに書き込む際の排他制御も可能です。ただし、S3 API経由のアクセスはファイルロックをバイパスするため、ファイルシステムとS3 APIの両方から同時に書き込むワークロードでは排他制御が効かない点に注意が必要です。
制約と判断基準
S3 Filesは万能ではありません。導入を検討する際に把握しておくべき制約です。
コミット間隔: 約60秒(ただし設計上の意図がある)
ファイルシステムへの書き込みがS3オブジェクトとして反映されるまで約60秒かかります。たとえば、パイプラインのジョブAがファイルを書き込み、ジョブBがS3 API経由で即座にそのオブジェクトを取得するような構成では、ジョブBは最大60秒古いデータを見る可能性があります。
ただし、この60秒は単なる制約ではなく、コスト最適化の仕組みでもあります。公式ドキュメントによると、同じファイルへの連続した書き込みはこの60秒のウィンドウ内で集約され、S3へは1回のPUTとして反映されます。ログファイルへの追記のように短時間に何度も書き込みが発生するケースでは、書き込みのたびにS3のバージョンが作られるのではなく、まとめて一つのオブジェクトになるため、S3のリクエストコストとバージョニングによるストレージコストが抑えられます。
同期スループットの公式パフォーマンス仕様は、S3 → ファイルシステム方向が最大2,400オブジェクト/秒・700MB/s、ファイルシステム → S3方向が最大800ファイル/秒・2,700MB/sです。
GA時点では「今すぐS3にcommitする」APIは提供されていません。Warfieldは今後改善していく領域として言及しているため、将来的にはより細かいコミット制御が提供される可能性はあります。
対処法としては、ジョブ間の受け渡しもファイルシステム経由で行う(両方が同じマウントポイントを使う)か、S3のイベント通知でオブジェクトの作成を検知してから後続を起動する設計が考えられます。
rename操作のコスト
S3にはネイティブのrename操作がありません。ファイルシステムでのrenameは内部的に「コピー+削除」として実装されます。
ここで重要な点は、ファイルシステム上ではrenameは即座に完了するということです。遅延が発生するのはS3側への同期です。公式パフォーマンス仕様によると、10万ファイルのディレクトリrenameはファイルシステム上では一瞬で完了しますが、S3バケットへの反映には数分かかります。その間、ファイルシステムからは新しいパスでアクセスでき、S3からは古いキーでアクセスすることになります。
また、S3側では10万回のCopyObject+10万回のDeleteObjectが発生するため、リクエストコストも無視できません。ビルドシステムやデプロイスクリプトでディレクトリの移動を多用する構成では、この特性を把握しておく必要があります。
5,000万オブジェクト超のバケット
Warfieldのポストでは、5,000万オブジェクトを超えるバケットのマウントに対して注意を促しています(公式のクォータページには現時点でこの数値は記載されていません)。対処法として、バケット全体ではなくプレフィックスを指定してマウントすることで対象範囲を絞ることが考えられます。
VPCの制約
マウントターゲットはVPC内に配置する必要があり、Lambda関数やEC2インスタンスは同じVPCの、マウントターゲットが存在するAZのサブネットから接続する必要があります。公式ドキュメントによると、サポートされるコンピュートサービスはEC2、Lambda、EKS、ECSです。オンプレミスや他クラウドのリソースからの直接マウントは、サポート対象に含まれていません。
ネームスペースの非互換
S3のオブジェクトキーとPOSIXファイル名には互換性がないケースがあります。スラッシュで終わるオブジェクトキー(S3では「ディレクトリのように見えるがオブジェクトであるもの」が作成可能)や、POSIXで無効な文字を含むキー、パスコンポーネントが255バイトを超えるキーなどは、ファイルシステム側に反映されません。具体的な制約は公式のクォータページに記載されています。
この設計は意図的なものです。Warfieldのポストによると、命名が両方の世界で有効な大多数のケースを通し、非互換なケースは無理に変換せずイベントで通知するという判断がなされています。既存バケットをマウントする前に、ファイルとして表現できないキーがないかの事前チェックは行っておくとよいでしょう。
バージョニング必須
S3 Filesを使うにはS3バケットのバージョニングが有効化されている必要があります。既存バケットに適用する場合、バージョニング有効化によるストレージコスト増(旧バージョンが保持される)と、既存のライフサイクルルールとの整合性を事前に確認してください。実際のセットアップ手順やEFSとのI/O性能比較は、前述のDevelopersIOの検証記事が参考になります。
向いている / 向いていない判断基準
既存アーキテクチャの棚卸しポイント
この発表を受けて検討する価値がある観点をまとめます。
一つ目は、自社のパイプラインで「S3からデータをコピーして処理し、結果をS3に書き戻す」構成になっている箇所の棚卸しです。特にバッチ処理やML前処理でEBS/EFSへのステージング層を持っているパイプラインは、S3 Filesで直接マウントに置き換えられる候補です。
二つ目は、新規プロジェクトにおけるストレージ選択の考え方の変化です。従来は初期設計の段階で「このデータはオブジェクトで扱うか、ファイルシステムで扱うか」を決める必要がありました。S3 Filesにより「S3に入れておけば、あとからファイルシステムとしてもアクセスできる」という選択肢が増えたことで、この判断を急ぐ必要性がひとつ減ります。
三つ目は、Lambda関数の設計です。/tmp への明示的なダウンロード/アップロードを行っている関数がどれくらいあるか。それらの関数がS3 Filesマウントで簡素化できないか。特に大きな参照データを扱う関数や、複数のLambda呼び出し間でデータを共有したい関数は検討の価値があるでしょう。
S3が20年前にオブジェクトストアとして始まり、Tables、Vectors、そして今回のFilesと、データへのアクセス手段を拡張してきた流れの中で、S3 Filesは「ストレージの選択に伴うアーキテクチャ上の制約」をひとつ減らす機能です。すべてのワークロードに適用するものではありませんが、「S3にデータがあるのに、ファイルシステムがないと使えない」という場面は多くの組織にあるはずで、そこに対するインパクトは大きいでしょう。
Discussion