Cloud Storage FUSEが便利なので、この辺で一旦理解を深めておく
はじめに
こんにちは。クラウドエースの間瀬です。
本記事は Google Cloud Champion Innovators Advent Calendar 2024の 18 日目の記事になります。しばらく記事投稿ができていなかったのですが、Advent Calendar に参加することで重い腰を上げることができました。
今回は Google Cloud のオブジェクトストレージサービスである Cloud Storage( 以下、GCS ) に関連する FUSE という機能について少し踏み込んで理解を深める記事をお届けしたいと思います。
私自身は本機能について実務で使ったことはなかったのですが、とあるイベントのライブパフォーマンスの中で初めて利用したところ、想像していたよりも簡単に利用することができて感動したので、理解を深めたいと思ったのがきっかけになります。
コンピューティングリソースのファイルシステムとしてGCSをマウントできる!!
ご存知の方も多いとは思いますが、本機能の特徴を一言で説明するならこれしかないと思います。
現在では、Google Compute Engine はもちろん、Google Kubernetes Engine や Cloud Run など幅広いコンピューティングサービスにて利用することが可能です。
オブジェクトストレージの特徴として、アクセスする際には HTTPS でのアクセスが一般的になり、GCS においては多くのケースで Google Cloud が提供する SDK を利用することになると思います。
GCS を利用するアプリケーションでは少なからずこれら SDK に依存する必要がありますが、FUSE を利用すればアプリケーションはローカルのファイルシステムを操作する振る舞いをするだけでオブジェクトストレージへアクセスすることができ、高可用性、大容量といったメリットを享受することができます。
主なユースケース
- ML ワークロードなどコンピューティングリソース上で動作するアプリケーションにおいて、GCS へのデータ読み書きが必要なとき
- オンプレミスからクラウドへシフトする際など、アプリケーションの変更は行わずにデータの可用性を高めたい
知っておきたい利用する上での注意点
一見、便利な FUSE ですが、制約も多いため、利用する上では 公式のドキュメント を確認しましょう。また、コンピューティングリソースによっても制約が異なることがあるので注意してください。以下は制限事項の一部になります。
- 同時実行制御ができない:複数のプロセスから同じオブジェクトに対する書き込みは制御されず、後勝ちになります。
- 保持ポリシーが有効なバケットへの書き込みはできない:バケットロック等の機能が使えないのでこれらバケットとは分離して利用する必要があります。また、バージョニングも正式にはサポートしていないようです。
- オブジェクトのメタデータを設定できない:メタデータを設定したい場合、別途 CLI や API 経由で編集してあげる必要があります。
仕組み詳細
ここからは GCS FUSE の詳細な仕組みについて記載していきます。
FUSE(Filesystem in Userspace) とは
ユーザーが独自のファイルシステムを作成できることを目的としたフレームワークです。通常のファイルシステムはカーネルにて実装する必要がありましたが、FUSE によって特権をもたないユーザープロセスでファイルシステムを開発し、実行することができるようになりました。
FUSE を利用して実現されているツールの例としては SSH 経由でリモートのファイルシステムをマウントする sshfs などがあります。GCS FUSE もこの FUSE を利用して実現されているということになります。
- Wikipedia (https://ja.wikipedia.org/wiki/Filesystem_in_Userspace)より抜粋
読み取り / 書き込み
読み書きいずれの場合も Cloud Storage API を使って実際にバケットオブジェクトに対してアクセスを行っています。そのため、通常のファイルシステムへの読み書きと比較してレイテンシが高くなる特徴があります。
書き込み時にはローカルにファイルが作成されることに注意
既存のオブジェクトを変更する場合はオブジェクトをローカルにダウンロードした上で更新を行い、ファイルのクローズが行われると GCS へアップロードを行います。
新規にオブジェクトを作成する場合においても、一度ローカルにファイルを書き出した上で GCS へアップロードを行います。
これらの特徴からファイルの書き込み時には一度ローカルにファイルが生成されることになるため、大容量のファイルを扱う場合はローカルのストレージ容量に注意する必要があります。
キャッシュ
GCS FUSE では GCS へのアクセスにかかるオーバーヘッドやコストの削減のために複数のキャッシュ機能を提供しています。
一部のキャッシュ機能はデフォルトで有効になっていますが、複数のマシンでそれぞれ同じのバケットをマウントしている場合はキャッシュによって一貫性を保つことができなくなるため、利用には注意が必要です。
Stat Caching
デフォルトで有効になっている機能でls -l
によって得られるようなオブジェクトのメタデータ(更新時刻、サイズなど)情報をキャッシュします。
Type Caching
デフォルトで有効になっている機能でストレージ上のオブジェクトがファイルかディレクトリかの情報をキャッシュします。
File Caching
ファイルの読み取り速度を向上させるためにローカルのストレージにキャッシュします。デフォルトでは無効になっています。
List Caching (Kernel List Cache)
ls
によって得られるディレクトリのリスト情報をキャッシュします。デフォルトでは無効になっています。
オブジェクト名とファイルシステムのマッピング
GCS FUSE ではオブジェクト名の / (スラッシュ) からディレクトリを表現します。
例えば 2024/12/18/fileA.txt というオブジェクト名である場合はファイルシステムから参照すると以下のように表現されます。
2024/
|-12/
|-18/
|-fileA.txt
この時、GCS FUSE がディレクトリとして認識できるように 2024, 12, 18 といったディレクトリにあたる 0 バイトのオブジェクトを作成するようです。
このような動きは GCS FUSE からオブジェクトを作成したり Console からオブジェクトを作成するときには行われますが、gcloud storage
といった CLI から作成した際には動作しないため、個別に mkdir コマンドでディレクトリを作成するまでファイルシステム上ではオブジェクトが参照できません。
上記に対策するための設定として、implicit-dirs
というオプションが用意されており、これを有効にすることで GCS FUSE が問題のディレクトリを検索し、ディレクトリを定義してくれるようになります。
他の選択肢として、階層型名前空間を有効にした GCS バケットであれば上記のようなオプションを使わずに対策することが可能です。 階層型名前空間を有効にしたバケットの特徴として、ディレクトリ(フォルダ)に該当するオブジェクトの名前変更がアトミックになるため、GCS FUSE においても 1 回のオペレーションで変更が完結するのでパフォーマンスにも優れます。GCS の階層型名前空間についてはこちらの記事で解説されているので参考にしてください。GCS FUSE からすると階層型名前空間との相性が良さそうですが、こちらにも制限事項や GCS アクセスにかかるオペレーションコストが異なってくるのでユースケースに合わせて利用を検討してください。
GKE におけるマウント方法の選択肢
GKE で GCS FUSE を利用する場合、以下いずれかの方法で Pod へ GCS のマウントが可能です。
- CSI エフェメラル ボリュームとしてマウント
- 静的プロビジョニングによるマウント
CSI エフェメラルボリュームというと、キャッシュなどデータの永続化を目的としない一時ボリュームとして利用し、 Pod のライフサイクルに合わせて確保・解放されるものですが、GCS FUSE の場合においては Pod を終了しても GCS へ作成したオブジェクトは削除されません。再度 Pod を起動しマウントしてもマウント前のオブジェクトの状態が維持されます。そのため使用感としては通常の永続ボリュームを使用することに近いと感じました。
どちらの方法でやるべきか?というと現状は GKE クラスタを管理するユーザーの所掌分担に合わせて決めていいのではないかと感じました。
さいごに
今回は GCS FUSE の仕組みや特徴についてまとめさせていただきました。前述している通り私自身、本機能はほとんど使えていないですが GCS を利用するケースは多いので今後、活用できる際には積極的に採用していきたいと思います。
今後利用する中で新たな気づきやノウハウを得ましたら記事にしたいと思います。ありがとうございました。
Discussion