CloudFront FunctionsとLambda@Edgeの比較と実践
それぞれの概要と違い
CloudFront FunctionsとLambda@Edgeの概要について、以下に要点を表形式でまとめました。
※その他の違いや詳細については、AWSドキュメント「CloudFront Functions と Lambda@Edge の違い」をご参照ください。
項目 | CloudFront Functions | Lambda@Edge |
---|---|---|
概要 | 簡単な処理、低レイテンシ要求の処理 | 複雑な処理、大規模なトラフィック、長時間実行の処理 |
プログラミング言語 | JavaScript (ECMAScript 5.1 準拠) | Node.js と Python |
関数の持続時間 | サブミリ秒 | 最大 5 秒 (Viewer request、Viewer response) 最大 30 秒 (Origin request、Origin response) |
スケール量 | 毎秒 1000 万 件 | 1 リージョンあたり毎秒 1万 件まで |
対応するCloudFront イベント | Viewer request Viewer response | Viewer request Viewer response Origin request Origin response |
Viewer Request/ResponseとOrigin Request/Responseについて
- Viewer Request
- クライアント(ユーザー)が CloudFront にリクエストを送信した直後に実行される
- Viewer Response
- CloudFront がクライアントにレスポンスを返す直前に実行される
- Origin Request
- CloudFront がオリジンサーバー(例: S3やEC2)にリクエストを送信する直前に実行される
- Origin Response
- オリジンサーバーから CloudFront にレスポンスが戻ってきた直後に実行される
Viewer Request/Response と Origin Request/Response のトリガーを使い分けることで、CloudFront 関数や Lambda@Edge を適切に活用し、キャッシュの前後でコンテンツを効率的に操作できます。
参考)Introducing CloudFront Functions – Run Your Code at the Edge with Low Latency at Any Scale
CloudFront Functions で利用できない機能が必要な場合でも、Lambda@Edge を使ってキャッシュの前後でコンテンツを操作できます。
参考)Introducing CloudFront Functions – Run Your Code at the Edge with Low Latency at Any Scale
料金比較
項目 | CloudFront Functions | Lambda@Edge |
---|---|---|
リクエスト料金 | 100 万回あたり 0.1 USD | 100万件あたり USD 0.60 |
コンピューティング料金 | なし | あり(128 MB-秒の使用につき 0.00000625125 USD) |
無料枠 | あり(1 か月あたり 200 万件の CloudFront 関数呼び出し) | なし |
※AWSドキュメント「Amazon CloudFront の料金」
※AWSドキュメント「AWS Lambda 料金」
料金の例
1か月に100万回リクエストされ、1か月のコンピューティング時間が合計100万 秒(毎回の実行時間が 100 ミリ秒)であった場合、発生する料金は以下のように計算されます。
項目 | CloudFront Functions | Lambda@Edge |
---|---|---|
1 か月のコンピューティング料金 | なし | 1000,000 × 0.00000625125 USD = 6.3 USD |
1 か月のリクエスト料金 | 100 万回あたり 0.1 USD | 100万件あたり USD 0.60 |
無料枠 | あり(1 か月あたり 200 万件) | なし |
合計 | 0 USD(※無料枠が適用された場合) | 6.9 USD |
CloudFront FunctionでBasic認証+IP制限の実装
私がCloudFront Functionを触ったことがなく、CloudFront Functionでどの程度の処理なら出来るかのイメージが分からないので、とりあえずCloudFront Functionを使ってBasic認証+IP制限の実装をやってみようと思います!
- 全体の流れ
- ステップ1: S3 作成
- ステップ2: CloudFront 作成
- ステップ3: S3 バケットポリシー編集
- ステップ4: (練習)リダイレクト用のCloudFront Functions作成)
- ステップ5: CloudFront FunctionでBasic認証+IP制限を実装
ステップ1: S3作成
- 今回オリジンをS3にするため、S3を作成します。
作成後、以下のフォルダ構成でフォルダ・HTMLファイルを作成し、S3バケットへのアップロードをします。
フォルダ構成はこちらになります。
1/
2└── index.html
3└── management/
4 └── index.html
- /index.html
1<!DOCTYPE >
2<html lang="ja">
3<h1>Welcome</h1>
4</html>
- management/index.html
1<!DOCTYPE >
2<html lang="ja">
3<h1>Welcome manager</h1>
4</html>
ステップ2: CloudFront 作成
- 続いて、CloudFrontを作成します。
- 作成する際にいくつかの項目を設定します。
- Origin domain:ステップ1で作成したS3を選択
- オリジンへのアクセス制御方法:OACを使用
- Create new OACをクリックして、任意の名前で作成可能
- 今回は検証のため、ウェブアプリケーションファイアウォール (WAF)は有効にせず作成
- CloudFront 作成後にAWSマネジメントコンソール上部に以下の通知が来るので、「ポリシーをコピー」をクリックお願いします。ステップ3の「S3バケットポリシー編集」で使用します。
ステップ3: S3バケットポリシー編集
- ステップ2の最後にコピーしたポリシーをステップ1で作成したS3のバケットポリシーに設定します。
例:
1{
2 "Version": "2008-10-17",
3 "Id": "PolicyForCloudFrontPrivateContent",
4 "Statement": [
5 {
6 "Sid": "AllowCloudFrontServicePrincipal",
7 "Effect": "Allow",
8 "Principal": {
9 "Service": "cloudfront.amazonaws.com"
10 },
11 "Action": "s3:GetObject",
12 "Resource": "arn:aws:s3:::{S3リソース名}/*",
13 "Condition": {
14 "StringEquals": {
15 "AWS:SourceArn": "{CloudFrontのArn}"
16 }
17 }
18 }
19 ]
20}
一度アクセスできるか確認
CloudFrontのコンソールにて、ステップ2で作成したCloudFrontのディストリビューションドメイン名をコピーしてアクセスしてみます。
そのドメインに/index.html
を足してアクセスし、「Welcome」と表示されればOKです!
例:https://xxxxxxxxxxx.cloudfront.net/index.html
練習:リダイレクト用のCloudFront Functions作成
ここで一度練習を入れました!必要ない方は飛ばしていただいても大丈夫です!
https://xxxxxxxxxxx.cloudfront.net
にアクセスした際に、https://xxxxxxxxxxx.cloudfront.net/index.html
にリダイレクトされるように設定します。
- CloudFrontのコンソールにて、ビヘイビアを作成します。
- パスパターンを
/
- オリジンに作成したS3を設定
- パスパターンを
- 作成後の設定画面がこちらになります。
- 続きまして、CloudFrontのコンソールのサイドメニューから関数をクリックし、CloudFront Functionsを作成します。
- 続いて実行するコードを記載し保存します。
1function handler(event) {
2 var request = event.request;
3 var uri = request.uri;
4
5 // リクエストパスが `/` の場合に `index.html` を追加
6 if (uri === "/") {
7 request.uri += "index.html";
8 }
9
10 return request;
11}
- 続いて実行するコードをテストをするために、テスト画面にて項目のURLパスを
/
にして関数をテストします。
- このように成功すればOKです!
実行結果に記載されているコンピューティング使用率が50以上であれば、関数コードの最適化するか、Lambda@Edgeへの移行が必要になります。
コンピューティング使用率値:
参考)https://docs.aws.amazon.com/ja_jp/AmazonCloudFront/latest/DeveloperGuide/test-function.html
- 1~50 — 関数は最大許容時間を十分に下回っており、スロットリングなしで実行する必要があります。
- 51~70 — 関数が最大許容時間に近づいています。関数コードの最適化を検討してください。
- 71~100 — 関数が最大許容時間に非常に近いか、それを超えています。ディストリビューションに関連付けると、CloudFront でこの関数が抑制される可能性があります。
- テストを実行した後、「関数を発行」をクリックすると、ディストリビューションに関連付けの設定画面が表示されるので、作成したディストリビューションのキャッシュビヘイビア
/
を関連付けることができます。
- クライアント(ユーザー)が CloudFront にリクエストを送信した直後に実行されるようにしたいので、イベントタイプでViewer Requestを選択しています。
- 関連付けが完了したのち、
https://xxxxxxxxxxx.cloudfront.net
にアクセスし、/index.html
の内容が表示されていればOKです!
ステップ4: CloudFront FunctionでBasic認証+IP制限を実装
それでは、先ほど実施していただいた手順と同じように、CloudFront FunctionでBasic認証+IP制限を実装していきます。https://xxxxxxxxxxx.cloudfront.net/management/index.html
へアクセスする際に、指定したIPアドレスからのアクセスのみを許可し、さらにBasic認証を要求する実装になります。
- CloudFrontのコンソールにて、ビヘイビアを作成します。
- パスパターンを
/management/*
- オリジンに作成したS3を設定
- パスパターンを
- 作成後の設定画面がこちらになります。
- 再度新規のCloudFunctionsを作成し、以下のコードを記載し保存します。
- 以下の三つの部分を変更してコードを使用してください。
- XX.XX.XX.XX :あなたの利用しているIPアドレス
- yourUsername :任意のユーザー名
- yourPassword :任意のパスワード
1function handler(event) {
2 var request = event.request;
3 var headers = request.headers;
4 var clientIP = event.viewer.ip;
5
6 console.log('Client IP:'+clientIP);
7
8 // アクセス許可するIPリスト
9 var IP_WHITE_LIST = {
10 'XX.XX.XX.XX': true, // 要変更
11 };
12
13 // Basic認証のユーザー名とパスワード
14 var credentials = {
15 username: "yourUsername", // 要変更
16 password: "yourPassword", // 要変更
17 };
18
19 // Basic認証のためにユーザー名とパスワードを Base64 でエンコード
20 var encodedAuth = Buffer.from(credentials.username + ':' + credentials.password).toString('base64');
21 var expectedAuthHeader = 'Basic ' + encodedAuth;
22
23 // クライアントIPが許可されているかを確認
24 var isPermittedIp = IP_WHITE_LIST[clientIP] || false;
25 console.log(isPermittedIp);
26
27 if (isPermittedIp) {
28 // Authorizationヘッダーの有無と認証情報をチェック
29 if (headers.authorization && headers.authorization.value === expectedAuthHeader) {
30 console.log("Authorization successful for IP:"+clientIP);
31 return request; // 認証成功
32 } else {
33 console.log("Authorization failed for IP:"+clientIP);
34 // 認証失敗時に401レスポンスを返す
35 return {
36 statusCode: 401,
37 statusDescription: 'Unauthorized',
38 headers: {
39 'www-authenticate': { value: 'Basic realm="Enter credentials"' },
40 },
41 };
42 }
43 } else {
44 // 許可されていないIPの場合403レスポンスを返す
45 console.log("Forbidden access for IP:"+clientIP);
46 return {
47 statusCode: 403,
48 statusDescription: 'Forbidden',
49 };
50 }
51}
- 想定外のIPアドレスからのアクセスが禁止されていることを確認するために、テスト画面にて以下の設定を行い、実行します。
- URLパス:
/management/index.html
- IPアドレス:1.2.3.4
- URLパス:
- テストを実行した結果、ステータスが
403 Forbidden
となっていればOKです。この結果により、IPアドレス1.2.3.4
からのアクセスが適切に禁止されていることが確認できます。
- 次に、CloudFront Functionsに登録されているIPアドレスからのアクセスが許可されているかを確認するため、テスト画面にて以下の設定を行い、実行します。
- URLパス:
/management/index.html
- IPアドレス:あなたの利用しているIPアドレス
- URLパス:
- テストを実行した結果、ステータスが
401 Unauthorized
となっていればOKです!401 Unauthorized
エラーは、IPアドレスからのアクセスが許可された後に、Basic認証においてエラーとなっているだけです。つまり、このテスト結果から、ご利用のIPアドレスからのアクセスが許可されていることが確認できます。
- テストを実行した後、「関数を発行」をクリックすると、ディストリビューションに関連付けの設定画面が表示されるので、作成したディストリビューションのキャッシュビヘイビア
/manager/*
を関連付けることができます。
- 関連付けたあと、アクセスするとBasic認証を求められます。
- ユーザー名とパスワードを入力し、「Welcome manager」と表示されればOKです!
まとめ
CloudFront FunctionsとLambda@Edgeの比較と実践を行いました!
これまでLambda@Edgeを利用して実装していたものをCloudFront Functionsに移行することも検討してみると良いかもしれません。
CloudFront Functionsの導入を検討されている方々にとって、少しでも参考になれば幸いです!