tl;dr
Amazon S3 をマウントしてファイルシステムのように扱うことが出来るツールとして, いにしへの昔から s3fs というツールが有名ですが, その s3fs を凌駕するパフォーマンスを備える goofys というツールを検証する機会がありました.
そもそも, Amazon S3 をファイルシステムのように扱って良いものかどうかについての議論は別の機会にするとして, 簡単な検証ではありましたが, Go でコンパイルされたシングルバイナリをダウンロードしてきて, 以下のようにコマンド一発で S3 バケットが簡単にマウントしてファイルの読み書きが出来るのは気持ち良かったです.
$ goofys your.s3.bucket.com your-mount-point
尚, goofys の詳細については, 上記の Github リポジトリの README や, 各種検証記事を参照して頂ければと思います.
今回, この記事では, goofys を利用するにあたり, S3 バケットにどのようなポリシーを付与すれば, 正常にマウント出来るのか調べた内容をメモしています. 例によって, 本記事の内容は, 自分の手元の環境で調べた内容となり, goofys オフィシャルの見解 (が有った場合) とは異なる場合がありますのでご容赦下さい.
全て許可ならなんでも出来る (by アンダコレ猪木
今回の構成
以下のような構成で確認しました.
- S3 バケット (バケット名: goofys.inokara.debug)
- EC2 (Amazon Linux AMI release 2018.03)
- goofys (goofys version 0.19.0-943e017724ea820eb4185419ef3c41d6f921a324)
S3 バケット goofys.inokara.debug を EC2 上の goofys にマウントしたいと思います. 尚, goofys のインストールについては, ビルドされたバイナリ をパスが通ったディレクトリに保存しています.
シンプルに
やりたいのであれば, マネジメントポリシーの AmazonS3FullAccess
を付与しておきましょう. 特に何も問題なくバケットをマウント出来ますし, ファイル (オブジェクト) の操作が可能です. 以下のように goofys を起動する際に --debug_s3
オプションと -f
(フォアグラウンドで起動) を付与します. そうすることで, goofys が S3 とどのような通信を行っているかをリアルタイムに確認することが可能です.
$ goofys-latest --debug_s3 -f goofys.inokara.debug goofys
起動すると, 以下のように出力されます.
$ goofys-latest --debug_s3 -f goofys.inokara.debug goofys 2019/03/03 06:47:08.432202 s3.DEBUG HEAD https://s3.amazonaws.com/goofys.inokara.debug = 301 [ap-northeast-1] 2019/03/03 06:47:08.432276 s3.INFO Switching from region 'us-east-1' to 'ap-northeast-1' 2019/03/03 06:47:08.434407 s3.DEBUG DEBUG: Request ec2metadata/GetMetadata Details: ---[ REQUEST POST-SIGN ]----------------------------- GET /latest/meta-data/iam/security-credentials HTTP/1.1 Host: 169.254.169.254 User-Agent: aws-sdk-go/1.8.25 (go1.9.2; linux; amd64) Accept-Encoding: gzip ... (略) ... ----------------------------------------------------- 2019/03/03 06:47:08.437554 s3.DEBUG DEBUG: Request s3/HeadObject Details: ---[ REQUEST POST-SIGN ]----------------------------- HEAD /goofys.inokara.debug/uirkrag88e0lkubvsqlv5nt33pyi5z4b HTTP/1.1 Host: s3-ap-northeast-1.amazonaws.com User-Agent: aws-sdk-go/1.8.25 (go1.9.2; linux; amd64) Authorization: AWS4-HMAC-SHA256 Credential=XXXXXXXXXXXXXXXXXXXXXX/20190303/ap-northeast-1/s3/aws4_request, SignedHeaders=host;x-amz-content-sha256;x-amz-date;x-amz-security-token, Signature=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx X-Amz-Content-Sha256: xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx X-Amz-Date: 20190303T064708Z X-Amz-Security-Token: xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx ----------------------------------------------------- 2019/03/03 06:47:08.470979 s3.DEBUG DEBUG: Response s3/HeadObject Details: ---[ RESPONSE ]-------------------------------------- HTTP/1.1 404 Not Found Transfer-Encoding: chunked Content-Type: application/xml Date: Sun, 03 Mar 2019 06:47:07 GMT Server: AmazonS3 X-Amz-Id-2: QMaYNxI2kCk5NAXL+h44hlTwwVuQX0LI3i+/PZG29w3Z2z6zbbE/fuBeXIG9IDcVl+pFfJC+Qvg= X-Amz-Request-Id: 68D120986D39ADA3 ... (略) ... ----------------------------------------------------- 2019/03/03 06:47:08.473279 main.INFO File system has been successfully mounted. 2019/03/03 06:47:08.492164 s3.DEBUG DEBUG: Response s3/ListMultipartUploads Details: ---[ RESPONSE ]-------------------------------------- HTTP/1.1 200 OK Transfer-Encoding: chunked Content-Type: application/xml Date: Sun, 03 Mar 2019 06:47:09 GMT Server: AmazonS3 X-Amz-Id-2: 12MVKsLN/9gPBD5rhOLRvQ3dSfOz+1ieFos7eq77lBXo9YZnC+14ynqzz/+CZ5xZhsb3peEjGPM= X-Amz-Request-Id: F5CC8A70E5CB4E8B ----------------------------------------------------- 2019/03/03 06:47:08.492207 s3.DEBUG { Bucket: "goofys.inokara.debug", IsTruncated: false, KeyMarker: "", MaxUploads: 1000, NextKeyMarker: "", NextUploadIdMarker: "", UploadIdMarker: "" }
マウントされるだけで随分いろいろなやりとりが行われていることが判ります. S3 だけではなく, EC2 のメタデータへのアクセスも行われていることも判ります. さらに興味深いのは, S3 に対して, 最初に HeadObject
リクエストを送信している点です. 引き続き, マウントしたディレクトリに移動して ls
コマンドを叩いてみたところ, 以下のように出力されました.
---[ REQUEST POST-SIGN ]----------------------------- GET /goofys.inokara.debug?delimiter=%2F&prefix= HTTP/1.1 Host: s3-ap-northeast-1.amazonaws.com User-Agent: aws-sdk-go/1.8.25 (go1.9.2; linux; amd64) Accept-Encoding: identity Authorization: AWS4-HMAC-SHA256 Credential=XXXXXXXXXXXXXXXXXXXXXX/20190303/ap-northeast-1/s3/aws4_request, SignedHeaders=host;x-amz-content-sha256;x-amz-date;x-amz-security-token, Signature=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx X-Amz-Content-Sha256: xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx X-Amz-Date: 20190303T085410Z X-Amz-Security-Token: xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx ----------------------------------------------------- 2019/03/03 08:54:10.553495 s3.DEBUG DEBUG: Response s3/ListObjects Details: ---[ RESPONSE ]-------------------------------------- HTTP/1.1 200 OK Transfer-Encoding: chunked Content-Type: application/xml Date: Sun, 03 Mar 2019 08:54:11 GMT Server: AmazonS3 X-Amz-Bucket-Region: ap-northeast-1 X-Amz-Id-2: PdxicsoYsHk8URTDbhw87NEJxPrXZ8yTOY2KKdQkWKZjplVDMa81xzrjWnF48Onz7IXRR1DL4/4= X-Amz-Request-Id: 3BA625E381F3D35C ----------------------------------------------------- 2019/03/03 08:54:10.553700 s3.DEBUG { Contents: [{ ETag: "\"d41d8cd98f00b204e9800998ecf8427e\"", Key: "test.txt", LastModified: 2019-03-03 06:43:16 +0000 UTC, Owner: { DisplayName: "inokara", ID: "8699d12cece0e8306cc7c614f7f169f4b61da044533a29f0b8431b781fcd9b93" }, Size: 0, StorageClass: "STANDARD" }], Delimiter: "/", IsTruncated: false, Marker: "", MaxKeys: 1000, Name: "goofys.inokara.debug", Prefix: "" }
ListObject
リクエストが送信されていることが判ります. ここで疑問に湧くのが, ls
コマンドが実行すると ListObjects
がコールされる点. この関連付けってどこで行われているだろうと気になります. ちゃんと調べきれていませんが, このあたりで FUSE が暗躍していて, ls
のシステムコールを FUSE がフックして ListObjects
を叩いているのかなと妄想しております.
さて
試すだけなら
goofys を試すだけなら, 先述のようにマウントする EC2 に AmazonS3FullAccess
を与えておけば良いと思います. しかし, 実際に業務等で運用する場合, 出来るだけ大きな権限を与えたくないというのが定石だと思います. ということで, goofys を利用するにあたって, 最低限必要なポリシーを探ってみたいと思います.
仮想要件
- ec2-user で S3 バケット (バケット名: goofys.inokara.debug) を goofys ディレクトリにマウントする
- バケット内の
files
というパス以下はファイル作成, 更新が行えるが, 削除することは出来ない
まず, 権限無し
まずは, 先述の環境の S3 にアクセスする為のポリシーを削除してみます. この状態ではマウントに失敗する想定ですが, goofys を実行すると以下のように出力されました.
$ goofys-latest --debug_s3 -f goofys.inokara.debug goofys ... (略) ... ----------------------------------------------------- 2019/03/03 10:45:15.307392 s3.DEBUG DEBUG: Response s3/HeadObject Details: ---[ RESPONSE ]-------------------------------------- HTTP/1.1 403 Forbidden Transfer-Encoding: chunked Content-Type: application/xml Date: Sun, 03 Mar 2019 10:45:14 GMT Server: AmazonS3 X-Amz-Id-2: KQBkujVF1zNsd63FYXJTJ9pI3U9PIrKPDJTkDoEIMak+gtYVfUi67DPyjb5DeBl2zEuHbX/7mJg= X-Amz-Request-Id: CF02690385F33272 ----------------------------------------------------- 2019/03/03 10:45:15.307511 s3.DEBUG DEBUG: Validate Response s3/HeadObject failed, not retrying, error Forbidden: Forbidden status code: 403, request id: CF02690385F33272, host id: KQBkujVF1zNsd63FYXJTJ9pI3U9PIrKPDJTkDoEIMak+gtYVfUi67DPyjb5DeBl2zEuHbX/7mJg= 2019/03/03 10:45:15.307584 main.ERROR Unable to access 'goofys.inokara.debug': permission denied 2019/03/03 10:45:15.307627 main.FATAL Mounting file system: Mount: initialization failed
想定通りマウントに失敗しました.
ListBucket ポリシー
上記のドキュメントを参考にして, ListBucket ポリシーを付与してみます.
{ "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Action": [ "s3:ListBucket" ], "Resource": [ "arn:aws:s3:::goofys.inokara.debug" ] } ] }
この状態で goofys でバケットをマウントしてみると, 以下のように出力されました.
$ goofys-latest --debug_s3 -f goofys.inokara.debug goofys ... (略) ... ----------------------------------------------------- 2019/03/03 10:57:19.169593 main.INFO File system has been successfully mounted. 2019/03/03 10:57:19.172542 s3.DEBUG DEBUG: Response s3/ListMultipartUploads Details: ---[ RESPONSE ]-------------------------------------- HTTP/1.1 403 Forbidden Transfer-Encoding: chunked Content-Type: application/xml Date: Sun, 03 Mar 2019 10:57:18 GMT Server: AmazonS3 X-Amz-Id-2: 7/6sgwcEaNd4gQxmfToLH3F4jxDOb4vUzVQ5tiXmn712zvRoPH/nrWspmiRaDcvJyCTALwpQhPs= X-Amz-Request-Id: CDC72FCAF1F770F4 ----------------------------------------------------- 2019/03/03 10:57:19.172565 s3.DEBUG DEBUG: Validate Response s3/ListMultipartUploads failed, not retrying, error AccessDenied: Access Denied status code: 403, request id: CDC72FCAF1F770F4, host id: 7/6sgwcEaNd4gQxmfToLH3F4jxDOb4vUzVQ5tiXmn712zvRoPH/nrWspmiRaDcvJyCTALwpQhPs=
ほう, ListMultipartUploads
がコールされていますが, これが AccessDenied
になっています. ちなみに, HeadObject
というポリシーは存在しておりませんが, ListBucket
ポリシーを付与することで, オブジェクトの一覧を取得することが出来るようなので ListBucket
が付与されていれば, 透過的に HeadObject
をコール可能になっていると想定されます.
ListBucketMultipartUploads ポリシー
先述の通り, ListMultipartUploads
をコールした際に AccessDenied
となっていたので, ポリシーに ListBucketMultipartUploads
を追加してみたいと思います.
{ "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Action": [ "s3:ListBucket", "s3:ListBucketMultipartUploads" ], "Resource": [ "arn:aws:s3:::goofys.inokara.debug" ] } ] }
このポリシーの状態で, 改めて goofys を使ってバケットをマウントをしてみると...以下のように出力されました.
$ goofys-latest --debug_s3 -f goofys.inokara.debug goofys ... (略) ... ----------------------------------------------------- 2019/03/03 11:09:55.387597 main.INFO File system has been successfully mounted. 2019/03/03 11:09:55.410434 s3.DEBUG DEBUG: Response s3/ListMultipartUploads Details: ---[ RESPONSE ]-------------------------------------- HTTP/1.1 200 OK Transfer-Encoding: chunked Content-Type: application/xml Date: Sun, 03 Mar 2019 11:09:56 GMT Server: AmazonS3 X-Amz-Id-2: WcyAjJdK76wsGygYDxNYn6ZYTj3LZqkxQWth+yTKf7kprvZivMw74FQ3X+4ahzw65Q5ZriHPM2Q= X-Amz-Request-Id: 3E897110C2CC4E5E ----------------------------------------------------- 2019/03/03 11:09:55.410472 s3.DEBUG { Bucket: "goofys.inokara.debug", IsTruncated: false, KeyMarker: "", MaxUploads: 1000, NextKeyMarker: "", NextUploadIdMarker: "", UploadIdMarker: "" }
特にエラー無くマウントされたようです. この状態で ls
コマンドを実行してみると以下のように出力されました.
... (略) ... ----------------------------------------------------- 2019/03/03 11:13:29.457766 s3.DEBUG DEBUG: Response s3/ListObjects Details: ---[ RESPONSE ]-------------------------------------- HTTP/1.1 200 OK Transfer-Encoding: chunked Content-Type: application/xml Date: Sun, 03 Mar 2019 11:13:30 GMT Server: AmazonS3 X-Amz-Bucket-Region: ap-northeast-1 X-Amz-Id-2: +uEEujy8wlHA/dtZ4n/TpE3lZQWGZCH1cVQPj9F8WGSjl1kinRvK3yCvbvDXwc384PYHLVbrrzA= X-Amz-Request-Id: 7B7D4B43444404D8 ----------------------------------------------------- 2019/03/03 11:13:29.457991 s3.DEBUG { Contents: [{ ETag: "\"d41d8cd98f00b204e9800998ecf8427e\"", Key: "test.txt", LastModified: 2019-03-03 06:43:16 +0000 UTC, Size: 0, StorageClass: "STANDARD" }], Delimiter: "/", IsTruncated: false, Marker: "", MaxKeys: 1000, Name: "goofys.inokara.debug", Prefix: "" }
ファイルの閲覧と作成
files
というキー以下でのみファイルの作成が出来るようにします. 以下のようにポリシーを更新しました.
{ "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Action": [ "s3:ListBucket", "s3:ListBucketMultipartUploads" ], "Resource": [ "arn:aws:s3:::goofys.inokara.debug" ] }, { "Effect": "Allow", "Action": [ "s3:PutObject", "s3:GetObject" ], "Resource": [ "arn:aws:s3:::goofys.inokara.debug/files/*" ] } ] }
まずは, files
パス以外でファイルを作成してみると, 以下のように出力されました.
... (略) ... ----------------------------------------------------- 2019/03/03 11:20:23.091167 s3.DEBUG DEBUG: Response s3/PutObject Details: ---[ RESPONSE ]-------------------------------------- HTTP/1.1 403 Forbidden Transfer-Encoding: chunked Content-Type: application/xml Date: Sun, 03 Mar 2019 11:20:22 GMT Server: AmazonS3 X-Amz-Id-2: pehJsUi+UF2hUwPkQJkwSnvYJuVZTxK9EBQDSvsWumIKsZlKYXOjeEK+5Yajvyv0HlKTVFnmOCo= X-Amz-Request-Id: 664CA4D73C306D63 ----------------------------------------------------- 2019/03/03 11:20:23.091194 s3.DEBUG DEBUG: Validate Response s3/PutObject failed, not retrying, error AccessDenied: Access Denied status code: 403, request id: 664CA4D73C306D63, host id: pehJsUi+UF2hUwPkQJkwSnvYJuVZTxK9EBQDSvsWumIKsZlKYXOjeEK+5Yajvyv0HlKTVFnmOCo=
引き続き, files
パス以下でファイルを作成してみると, 以下のように出力されました.
----------------------------------------------------- 2019/03/03 11:24:16.005394 s3.DEBUG DEBUG: Response s3/PutObject Details: ---[ RESPONSE ]-------------------------------------- HTTP/1.1 200 OK Content-Length: 0 Date: Sun, 03 Mar 2019 11:24:16 GMT Etag: "d41d8cd98f00b204e9800998ecf8427e" Server: AmazonS3 X-Amz-Id-2: 0XgeKsUv8e/Gb49KJ7mGO7samq3zfkVZgGKrimx82qZ7WzaG6qyNxiXmlEPHunjuRRR0yYi+b+w= X-Amz-Request-Id: F6CA71FDA3007423 -----------------------------------------------------
HTTP のレスポンスで 200
が返却されているので, 正常にファイルが作成されているようです. 念の為, 確認してみます.
$ pwd /home/ec2-user/goofys/files $ ls -l 合計 0 -rw-r--r-- 1 ec2-user ec2-user 0 3月 3 11:20 1.txt -rw-r--r-- 1 ec2-user ec2-user 0 3月 3 11:24 2.txt
goofys 側では以下のように出力されました.
... (略) ... ----------------------------------------------------- 2019/03/03 11:25:57.822293 s3.DEBUG DEBUG: Response s3/ListObjects Details: ---[ RESPONSE ]-------------------------------------- HTTP/1.1 200 OK Transfer-Encoding: chunked Content-Type: application/xml Date: Sun, 03 Mar 2019 11:25:58 GMT Server: AmazonS3 X-Amz-Bucket-Region: ap-northeast-1 X-Amz-Id-2: VknicuS9iz1q60hmiQ1OmqceX2Xxj3DzST0jTtbTYajVI0UcK4i16STxBPELd0ACDgd6hDhZc24= X-Amz-Request-Id: FD2F29B8186C04A2 ----------------------------------------------------- 2019/03/03 11:25:57.822503 s3.DEBUG { Contents: [{ ETag: "\"d41d8cd98f00b204e9800998ecf8427e\"", Key: "files/", LastModified: 2019-03-03 11:19:52 +0000 UTC, Size: 0, StorageClass: "STANDARD" },{ ETag: "\"d41d8cd98f00b204e9800998ecf8427e\"", Key: "files/1.txt", LastModified: 2019-03-03 11:20:16 +0000 UTC, Size: 0, StorageClass: "STANDARD" },{ ETag: "\"d41d8cd98f00b204e9800998ecf8427e\"", Key: "files/2.txt", LastModified: 2019-03-03 11:24:16 +0000 UTC, Size: 0, StorageClass: "STANDARD" }], Delimiter: "/", IsTruncated: false, Marker: "", MaxKeys: 1000, Name: "goofys.inokara.debug", Prefix: "files/" }
作成したファイルを /tmp/
以下にコピーしてみます.
$ pwd /home/ec2-user/goofys/files $ cp 1.txt /tmp/ $ ls -l /tmp/1.txt -rw-r--r-- 1 ec2-user ec2-user 5 3月 3 12:26 /tmp/1.txt
goofys 側では以下のように出力されました.
$ goofys-latest --debug_s3 -f goofys.inokara.debug goofys 2>&1 | grep "s3.DEBUG DEBUG" ... (略) ... 2019/03/03 12:32:51.268230 s3.DEBUG DEBUG: Request s3/ListObjects Details: 2019/03/03 12:32:51.268500 s3.DEBUG DEBUG: Request s3/HeadObject Details: 2019/03/03 12:32:51.268595 s3.DEBUG DEBUG: Request s3/HeadObject Details: 2019/03/03 12:32:51.289604 s3.DEBUG DEBUG: Response s3/HeadObject Details: 2019/03/03 12:32:51.289755 s3.DEBUG DEBUG: Validate Response s3/HeadObject failed, not retrying, error NotFound: Not Found 2019/03/03 12:32:51.297684 s3.DEBUG DEBUG: Response s3/HeadObject Details: 2019/03/03 12:32:51.298363 s3.DEBUG DEBUG: Response s3/ListObjects Details: 2019/03/03 12:32:51.299038 s3.DEBUG DEBUG: Request s3/GetObject Details: 2019/03/03 12:32:51.308361 s3.DEBUG DEBUG: Response s3/GetObject Details:
ファイルのコピーにあたっては, ListObjects
と GetObject
がコールされることが判ります.
ということで...
goofys で S3 バケットをマウントする際のステップ
以下のようなステップとなるようです.
- S3 バケット内の存在していないオブジェクトに対して,
HeadObject
をコールしてバケットにアクセス出来ることをチェックする (このあたり) - バケットにアクセス出来た時点でファイルシステムとしてマウントは完了する
- その後,
ListMultipartUploads
が可能であるかチェックする (このあたり)
この 3 つのステップが完了することでファイルシステムで利用可能な状態になると思われます.
最低限必要なポリシー
goofys を利用するにあたって, 最低限必要な IAM Policy は以下のようになると思います.
{ "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Action": [ "s3:ListBucket", "s3:ListBucketMultipartUploads" ], "Resource": [ "arn:aws:s3:::goofys.inokara.debug" ] }, { "Effect": "Allow", "Action": [ "s3:PutObject", "s3:GetObject" ], "Resource": [ "arn:aws:s3:::goofys.inokara.debug/files/*" ] } ] }
オブジェクト全体に対してファイルの作成や取得を許可する場合には以下の通り.
{ "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Action": [ "s3:ListBucket", "s3:ListBucketMultipartUploads", "s3:PutObject", "s3:GetObject" ], "Resource": [ "arn:aws:s3:::goofys.inokara.debug" ] } ] }
以上, 長々と失礼いたしました. 素敵なファイルシステムライフをお過ごし下さい.