Amazon Web Services ブログ
Visitor PrioritizationソリューションをCloudFront Functionsを使って実装するための考慮点 Part2
2021年5月に「Visitor PrioritizationソリューションをCloudFront Functionsを使って実装するための考慮点」の Blog を公開しました。以降、多くのお客様、システムにおいて CloudFront Functions を使った活用がされており、チケットの購入や予約などに関わるシステムにおいて利用されているユースケースが増えております。年末に向けて多くのお客様においてイベントやチケット販売などにおいて利用してみたいというご要望を受ける機会が増えておりますので、改めてこのソリューションについて紹介していきたいと思います(一部再掲を含みます)。
動作するイベントポイントについて
CloudFront Functionsの紹介ブログにより詳細な内容が載っているのでここでは詳しく説明しませんが、Lambda@EdgeとCloudFront Functionsは動作するイベントポイントが異なります。以下の図のようにLambda@EdgeはViewerフェーズ、Originフェーズの双方においてリクエスト/レスポンスのイベントで利用することができますが、CloudFront FunctionsはViewerフェーズのリクエスト/レスポンスのイベントでのみ使用することができます。
このソリューションを利用するための事前条件
- 新しいAWSアカウントの作成 または 既存のAWSアカウント
- 新しく作成する場合は、AWS Hands-on for Beginners ハンズオンはじめの一歩: AWS アカウントの作り方 & IAM 基本のキ を参考にしてください。
- CloudFrontディストリビューションに対して利用する2つのビヘイビア(もし、CloudFrontのディストリビューションがない場合は新しいCloudFrontディストリビューションを用意)
- 以降で説明する例では“/index.html” をデフォルトのページとし“/waitingroom.html” をウェイティングルームとして利用します。
CloudFront Functions の作成
以下のコードを利用しCloudFront Functions を作成します。
/*
* A flag indicating whether the origin is ready to accept traffic.
* Unlike Lambda@Edge, CloudFront Functions doesn't support network call.
* So if you want to change this value, you need to modify then re-deploy
* this function.
*/
var originAcceptingTraffic = true;
/*
* The origin hit rate (a value between 0 and 1) specifies a percentage of
* users that go directly to the origin, while the rest go to
* a "waiting room." Premium users always go to the origin. if you want to
* change this value, you need to modify then re-deploy this function.
*/
var originHitRate = 0.3;
/*
* Waiting Room Redirect URL
*/
var FullClose = `https://FullCLOSE SITE` // Change the redirect URL to your choice
function handler(event) {
var request = event.request;
var uri = event.request.uri;
var cookies = event.request.cookies;
var premiumUserCookieValue = 'some-secret-cookie-value';
if(!originAcceptingTraffic) {
console.log("Origin is not accepting any traffic. " +
"All requests go to the Full close waiting room.");
var response = {
statusCode: 302,
statusDescription: 'Found',
headers:
{ "location": { "value": FullClose } }
}
return response;
}
// Check Whether Cookie is available or not.
// in this sample it checks premium-user-cookie. This name is case
// sensitive, so if you use upper charactor, please modify name parameter.
if(cookies.hasOwnProperty("premium-user-cookie") && cookies["premium-user-cookie"].value === premiumUserCookieValue){
console.log(`Verified Premium user cookie, this request goes to Origin cause it has Cookie with a valid secret value of "${premiumUserCookieValue}".`);
return request;
}
// Lotterly to check go to origin
if (Math.random() >= originHitRate) {
console.log("An unlucky user goes to the waiting room.");
request.uri = '/waitingroom.html';
return request;
}
console.log("A lucky user goes to the origin.");
return request;
};
動作をカスタマイズしたい場合は以下の値をテキストエディタなどで編集を行います。
- originAcceptingTraffic : デフォルト値はTrueに設定されています。もし、オリジンサーバーが過負荷などでリクエストを受け入れ不能の場合はFalseに設定します。
- originHitRate : オリジンに転送されるリクエストの割合を0から1の値で設定します。この例では0.3なので30%のトラフィックがWaiting Roomのページに誘導されます。
- FullClose : 予約の受け入れ前や過負荷による受け入れ停止などの理由でoriginAcceptingTrafficの値をFalseに設定した場合において、誘導するページのURLを記載します。
CloudFront Functionを作成するための手順は以下のとおりです。
- CloudFront consoleから create CloudFront Function を選択し、上記のコードをコピーし貼り付けます。
- Stageに対してデプロイを行い、Save changes ボタンをクリックし保存します。
CloudFront Functions のテスト
CloudFront Functions のデプロイが完了したら、本番環境にデプロイする前にテストコンソールを利用することで動作確認を行うことができます。
テストケース1: premium cookie の値ありのユースケース
- CloudFront Functions Test タブの表示
- CloudFront Functions のページで Test タブを開きます。
- イベントタイプの選択
- 上述の通りCloudFront Functions は Viewer のリクエスト及びレスポンスでのみ動作します。ここでは Viewer Request の Event Type を選択します。
- ステージの選択
- 本番環境にデプロイ前なので Development の Stage を選択します。
- リクエストパラメータの設定(以下の値を選択)
- HTTP Method : GET
- URL Path : デフォルトページを選択 (/index.htmlなど)
- IP address : 選択なし
- Cookie 値の設定(以下の値を選択)
- Name : premium-user-cookie
- Value : some-secret-cookie-value
- Attributes : 空白
- テストの実行
- “Test function” をクリックすると、以下のような uri と Output の値が表示されます。
Test case 2: premium cookie の値なしのユースケース
上記からCookieの値を除外し、premium cookie の値なしのユースケースをテストします。この場合は約30%の確率で uri の値が /waitingroom.html になることが確認できます(この値は originHitRate の値を変更することで0-100%の値で変更することが可能です)。
CloudFront のDistributionへの適用
上記のテストなどで設定値の確認が完了したら、CloudFront への適用を行います。
Step 1: CloudFront Functions のステージの変更
- Functions のページから Publish タブを選択します。
- Publish function ボタンを押下し、コードをデプロイします。
Functionをパブリッシュし動作の確認が完了したら、CloudFront Functions をディストリビューションへの関連付けをおこないます。
Step 2: Functionの関連付け
- Add association ボタンを押下します。
- Distribution : 関連付けを行いたい CloudFront のディストリビューションIDを選びます。
- Event type : Viewer Request を選択します
- Cache behavior : Default(*) もしくは適切なビヘイビアを選択します。
- Add association ボタンを押下します。
- 以上が完了したらCloudFrontへのデプロイの完了を待ちます。
デプロイが完了したらCloudFront Function が適用されるので、利用が可能となります。
CloudFormation Template.
CloudFront の設定をお持ちでない場合などでは、以下の CloudFormation テンプレートを利用することで確認できます。このテンプレートでは以下の設定を行います。
- Origin Access Identity を有効化したS3バケット作成
- 上記の S3 バケットを利用したCloudFront Distribution の作成
- CloudFront Functions の作成
以下の CloudFormation をローカルに保存して、実行してください。
AWSTemplateFormatVersion: "2010-09-09"
Description:
Creating CloudFront with OAI enabled S3 Origin
Metadata:
"AWS::CloudFormation::Interface":
ParameterGroups:
- Label:
default: "S3 and CloudFront Configuration"
Parameters:
- BucketName
- Label:
default: "CloudFront Functions Name"
Parameters:
- CFFunctionName
ParameterLabels:
BucketName:
default: "BucketName"
CFFunctionName:
default: "CFFunctionName"
Parameters:
BucketName:
Type: String
CFFunctionName:
Type: String
Resources:
Bucket:
Type: "AWS::S3::Bucket"
Properties:
BucketName: !Ref BucketName
CloudFrontOriginAccessIdentity:
Type: "AWS::CloudFront::CloudFrontOriginAccessIdentity"
Properties:
CloudFrontOriginAccessIdentityConfig:
Comment: !Sub "access-identity-${Bucket}"
BucketPolicy:
Type: "AWS::S3::BucketPolicy"
Properties:
Bucket: !Ref Bucket
PolicyDocument:
Statement:
- Action: "s3:GetObject"
Effect: Allow
Resource: !Sub "arn:aws:s3:::${Bucket}/*"
Principal:
CanonicalUser: !GetAtt CloudFrontOriginAccessIdentity.S3CanonicalUserId
WaitingRoomFunction2020:
Type: AWS::CloudFront::Function
Properties:
Name: !Ref CFFunctionName
AutoPublish: true
FunctionCode: |
/*
* A flag indicating whether the origin is ready to accept traffic.
* Unlike Lambda@Edge, CloudFront Functions doesn't support network call.
* So if you want to change this value, you need to modify then re-deploy
* this function.
*/
var originAcceptingTraffic = true;
/*
* The origin hit rate (a value between 0 and 1) specifies a percentage of
* users that go directly to the origin, while the rest go to
* a "waiting room." Premium users always go to the origin. if you want to
* change this value, you need to modify then re-deploy this function.
*/
var originHitRate = 0.3;
/*
* Waiting Room Redirect URL
*/
var FullClose = `https://FullCLOSE SITE` // Change the redirect URL to your choice
function handler(event) {
var request = event.request;
var uri = event.request.uri;
var cookies = event.request.cookies;
var premiumUserCookieValue = 'some-secret-cookie-value';
if(!originAcceptingTraffic) {
console.log("Origin is not accepting any traffic. " +
"All requests go to the Full close waiting room.");
var response = {
statusCode: 302,
statusDescription: 'Found',
headers:
{ "location": { "value": FullClose } }
}
return response;
}
if(cookies.hasOwnProperty("premium-user-cookie") && cookies["premium-user-cookie"].value === premiumUserCookieValue){
console.log(`Verified Premium user cookie, this request goes to Origin cause it has Cookie with a valid secret value of "${premiumUserCookieValue}".`);
return request;
}
// Lotterly to check go to origin
if (Math.random() >= originHitRate) {
console.log("An unlucky user goes to the waiting room.");
request.uri = '/waitingroom.html';
return request;
}
console.log("A lucky user goes to the origin.");
return request;
};
FunctionConfig:
Comment: waitingroom-functions
Runtime: cloudfront-js-1.0
CloudFrontDistribution:
Type: "AWS::CloudFront::Distribution"
Properties:
DistributionConfig:
PriceClass: PriceClass_All
Origins:
- DomainName: !GetAtt Bucket.RegionalDomainName
Id: !Sub "S3origin-${BucketName}"
S3OriginConfig:
OriginAccessIdentity: !Sub "origin-access-identity/cloudfront/${CloudFrontOriginAccessIdentity}"
DefaultRootObject: index.html
DefaultCacheBehavior:
TargetOriginId: !Sub "S3origin-${BucketName}"
ViewerProtocolPolicy: redirect-to-https
AllowedMethods:
- GET
- HEAD
CachedMethods:
- GET
- HEAD
DefaultTTL: 600
MaxTTL: 600
MinTTL: 600
Compress: true
ForwardedValues:
Cookies:
Forward: none
QueryString: false
FunctionAssociations:
- EventType: viewer-request
FunctionARN:
Fn::GetAtt:
- WaitingRoomFunction2020
- FunctionARN
HttpVersion: http2
Enabled: true
Outputs:
BucketName:
Value: !Ref Bucket
DomainName:
Value: !GetAtt CloudFrontDistribution.DomainName
DistributionID:
Value: !Ref CloudFrontDistribution
テストの実行
デプロイが完了したら、cURLなどのツールを利用することで確認することができます。
$ curl -i https://XXXXXXXXXX.cloudfront.net
HTTP/2 200
content-type: text/html
content-length: 14
last-modified: Mon, 02 Aug 2021 04:10:48 GMT
accept-ranges: bytes
server: AmazonS3
date: Mon, 02 Aug 2021 05:03:53 GMT
etag: "25e8f2fd2871c8423bbe4e254066cd98"
x-cache: Hit from cloudfront
via: 1.1 4004d5f75919e4406a8e631c774796f5.cloudfront.net (CloudFront)
x-amz-cf-pop: NRT57-C4
x-amz-cf-id: Rkh-XCJx409E1r7kvjJ7ka_WKwKIzWAV1LKn9D1-WasSXR9rYJejbQ==
Please wait...(Waiting Room)
Conclusion
CloudFront Functions はチケットの予約など定常時よりも多くのリクエストが瞬間的に発生するユースケースにおいて流量制御を行いたい場合には非常に有効なソリューションを提供することができます。ここではトラフィックの誘導を行う例を説明していますが、他のユースケースにおいても同様に CloudFront でCloudFront Functions または Lambda@Edge を使うことで様々なコンピューティングを行うことができます。ぜひ、ここでご紹介したような流量制御以外のユースケースでも利用していただき、CloudFront Functions 、Lambda@Edgeの利用をご検討ください。
このブログの著者
中谷 喜久 (Yoshihisa Nakatani)
Solutions Architect
藤原 吉規 (Yoshinori Fujiwara)
Solutions Architect
森 啓 (Akira Mori)
Solutions Architect