EC2からS3を使うための考察

  • 2019.11.19
  • AWS
EC2からS3を使うための考察

はじめに

EC2からS3を使う方法は色々あると思いますが、どうやって使うのがいいのかと少し考えてみました。すでに答えは出ているのかとは思いますが、勉強のために調べながら比べてみることとします。

テスト用のS3バケットを作成

まずはテスト用のEC2インスタンスとテスト用のS3バケットを作成します。

EC2インスタンスは毎度のことながら次のJSONファイルを使用します。

{
    "ImageId": "ami-0064e711cbc7a825e",
    "InstanceType": "t2.micro",
    "KeyName": "myawskey-tokyo",
    "MaxCount": 1,
    "SecurityGroupIds": [
        "sg-04b4e7642a8fc6efe"
    ],
    "SubnetId": "subnet-0b99e52d17314979d"
}
$ aws ec2 describe-instances --filter Name=instance-id,Values=i-00769d4e437c06154
(出力略)
$ ins_id=i-00769d4e437c06154
$ ins_pubdns=ec2-hoge.ap-northeast-1.compute.amazonaws.com

S3バケットも作成しておきます。

$ aws s3 mb s3://s3-test.waku.nagoya
make_bucket: s3-test.waku.nagoya

EC2インスタンスからS3へアクセスできるように設定

EC2インスタンスの AWS CLIの環境を設定し、S3へアクセスできるようにします。

参考URL: IAMロールでEC2からS3をアクセス制御

まずは、EC2インスタンスに接続する前にアクセスキーを作成しておきます。

$ aws iam create-access-key 
{
    "AccessKey": {
        "UserName": "hoge",
        "AccessKeyId": "HOGEHOGE",
        "Status": "Active",
        "SecretAccessKey": "hogehogehogehoge",
        "CreateDate": "2019-11-15T02:43:55Z"
    }
}

AWS CLIの初期設定を行います。

$ ssh -i $key ec2-user@$ins_pubdns
Last login: Fri Nov 15 02:33:01 2019 from hoge

       __|  __|_  )
       _|  (     /   Amazon Linux 2 AMI
      ___|\___|___|

https://aws.amazon.com/amazon-linux-2/
13 package(s) needed for security, out of 26 available
Run "sudo yum update" to apply all updates.
[ec2-user@ip-10-1-0-58 ~]$ aws configure
AWS Access Key ID [None]: HOGEHOGE
AWS Secret Access Key [None]: hogehogehogehoge
Default region name [None]: ap-northeast-1
Default output format [None]: 

S3にアクセスできるか確認してみます。

[ec2-user@ip-10-1-0-58 ~]$ aws s3 ls s3://s3-test.waku.nagoya
[ec2-user@ip-10-1-0-58 ~]$ echo OK > ok.txt
[ec2-user@ip-10-1-0-58 ~]$ aws s3 cp ok.txt s3://s3-test.waku.nagoya
upload: ./ok.txt to s3://s3-test.waku.nagoya/ok.txt            
[ec2-user@ip-10-1-0-58 ~]$ aws s3 ls s3://s3-test.waku.nagoya
2019-11-15 02:49:37          3 ok.txt

s3へアクセスできました。

ただ、認証情報(credentials)を設定するのは運用上よろしくなさそうです。

credentialsなしでS3へアクセス

credentialsなしでもS3へアクセスできるように設定してみます。

先ほど構築したEC2インスタンスとアクセスキーは削除しておきます。

$ aws ec2 terminate-instances --instance-ids $ins_id
(出力略)
$ aws iam list-access-keys
(出力略)
$ aws iam delete-access-key --access-key-id HOGEHOGE

別のEC2インスタンスを構築します。

$ aws ec2 run-instances --cli-input-json file://ec2-amazonlinux.json
(出力略)
$ ins_id=i-0e55281d0be96c43
$ aws ec2 describe-instances --filter Name=instance-id,Values=$ins_id
(出力略)
$ ins_pubdns=ec2-hoge.ap-northeast-1.compute.amazonaws.com

sshで接続し、s3に対してアクセスしてみます。

[ec2-user@ip-10-1-0-108 ~]$ aws s3 ls s3://s3-test.waku.nagoya
Unable to locate credentials. You can configure credentials by running "aws configure".

credentialsがないので怒られてしまいました。一旦sshから抜けてロールを作成します。まずはAssumeRole用のJSONファイルを作成します。

{
  "Version": "2012-10-17",
  "Statement": {
    "Effect": "Allow",
    "Principal": {"Service": "ec2.amazonaws.com"},
    "Action": "sts:AssumeRole"
  }
}

次のコマンドでロールを作成します。

$ aws iam create-role --role-name s3-fullaccess --assume-role-policy-document file://ec2-assumerole.json
{
    "Role": {
        "Path": "/",
        "RoleName": "s3-fullaccess",
        "RoleId": "AROAUC6VD3XAMERCFFOR5",
        "Arn": "arn:aws:iam::281230433728:role/s3-fullaccess",
        "CreateDate": "2019-11-15T06:12:53Z",
        "AssumeRolePolicyDocument": {
            "Version": "2012-10-17",
            "Statement": {
                "Effect": "Allow",
                "Principal": {
                    "Service": "ec2.amazonaws.com"
                },
                "Action": "sts:AssumeRole"
            }
        }
    }
}

作成したロールにAmazonS3FullAccessという既存のポリシーを割り当てます。

まずは、AmazonS3FullAccessのArnを調べます。

$ aws iam list-policies | jq '.Policies[] | select(.PolicyName == "AmazonS3FullAccess")'
{
  "PolicyName": "AmazonS3FullAccess",
  "PolicyId": "ANPAIFIR6V6BVTRAHWINE",
  "Arn": "arn:aws:iam::aws:policy/AmazonS3FullAccess",
  "Path": "/",
  "DefaultVersionId": "v1",
  "AttachmentCount": 1,
  "PermissionsBoundaryUsageCount": 0,
  "IsAttachable": true,
  "CreateDate": "2015-02-06T18:40:58Z",
  "UpdateDate": "2015-02-06T18:40:58Z"
}

次のコマンドでロールにポリシーを割り当てます。

$ aws iam attach-role-policy --role-name s3-fullaccess --policy-arn arn:aws:iam::aws:policy/AmazonS3FullAccess

インスタンスprofileを作成し、ロールを追加します。

$ aws iam create-instance-profile --instance-profile-name s3fullaccess-profile
{
    "InstanceProfile": {
        "Path": "/",
        "InstanceProfileName": "s3fullaccess-profile",
        "InstanceProfileId": "AIPAUC6VD3XAELF263QYB",
        "Arn": "arn:aws:iam::281230433728:instance-profile/s3fullaccess-profile",
        "CreateDate": "2019-11-15T06:36:06Z",
        "Roles": []
    }
}
$ aws iam add-role-to-instance-profile --instance-profile-name s3fullaccess-profile --role-name s3-fullaccess

EC2インスタンスにインスタンスprofileを割り当てます。

$ aws ec2 associate-iam-instance-profile --instance-id $ins_id --iam-instance-profile Name="s3fullaccess-profile"
{
    "IamInstanceProfileAssociation": {
        "AssociationId": "iip-assoc-067e875b69b6d3290",
        "InstanceId": "i-0e55281d0be96c435",
        "IamInstanceProfile": {
            "Arn": "arn:aws:iam::281230433728:instance-profile/s3fullaccess-profile",
            "Id": "AIPAUC6VD3XAELF263QYB"
        },
        "State": "associating"
    }
}

この状態でもう一度EC2インスタンスにログインしs3へアクセスしてみます。

$ ssh -i $key ec2-user@$ins_pubdns
Last login: Fri Nov 15 06:07:17 2019 from hoge

       __|  __|_  )
       _|  (     /   Amazon Linux 2 AMI
      ___|\___|___|

https://aws.amazon.com/amazon-linux-2/
13 package(s) needed for security, out of 26 available
Run "sudo yum update" to apply all updates.
[ec2-user@ip-10-1-0-108 ~]$ aws s3 ls s3://s3-test.waku.nagoya
2019-11-15 02:49:37          3 ok.txt

正常にアクセスできました。

転送速度を確認しておきます。

まずは、テスト用のファイルを作成。

[ec2-user@ip-10-1-0-108 ~]$ dd if=/dev/zero of=file_10M bs=1M count=10
10+0 レコード入力
10+0 レコード出力
10485760 バイト (10 MB) コピーされました、 0.00708112 秒、 1.5 GB/秒
[ec2-user@ip-10-1-0-108 ~]$ ls -lh
合計 10M
-rw-rw-r-- 1 ec2-user ec2-user 10M 11月 15 07:41 file_10M
[ec2-user@ip-10-1-0-108 ~]$ dd if=/dev/zero of=file_100M bs=1M count=100
100+0 レコード入力
100+0 レコード出力
104857600 バイト (105 MB) コピーされました、 0.0489875 秒、 2.1 GB/秒
[ec2-user@ip-10-1-0-108 ~]$ dd if=/dev/zero of=file_1G bs=1M count=1024
1024+0 レコード入力
1024+0 レコード出力
1073741824 バイト (1.1 GB) コピーされました、 13.6148 秒、 78.9 MB/秒
[ec2-user@ip-10-1-0-108 ~]$ ls -lh
合計 1.2G
-rw-rw-r-- 1 ec2-user ec2-user 100M 11月 15 07:42 file_100M
-rw-rw-r-- 1 ec2-user ec2-user  10M 11月 15 07:41 file_10M
-rw-rw-r-- 1 ec2-user ec2-user 1.0G 11月 15 07:42 file_1G

それぞれテストしてみます。

[ec2-user@ip-10-1-0-108 ~]$ time for i in {0..99}; do aws s3 cp file_10M s3://s3-test.waku.nagoya; done
upload: ./file_10M to s3://s3-test.waku.nagoya/file_10M      
upload: ./file_10M to s3://s3-test.waku.nagoya/file_10M      
upload: ./file_10M to s3://s3-test.waku.nagoya/file_10M      
upload: ./file_10M to s3://s3-test.waku.nagoya/file_10M  

(省略)

upload: ./file_10M to s3://s3-test.waku.nagoya/file_10M      

real	2m37.376s
user	0m49.699s
sys	0m6.376s

[ec2-user@ip-10-1-0-108 ~]$ time for i in {0..9}; do aws s3 cp file_100M s3://s3-test.waku.nagoya; done
upload: ./file_100M to s3://s3-test.waku.nagoya/file_100M    
upload: ./file_100M to s3://s3-test.waku.nagoya/file_100M    
upload: ./file_100M to s3://s3-test.waku.nagoya/file_100M           
(省略)
          
upload: ./file_100M to s3://s3-test.waku.nagoya/file_100M           
real	0m26.482s
user	0m11.512s
sys	0m3.147s


[ec2-user@ip-10-1-0-108 ~]$ time aws s3 cp file_1G s3://s3-test.waku.nagoya
Completed 804.2 MiB/1.0 GiB (59.8 MiB/s) with 1 file(s) remaining 

real	0m17.619s
user	0m6.711s
sys	0m2.151s

エンドポイントを使用してS3にアクセス

さきほどのEC2からS3へのアクセス方法はインターネット経由となるためS3の通信料課金にカウントされてしまいます。エンドポイントを使用することでEC2-S3間がプライベートアクセスとなるようです。

というわけでS3へアクセスするためのエンドポイントを作成します。

まずは、s3のサービス名を調べます。

$ aws ec2 describe-vpc-endpoint-services

(途中略)

        "com.amazonaws.ap-northeast-1.rekognition",
        "com.amazonaws.ap-northeast-1.s3",
        "com.amazonaws.ap-northeast-1.sagemaker.api",
        "com.amazonaws.ap-northeast-1.sagemaker.runtime",
        "com.amazonaws.ap-northeast-1.secretsmanager",
        "com.amazonaws.ap-northeast-1.servicecatalog",
        "com.amazonaws.ap-northeast-1.sns",
        "com.amazonaws.ap-northeast-1.sqs",
        "com.amazonaws.ap-northeast-1.ssm",
        "com.amazonaws.ap-northeast-1.ssmmessages",
        "com.amazonaws.ap-northeast-1.storagegateway",
        "com.amazonaws.ap-northeast-1.sts",
        "com.amazonaws.ap-northeast-1.transfer",
        "com.amazonaws.ap-northeast-1.transfer.server"
    ]
}

vpc idとroutetable id も調べておきます。

次のコマンドでエンドポイントが作成されます。

$ aws ec2 create-vpc-endpoint --vpc-endpoint-type Gateway --vpc-id $vpc_id --service-name com.amazonaws.ap-northeast-1.s3 --route-table-ids rtb-0e959c6390ba6708b
{
    "VpcEndpoint": {
        "VpcEndpointId": "vpce-0b70fda773592c1d3",
        "VpcEndpointType": "Gateway",
        "VpcId": "vpc-04e72e6413b624d29",
        "ServiceName": "com.amazonaws.ap-northeast-1.s3",
        "State": "available",
        "PolicyDocument": "{\"Version\":\"2008-10-17\",\"Statement\":[{\"Effect\":\"Allow\",\"Principal\":\"*\",\"Action\":\"*\",\"Resource\":\"*\"}]}",
        "RouteTableIds": [
            "rtb-0e959c6390ba6708b"
        ],
        "SubnetIds": [],
        "Groups": [],
        "PrivateDnsEnabled": false,
        "RequesterManaged": false,
        "NetworkInterfaceIds": [],
        "DnsEntries": [],
        "CreationTimestamp": "2019-11-15T08:26:58.000Z"
    }
}

この状態でEC2インスタンスにログインしエンドポイント経由でS3にアクセスしてみます。

$ ssh -i $key ec2-user@$ins_pubdns
Last login: Fri Nov 15 08:15:41 2019 from hoge

       __|  __|_  )
       _|  (     /   Amazon Linux 2 AMI
      ___|\___|___|

https://aws.amazon.com/amazon-linux-2/
13 package(s) needed for security, out of 26 available
Run "sudo yum update" to apply all updates.
[ec2-user@ip-10-1-0-108 ~]$ time for i in {0..99}; do aws s3 cp file_10M s3://s3-test.waku.nagoya --region ap-northeast-1;done
upload: ./file_10M to s3://s3-test.waku.nagoya/file_10M      
upload: ./file_10M to s3://s3-test.waku.nagoya/file_10M      
upload: ./file_10M to s3://s3-test.waku.nagoya/file_10M      
(途中略)
upload: ./file_10M to s3://s3-test.waku.nagoya/file_10M      
Completed 10.0 MiB/10.0 MiB (45.0 MiB/s) with 1 file(s) remaining

[ec2-user@ip-10-1-0-108 ~]$ time aws s3 cp file_1G s3://s3-test.waku.nagoya --region ap-northeast-1
Completed 424.0 MiB/1.0 GiB (64.0 MiB/s) with 1 file(s) remaining 

upload: ./file_1G to s3://s3-test.waku.nagoya/file_1G             
real	0m16.956s
user	0m6.746s
sys	0m2.275s

エンドポイント経由でS3にアクセスするにはregionを指定する必要があります。S3自体はグローバルのためリージョンはありませんが、EC2インスタンスがあるリージョンをしていすれば良いようです。

速度的には若干早いように見えます。

s3fsを使ってマウント

先ほどまでのEC2インスタンスをそのまま使用すると色々とハマりポイントがあったので、もう一度EC2インスタンスを作りおなしてs3fsのところは実施しました。また、バケット名に . “ピリオド”が含まれているのもハマりポイントがあるので、新しく s3-test-waku-nagoya というバケットを作成しました。

IAMユーザーの設定

次はs3fsを使ってEC2にS3をマウントしてみます。

s3fsもアクセスキーとシークレットキーを使用しますので準備しておきます。

また、そのIAMユーザーにS3へのアクセス権があることを確認します。

$ aws iam list-attached-user-policies --user-name admin
{
    "AttachedPolicies": [
        {
            "PolicyName": "AdministratorAccess",
            "PolicyArn": "arn:aws:iam::aws:policy/AdministratorAccess"
        },
        {
            "PolicyName": "EC2InstanceConnect",
            "PolicyArn": "arn:aws:iam::aws:policy/EC2InstanceConnect"
        }
    ]
}
$ aws iam list-policies | jq '.Policies[] | select (.PolicyName=="AdministratorAccess")'
{
  "PolicyName": "AdministratorAccess",
  "PolicyId": "ANPAIWMBCKSKIEE64ZLYK",
  "Arn": "arn:aws:iam::aws:policy/AdministratorAccess",
  "Path": "/",
  "DefaultVersionId": "v1",
  "AttachmentCount": 2,
  "PermissionsBoundaryUsageCount": 0,
  "IsAttachable": true,
  "CreateDate": "2015-02-06T18:39:46Z",
  "UpdateDate": "2015-02-06T18:39:46Z"
}
$ aws iam get-policy-version --version-id v1 --policy-arn arn:aws:iam::aws:policy/AdministratorAccess
{
    "PolicyVersion": {
        "Document": {
            "Version": "2012-10-17",
            "Statement": [
                {
                    "Effect": "Allow",
                    "Action": "*",
                    "Resource": "*"
                }
            ]
        },
        "VersionId": "v1",
        "IsDefaultVersion": true,
        "CreateDate": "2015-02-06T18:39:46Z"
    }
}

ここで使用しているユーザーは全てに対してAllowなので問題ありません。

s3fsのインストール

次のようにs3fsをインストールしていきます。

$ sudo yum -y update
$ sudo amazon-linux-extras install epel
$ sudo yum install s3fs-fuse
$ echo ACCESSKEY_ID:SECRETKEY_ID> ${HOME}/.passwd-s3fs
$ chmod 600 ${HOME}/.passwd-s3fs
$ sudo mkdir /mnt/s3
$ sudo s3fs s3-test-waku-nagoya /mnt/s3 -o passwd_file=${HOME}/.passwd-s3fs -o allow_other

動作確認

$ time for i in {0..99};do sudo cp file_10M /mnt/s3;done

real	0m36.087s
user	0m0.602s
sys	0m0.985s

[ec2-user@ip-10-1-0-171 ~]$ time for i in {0..9};do sudo cp file_100M /mnt/s3;done

real	0m19.289s
user	0m0.085s
sys	0m0.654s

$ time sudo cp file_1G /mnt/s3

real	0m49.804s
user	0m0.038s
sys	0m0.758s

複数回実行するとs3 cp より早いですが、大きなファイルを1つ書き込む速度はだいぶ遅いです。

goofysを使用してS3をマウント

もう一つgoofysを使ってS3をマウントしてみます。こちらもEC2インスタンスを作り直してから検証です。

それでは構築していきます。

goは通常のyumではなく amazon-linux-extrasから新しいgoを入れます。

[ec2-user@ip-10-1-0-214 ~]$ sudo yum -y update
[ec2-user@ip-10-1-0-214 ~]$ sudo sudo yum install -y fuse git
[ec2-user@ip-10-1-0-214 ~]$ amazon-linux-extras list | grep golang
 29  golang1.11               available    \
[ec2-user@ip-10-1-0-214 ~]$ sudo amazon-linux-extras install golang1.11
Installing golang

次にaws configure をしていきます。アクセスキーとシークレットキーを準備しておきます。ここからはrootユーザーで実行します。

[ec2-user@ip-10-1-0-214 ~]$ sudo su -
[root@ip-10-1-0-214 s3]# aws configure
AWS Access Key ID [None]: hoge
AWS Secret Access Key [None]: hoge
Default region name [None]: ap-northeast-1
Default output format [None]: 

goofysをインストールします。

[root@ip-10-1-0-214 s3]# export GOPATH=$HOME/work
[root@ip-10-1-0-214 s3]# go get github.com/kahing/goofys
[root@ip-10-1-0-214 s3]# go install github.com/kahing/goofys

S3をマウントします。

[root@ip-10-1-0-214 s3]# mkdir /mnt/s3
[root@ip-10-1-0-214 s3]# $GOPATH/bin/goofys s3-test-waku-nagoya /mnt/s3

動作確認

これまでと同じように動作確認してみます。

[root@ip-10-1-0-214 ~]# time for i in {0..99};do sudo cp file_10M /mnt/s3;done

real	0m48.776s
user	0m0.955s
sys	0m0.545s
[root@ip-10-1-0-214 ~]# time for i in {0..9};do sudo cp file_100M /mnt/s3;done

real	0m20.252s
user	0m0.105s
sys	0m0.686s
[root@ip-10-1-0-214 ~]# time sudo cp file_1G /mnt/s3

real	0m22.313s
user	0m0.025s
sys	0m0.920s

複数ファイルの場合はs3fsのが早いですが、大きいサイズではs3fsの半分程度の速度でした。ですが、aws s3 cp が一番早かったです。

まとめ

s3をマウントすることはこの記事では非推奨となっています。

AWSに関するありがちミスとその対策〜EC2、S3、RDS、Lambda、CloudFrontの場合

実際の運用が始まった時にどう使っていくかは、まだ分かりませんが、今のことエンドポイントを設置して、aws s3 cp など標準のCLIを使用しておけば間違いなさそうです。

今回は書込み転送速度しか見ませんでしたが、読み込みについても違ってくるでしょうし、もう少しこれから経験を積んでいってどのように使っていけばいいか検討していきたいと思います。