ちょっと話題の記事

s3fsを使ってEC2からS3をマウントしたときにうまくいかなくて調べた事まとめ

2013.06.03

この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。

こんにちは植木和樹です。今回はタイトル通りs3fsという仕組みを使ってEC2からS3をマウントしてみました。

解決したい問題

たとえば複数のWebサーバがあった時に、サーバ間でファイルを共有したいってことがあります。Webアプリの処理でファイルを作る場合、あるEC2インスタンス障害が発生してダウンしても別のインスタンスからアクセスできるようにしておきたい、といったケースです。

解決策として以下の案が思いつきます。

  • NFSで領域を共有する
  • RDSにファイルを格納する
  • S3にファイルをアップロードする

既存のアプリをなるべく変更せずに対応するとなるとNFSサーバを用意するのが手っ取り早いのですが、そうなるとNFSサーバが単一障害点(SPOF)になります。冗長かのためにEC2インスタンスの数が増えて構成が複雑になるのはなるべく避けたいですよね。

ということで信頼性の高いS3ストレージをなんとかお手軽に扱えないか、というのが今回の取り組みです。

s3fsとは

s3fsというのは、その名前の通りS3のバケットをファイルシステムとしてマウントできる仕組みです。FUSE(Filesystem in Userspace)という一般ユーザが様々なファイルシステムを作成できる機能を用いて実装されています。 *1

IAMユーザの作成

S3バケットへのアクセスは権限を持ったIAMユーザ(アクセスキーとシークレットアクセスキー)で行います。IAM Roleには対応していないようです(v1.70)。

マネージメントコンソールのIAMの画面からS3アクセス用のユーザを作り、アクセスキーとシークレットアクセスキーを入手しておいてください。

20130602-s3fs-001

20130602-s3fs-002

インストールと設定

EC2インスタンスのOSは Amazon Linux AMI 2013.03.1(x86_64)を使用しました。

s3fsはyumパッケージで提供されていないのでソースからコンパイルします。コンパイルに必要なパッケージをインストールしたら、configure、make、make installでOKです。

$ sudo yum -y update
$ sudo yum -y install gcc-c++ fuse fuse-devel libcurl-devel libxml2-devel  openssl-devel
$ curl -O http://s3fs.googlecode.com/files/s3fs-1.70.tar.gz
$ tar xzf s3fs-1.70.tar.gz
$ cd s3fs-1.70
$ ./configure --prefix=/usr/local
$ make
$ sudo make install
$ find /usr/local -type f
/usr/local/bin/s3fs
/usr/local/share/man/man1/s3fs.1

アクセスキーとシークレットアクセスキーを設定します。キーを設定する方法は4つあります。

  1. コマンドラインオプションpasswd_fileを指定する
  2. 環境変数 AWSACCESSKEYID、 AWSSECRETACCESSKEY を指定する
  3. マウントを実行するユーザのホームディレクトリに.passwd-s3fsというパスワードファイルを作成する
  4. /etc/passwd-s3fs にパスワードファイルを作成する

今回は /etc/passwd-s3fs にファイルを作成します。ファイルは2種類の記述方法があるのですが、今回はS3のバケットによってアクセスキーを変えることができるよう「バケット名」「アクセスキー」「シークレットアクセスキー」をコロンで区切った文字列を書いておきます。

$ echo "bucketname:access_key:secret_access_key" | sudo tee -a /etc/passwd-s3fs
$ sudo chmod 640 /etc/passwd-s3fs

s3fs 1.63以降の注意点

s3fsはv1.63以降パーミッションの取り扱いが変わっています。このためマウントオプションを指定しないと「ファイル一覧は表示できるけど、一般ユーザでファイルの中身を見ようとするとPermission Deniedされる」という問題がでます。補足するとS3のバケットにマネージメントコンソールからアップロードしたファイルでこの問題がでます。

$ ls -l /share/s3fs_test.txt
---------- 1 root root 6 6月 2 01:54 2013 /share/s3fs_test.txt
$ cat /share/s3fs_test.txt
cat: /share/s3fs_test.txt: 許可がありません

詳細な経緯は Issue 321に書かれています。v1.62まではファイルへのアクセスをすべてroot権限で行っていたので読み書きできていたのが、v1.63以降user/group/modeを考慮するようになったためだそうです。ファイルの権限はS3のファイル個々に設定されたメタデータx-amz-meta-modeから判断するのですが、マネージメントコンソールからアップロードされたファイルにはこのメタデータが付与されないため上述の問題が発生します。

20130602-s3fs-003

これを解決するにはv1.69(2013/5/16リリース)で追加されたuid/gidマウントオプションを使います。このオプションによりメタデータが設定されていないファイルについては、指定したuid/gidがオーナー(グループ)として扱われるので読み書きできるようになります。

マウント、読み書き、アンマウント、fstab

マウント

一般ユーザ(今回はec2-user)から読み書きできるよう設定します。idコマンドによるとec2-userのuidは222、gidは500です。

$ id ec2-user
uid=222(ec2-user) gid=500(ec2-user) 所属グループ=500(ec2-user),10(wheel)
$ sudo mkdir /share
$ ls -ld /share
drwxr-xr-x 2 root root 4096 6月 2 01:52 2013 /share
$ sudo /usr/local/bin/s3fs bucketname /share -o rw,allow_other,use_cache=/tmp,uid=222,gid=500
$ ls -ld /share
drwxr-xr-x 1 ec2-user ec2-user 0  1月  1 00:00 1970 /share

読み書き

ファイルを読み書きしてみます。マネージメントコンソールからアップロードしたs3fs_test.txtのパーミッションは000ですが、uidオプションによってオーナーがec2-userになっているため読み書きすることができます。

$ ls -l /share/s3fs_test.txt
---------- 1 ec2-user ec2-user 6 6月 2 01:54 2013 /share/s3fs_test.txt
$ cat /share/s3fs_test.txt
Hello
$ echo "Hello" >> /share/s3fs_test.txt 
$ cat /share/s3fs_test.txt 
Hello
Hello
$ echo "World" > /share/s3fs_test2.txt
$ ls -l /share/s3fs_test2.txt
-rw-rw-r-- 1 ec2-user ec2-user 6 6月 2 02:12 2013 /share/s3fs_test2.txt
$ cat /share/s3fs_test2.txt
World

なおs3fsで作成したファイルには上述のメタデータが付与されます。

20130602-s3fs-004

アンマウント

アンマウントはfusermountコマンドを実行します。

$ sudo fusermount -u /share

/etc/fstab

OS起動時にS3をマウントする場合は/etc/fstabにエントリを追加します。

s3fs#bucketname /share fuse auto,rw,allow_other,use_cache=/tmp,uid=222,gid=500 0 0

読み書きにあたっての注意点

書き込みレスポンス速度

「遅い!!」この一言に尽きます。echoコマンドで"World"という文字列をファイルに書き出すだけで1〜2秒くらいかかります。これはS3とのやりとりをHTTPのリクエスト/レスポンスで行っているためです。ローカルストレージのブロックデバイスではないので仕方がないですね。Webアプリから直接このS3領域に書き込むのではなく一旦ローカルディスクに書き込み、rsyncなどで定期的に同期してあげると良いでしょう。

読み込みキャッシュの問題

マウントオプションでuse_cache=/tmpのようにキャッシュディレクトリを指定すると、読み書きしたファイルを/tmp/<bucketname>にキャッシュしてくれるので2回目以降の読み込みが高速になります。

ところが弊害もあり別EC2インスタンスからファイルが更新されても、それを知らずにキャッシュをみてしまいます。あるファイルへの書き込みが特定のEC2インスタンスからのみの場合は問題になりませんが、複数インスタンスから更新処理が重なった場合には不整合がおきますので注意してください。

結論

S3をファイルシステムとしてマウントすることができました。lsやcp、cat、rsyncなど扱い慣れたコマンドでファイルを操作できるのは便利です。ただ、ややレスポンス速度の面などで使いどころは考慮する必要があります。

使い方によってはNFSサーバが不要になり、システム構成をシンプルにする(=コストダウンする)ことができるため、同様の問題で困っている方は検討されてはいかがでしょうか。

脚注

  1. http://ja.wikipedia.org/wiki/Filesystem_in_Userspace