Webホスティング機能を利用する

s3_webhosting.yml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
AWSTemplateFormatVersion: '2010-09-09'
Description: S3 Webhosting

# ------------------------------------------------------------ #
# Input Parameters
# ------------------------------------------------------------ #
Parameters:
S3BucketName:
Description: A name for the WebContents buckets.
Type: String
S3BucketNameLog:
Description: A name for the Log buckets.
Type: String

Resources:
# ------------------------------------------------------------ #
# S3
# ------------------------------------------------------------ #
S3BucketContents:
Type: AWS::S3::Bucket
#DeletionPolicy: Retain
Properties:
BucketName: !Sub ${S3BucketName}
AccessControl: PublicRead
WebsiteConfiguration:
IndexDocument: index.html
ErrorDocument: error.html
LoggingConfiguration:
DestinationBucketName: !Ref S3BucketLog
LogFilePrefix: !Sub "/logs/${S3BucketName}/"

S3BucketContentsPolicy:
Type: AWS::S3::BucketPolicy
Properties:
Bucket: !Ref S3BucketContents
PolicyDocument:
Statement:
Action:
- "s3:GetObject"
Effect: "Allow"
Resource: !Sub "arn:aws:s3:::${S3BucketName}/*"
Principal: "*"

S3BucketLog:
Type: "AWS::S3::Bucket"
Properties:
BucketName: !Ref S3BucketNameLog
AccessControl: LogDeliveryWrite
BucketEncryption:
ServerSideEncryptionConfiguration:
- ServerSideEncryptionByDefault:
SSEAlgorithm: AES256
LifecycleConfiguration:
Rules:
- Id: !Sub "${S3BucketNameLog}-log-LifeCycle"
Status: Enabled
Prefix: log/
ExpirationInDays: 365
Transitions:
- StorageClass: GLACIER
TransitionInDays: 180

# ------------------------------------------------------------ #
# Output Parameters
# ------------------------------------------------------------ #
Outputs:
S3BucketName:
Value: !Ref S3BucketContents
WebsiteURL:
Value: !GetAtt S3BucketContents.WebsiteURL
Description: URL for website hosted on S3

カスタムエラードキュメント

HTTP 404 Not Foundをカスタマイズすることができる。

1
2
3
WebsiteConfiguration:
IndexDocument: index.html
ErrorDocument: error.html

コンテンツのアップロード

1
2
3
4
5
6
7
8
$ aws s3 ls s3://web.nullpo.io
$ aws s3 cp contents/index.html s3://web.nullpo.io/
upload: contents/index.html to s3://web.nullpo.io/index.html
$ aws s3 cp contents/error.html s3://web.nullpo.io/
upload: contents/error.html to s3://web.nullpo.io/error.html
$ aws s3 ls s3://web.nullpo.io
2020-05-01 22:38:45 79 error.html
2020-05-01 22:38:37 79 index.html

S3バケットにコンテンツがあるとスタック削除で削除エラーになる

スタックの削除でStackStatusReason: The following resource(s) failed to delete: [S3BucketContents]のエラーとなる。
イベントでみるとThe bucket you tried to delete is not empty (Service: Amazon S3; Status Code: 409; Error Code: BucketNotEmpty;でバケットが空でないため削除できない。

デフォルトのDeletionPolicy: Deleteの場合、バケットを空にする必要がある。

AWS CloudFormation はスタックの削除時にリソースと (該当する場合) そのすべてのコンテンツを削除します。この削除ポリシーは、あらゆるリソースタイプに追加することができます。デフォルトでは、DeletionPolicy を指定しない場合、AWS CloudFormation はリソースを削除します。
Amazon S3 バケットでは、削除を成功させるためにはバケットのすべてのオブジェクトを削除する必要があります。

スタック自体はそのまま削除させ、空でないバケットはそのまま残す方法もある。
DeletionPolicy: Retainを指定すれば、コンテンツとバケットは残り、スタックは削除される。

S3バケットのコンテンツを空する

バケットのコンテンツを空にするにはaws s3 rmで削除が可能。

1
2
3
$ aws s3 rm s3://web.nullpo.io/ --recursive
delete: s3://web.nullpo.io/index.html
delete: s3://web.nullpo.io/error.html

S3バケットとコンテンツを削除する

バケットも削除するにはaws s3 rbを使用する。

1
2
3
4
5
6
$ aws s3 rb s3://web.nullpo.io/
remove_bucket failed: s3://web.nullpo.io/ An error occurred (BucketNotEmpty) when calling the DeleteBucket operation: The bucket you tried to delete is not empty
$ aws s3 rb s3://web.nullpo.io/ --force
delete: s3://web.nullpo.io/index.html
delete: s3://web.nullpo.io/error.html
remove_bucket: web.nullpo.io

ログはベストエフォート型の記録

アクセスログはすぐに出力されない。
テストした際にはアクセス可能になるまで、1時間程度を要した。

サーバーアクセスログレコードの配信は、ベストエフォートで行われます。ログ記録用に適切にバケットを設定した場合、そのバケットへのほとんどのリクエストについてログレコードが配信されます。ほとんどのログレコードは、記録された時間から数時間以内に配信されますが、配信間隔は短くなる場合もあります。
サーバーログの完全性や適時性は保証されません。リクエストのログレコードが、リクエストが実際に処理されてからかなり後に配信されたり、配信すらされないこともあり得ます。サーバーログの目的は、バケットに対するトラフィックの特性を理解することです。ログレコードが失われることはまれですが、すべてのリクエストが完全に報告されるとは限りません。