Qiita Teams that are logged in
You are not logged in to any team

Log in to Qiita Team
Community
OrganizationEventAdvent CalendarQiitadon (β)
Service
Qiita JobsQiita ZineQiita Blog
14
Help us understand the problem. What is going on with this article?

More than 5 years have passed since last update.

@yoheiit

CloudFrontを経由してS3の静的ウェブサイトホスティングの機能を使う

Amazon S3をWebサーバとして利用できる、静的ウェブサイトホスティングの機能を利用してみました。
また、今回はCloudFrontを経由させることで、ELBとS3へそれぞれリクエストを振り分ける、ということも合わせて試しました。
(CloudFrontのキャッシュ機能の話については今回の対象外となります。今回はリクエストの振り分け用途として利用しています)

イメージとしては以下のようになります。

また、S3へのファイルアップロードは、EC2から行う想定です。

ELB

事前に作成して設定しておきます。
作成方法は公式ドキュメント (http://docs.aws.amazon.com/ja_jp/ElasticLoadBalancing/latest/DeveloperGuide/elb-getting-started.html) を参照してください。

また、CloudFront経由でのアクセスになるため、セキュリティグループでInboundへの任意の場所からのHTTP 80番ポートを許可しておきます。

EC2

事前にEC2を作成しておき、Apache httpdをインストールした後、コンテンツを配置します。
作成方法については割愛します。
今回はデフォルトのテストページをそのまま利用します。

また、このコンテンツをインターネットに公開したくない場合、ELB=>EC2の場合はセキュリティグループでIPアドレス制限を設定することが多いと思いますが、CloudFrontは今日時点ではアクセス制限を掛けることが出来ません。
そのため、EC2のWebサーバ側でアクセス制限を設定します。(S3へのアクセスはこの方法だと制限出来ませんが)

以下はApache httpd2.4での最低限の設定例になります。CloudFront, ELBを経由するため、X-Forwarded-Forにリクエスト元のIPアドレスが入ります。
(詳細は要件に合わせて設定します。)

RemoteIPHeader X-Forwarded-For

<Location />
  Require all denied
  Require ip IPアドレス/32
</Location>

S3

バケットの作成

公式ドキュメント (http://docs.aws.amazon.com/ja_jp/AmazonS3/latest/UG/CreatingaBucket.html) などを参照し、バケットを作成しておきます。

バケットポリシーの追加

ポリシージェネレーターを利用して、バケットポリシーを作成します。
(http://awspolicygen.s3.amazonaws.com/policygen.html)

{
  "Id": "Policyxxxxxxxxxxxxx",
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "Stmtxxxxxxxxxxxxx",
      "Action": [
        "s3:GetObject"
      ],
      "Effect": "Allow",
      "Resource": "arn:aws:s3:::バケット名/*",
      "Principal": "*"
    }
  ]
}

静的ウェブサイトホスティングの設定

S3の静的ウェブサイトホスティングの設定を行います。

  • インデックスドキュメント:必須設定となります。設定したいファイルがある場合は、設定したいファイル名を指定します。今回は特定のパスかつ特定のファイル名の場合のみS3から返すことを想定し、実在しないファイル名を指定しました。
  • エラードキュメント:必要に応じて設定します。
  • リダイレクト:必要に応じて設定します。今回は、インデックスドキュメントのindex.htmlは存在しないため、404になってしまった場合はELBにリダイレクトする、という形を試しました。

Kobito.sd4n2Y.png

  • リダイレクトルール例
<RoutingRules>
    <RoutingRule>
        <Condition>
            <HttpErrorCodeReturnedEquals>404</HttpErrorCodeReturnedEquals>
        </Condition>
        <Redirect>
            <HostName>ELB名.ap-northeast-1.elb.amazonaws.com</HostName>
            <ReplaceKeyWith></ReplaceKeyWith>
        </Redirect>
    </RoutingRule>
</RoutingRules>

CloudFront

Distributionの設定

公式ドキュメント等を参照し、Distibutionの設定を行います。
設定後は以下のようになります。

Kobito.5riopm.png

Originの設定

Originの設定を行います。ELBとS3の2つ分を設定します。
設定後は以下のようになります。

S3向けの設定の注意点として、S3に設定したリダイレクト設定を効かせたい場合、Origin Domain Nameにはドロップダウンリストに表示されるS3バケット名ではなく、S3のEndpoint名を直接指定します。
(そうしないとリダイレクトが効かなかった)

Kobito.GFb5EH.png

Behaviorの設定

Behaviorの設定にて、振り分けるパスの設定を行います。
今回は、/test/* の場合はS3、それ以外はELBへ振り分けを行うようにしています。

Kobito.VEakPe.png

IAM設定

EC2からS3にファイルをアップロード出来るようにするため、EC2に割り当てているIAMロールにS3へのポリシーをアタッチします。

AWS Management Consoleから行う場合、IAM - ポリシーの作成 - Policy Generator と進み、必要なアクションを指定してポリシーを作成します。

Kobito.7JLRet.png

ここで必要分だけアクションを指定するのですが、とりあえずs3:DeleteObject、s3:GetObject、s3:PutObject、s3:RestoreObjectあたりを設定してEC2からAWS CLIにてlsを試したところ、エラーとなりました。

$ aws s3 ls s3://バケット名/
A client error (AccessDenied) occurred when calling the ListObjects operation: Access Denied

DEBUGしてみたところ、確かに403になっています。ListObjectという文言もありますが、S3のアクションではListObjectというのはありません。

$ aws s3 ls s3://バケット名 --debug
(snip)
MainThread - botocore.vendored.requests.packages.urllib3.connectionpool - INFO - Starting new HTTPS connection (1): バケット名.s3-ap-northeast-1.amazonaws.com
MainThread - botocore.vendored.requests.packages.urllib3.connectionpool - DEBUG - "GET /?prefix=&delimiter=%2F HTTP/1.1" 403 None
MainThread - botocore.hooks - DEBUG - Event after-call.s3.ListObjects: calling handler <function enhance_error_msg at 0x7fe82cb3e488>
(スタックトレース等が続く)

色々試行錯誤の結果、以下のようになりました。
その後、AWS CLIにてlsやrmが実行可能なことを確認しました。
細かい要件が決まっていない場合は、S3FullAccessとかでも良いかもしれません。

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "Stmtxxxxxxxxxxxxx",
            "Effect": "Allow",
            "Action": [
                "s3:DeleteObject",
                "s3:DeleteObjectVersion",
                "s3:GetLifecycleConfiguration",
                "s3:GetObject",
                "s3:GetObjectAcl",
                "s3:GetObjectTorrent",
                "s3:GetObjectVersion",
                "s3:GetObjectVersionAcl",
                "s3:GetObjectVersionTorrent",
                "s3:ListAllMyBuckets",
                "s3:ListBucket",
                "s3:ListBucketMultipartUploads",
                "s3:ListBucketVersions",
                "s3:ListMultipartUploadParts",
                "s3:PutLifecycleConfiguration",
                "s3:PutObject",
                "s3:PutObjectAcl",
                "s3:PutObjectVersionAcl",
                "s3:RestoreObject"
            ],
            "Resource": [
                "arn:aws:s3:::バケット名",
                "arn:aws:s3:::バケット名/*"
            ]
        }
    ]
}

最終的には以下にファイルを配置しました。

$ aws s3 ls s3://バケット名/test/
2015-12-19 14:34:06          0
2015-12-19 14:34:34          8 test.html

Distributionを有効化

全て設定が終わったら、CloudFrontのDistributionを有効化します。
StateがEnabledに変化しています。

Kobito.oF9uKj.png

ブラウザからアクセス

それでは、この状態で、CloudFront経由でアクセスしてみます。
パスごとに振り分け出来ていることが確認出来ました。

→EC2上のコンテンツが表示される

Kobito.eFPQwD.png

→自分でS3に配置したコンテンツが表示される

14
Help us understand the problem. What is going on with this article?
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
14
Help us understand the problem. What is going on with this article?