Classi開発者ブログ

教育プラットフォーム「Classi」を開発・運営するClassi株式会社の開発者ブログです。

s3fsを使ってEC2の特定のディレクトリをS3と同期させる

はじめに

この記事は Classi developers Advent Calendar 2022 の7日目の記事です。

こんにちは!SRE留学で基盤インフラチームに在籍している id:ut61z です。 SRE留学についてはこちらのliaob88さんの記事に詳しいので興味がある方は御覧ください。

今回はs3fsというツールをClassiサービスの一部のコンポーネントに試行錯誤しながら導入してみたので、こんな挙動になるんだという発見を交えつつ、どのように設定をしたかなど紹介していきたいと思います。

s3fsとは

s3fsとは、S3バケットをFUSE(Filesystem in Userspace)経由でサーバのファイルシステムにマウントするためのツールです。
https://github.com/s3fs-fuse/s3fs-fuse

s3fsでマウントすると、マウントしたディレクトリを ls コマンドなどで参照したときに指定したS3バケット配下のファイルが表示されます。

また、常にS3と同期されているので、ディレクトリ内のファイルをサーバ上で操作すればS3バケットが、S3バケットのファイルをAWS上で操作すれば、サーバ上のファイルが更新されるという便利ツールです。

実際に設定したときのコマンド

今回はAmazon EC2で起動したAmazon Linux 2インスタンスの特定のディレクトリにS3バケットのマウントを行いました。

まずはインストールします。

sudo amazon-linux-extras install epel
sudo yum install s3fs-fuse

s3fsコマンドを実行します。
実際に設定したときのコマンドがこちらです(パスは適当な値に読み替えてください)。

s3fs some-bucket:/bucket/path /linux/path \ 
-o uid=999,gid=999,iam_role=auto,endpoint=ap-northeast-1, \
allow_other,mp_umask=022

-o 以下がオプションになるので、コマンドの核となる部分は

s3fs some-bucket:/bucket/path /linux/path

のみです。

S3のバケット及び必要であればそのバケット以下のパスと、マウントしたいサーバのディレクトリのパスを指定すればよいシンプルなコマンドです。

しかし、最初はオプションをどう指定すればよいかわからず、試行錯誤して設定しました。
最終的に指定したオプションについて紹介していきます。

s3fsのオプション

s3fsはFUSEのオプションも利用可能で、FUSEのオプションとs3fs独自のオプションを組み合わせて設定していく必要があります。

s3fsの全オプションについてはこちらに記載されていました。
https://www.mankier.com/1/s3fs
FUSEのオプションについてはこちらのREADMEに記載されています。
https://github.com/fuse4x/fuse

最終的に私が指定したオプションは以下になります(上述したものと同じ)。

s3fs some-bucket:/bucket/path /linux/path \ 
-o uid=999,gid=999,iam_role=auto,endpoint=ap-northeast-1, \
allow_other,mp_umask=022

uid・gid (FUSEのオプション)

マウントするディレクトリ配下で作成されるファイルのユーザーとグループを指定するものです。
マウントされたディレクトリで作成されたファイルのユーザー、グループがここで指定したuid、gidのものとなります。
指定しない場合、ユーザー=所有者は root になってしまうので注意が必要です。
なお、uid、gidは id コマンドで確認ができます。
https://www.gnu.org/software/coreutils/manual/html_node/id-invocation.html

iam_role=auto (s3fsのオプション)

このオプションを指定すると、そのEC2インスタンスに付与されている IAM Role を使ってS3と通信することができます。
特定のIAM Roleを指定することも可能で、その場合は auto ではなくロール名を指定します。
なお、当然S3またはそのバケットについて操作できるIAM Roleが付与されていなければS3と通信・操作することはできません。

endpoint=ap-northeast-1 (s3fsのオプション)

AWS リージョンの指定になります。
指定しない場合、順にリージョンを指定して成功するまでS3との疎通を試みるので、指定しなくても動作はしますが、無駄に通信が発生するので指定しておいた方が無難です。
なおdefaultは us-east-1 です。

allow_other (FUSEのオプション)

root以外のユーザーがそのディレクトリについて操作できるようにする設定です。
uid、gidと似ていて最初はuid、gidを指定していれば不要なのでは?と思っていたのですが結論からいうと必要でした。
uid、gidはS3という別空間にあるファイル群を擬似的にサーバのディレクトリ配下のファイルであるように見せるにあたって、そのファイルのユーザー、グループが未指定な状態を解決するような働きをします。
一方で allow_other オプションを指定すると、マウントするにあたってroot以外のユーザーにも操作を許可することができます。
ゆえに、このオプションを指定してあげないとあらゆるユーザーがファイル操作ができなくなるので指定が必要です。

mp_umask=022 (s3fsのオプション)

上記の allow_other オプションではすべてのユーザーに対してすべての操作を許可します。
mp_umask を指定することで、不必要な権限(たとえば所有者以外のユーザーのファイル更新権限)を除外することが可能です。
なおここで指定する引数は剥奪する権限を指定することになるので、最終的に付与したい権限が 755(rwxr-xr-x) であれば 022 を指定する必要があります(777 - 022 = 755 となるため)。

あくまで今回のユースケースに必要だったオプション指定なので、参考程度に見ていただければ幸いです。

導入における注意点

設定時マウントにタイムラグが発生する場合がある

再現性があるわけではないのですが、コマンド実行時マウントにタイムラグがある場合がありました。
マウント後、EC2インスタンスにSSHで接続している状態から抜けて、もう一度SSHでEC2インスタンスに接続し直すとディレクトリとS3の同期が確認できる、ということがありました。

パフォーマンスは期待できない

https://github.com/s3fs-fuse/s3fs-fuse#limitation
GithubのREADMEにも書かれていますが、基本的にパフォーマンスは期待できません。
ls コマンドを打ったときに若干もたついて表示されるような体験は残念ながらあります。
FUSE自体にパフォーマンスの課題はあるので、致し方ない部分はありそうです。

すでにファイルがある状態でマウントするとどうなるか

試行錯誤するなかで興味深かったのは、ディレクトリにすでにファイルが存在する状態でs3fsを用いてS3バケットをマウントすると、もとあったファイルは参照できなくなりますが、アンマウントするともとあったファイルは再び参照・操作が可能になるという点でした。

ディレクトリ内の情報を直接的に上書いているのではなく、マウント指定したパスでのファイル操作をジャックするようなかたちで、S3と同期的に通信し、擬似的にディレクトリ上にファイルが存在するかのように見せているのだということが伺えます。

そもそもFUSEとはカーネルの機能で、ユーザー空間でファイルシステムをプログラム実装できるツールです。”ファイル操作をジャック”するのはFUSEが提供している機能で、それを拡張してS3とやりとりしているのがs3fsなのだということを、調べていくうちに徐々に理解できてきました。

ちなみにファイルが存在するディレクトリにマウントするには nonempty オプションを指定する必要があります。

おわりに

s3fsを使ったS3バケットのディレクトリへのマウントについて紹介しました。

運用上必要に迫られs3fsを導入してみましたが、FUSEという低レイヤーなファイルシステムに対して高レイヤーなユーザー空間でのプログラミングで挙動をジャックする技術におもしろさを感じられてよい経験になりました。

明日は lowput さんです!お楽しみに!

© 2020 Classi Corp.