AWS

CloudFront FunctionsとLambda@Edgeの比較と実践

tiga

それぞれの概要と違い

CloudFront FunctionsとLambda@Edgeの概要について、以下に要点を表形式でまとめました。

※その他の違いや詳細については、AWSドキュメント「CloudFront Functions と Lambda@Edge の違い」をご参照ください。

項目CloudFront FunctionsLambda@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 FunctionsLambda@Edge
リクエスト料金100 万回あたり 0.1 USD100万件あたり USD 0.60
コンピューティング料金なしあり(128 MB-秒の使用につき 0.00000625125 USD)
無料枠あり(1 か月あたり 200 万件の CloudFront 関数呼び出し)なし

※AWSドキュメント「Amazon CloudFront の料金
※AWSドキュメント「AWS Lambda 料金」

料金の例

1か月に100万回リクエストされ、1か月のコンピューティング時間が合計100万 秒(毎回の実行時間が 100 ミリ秒)であった場合、発生する料金は以下のように計算されます。

項目CloudFront FunctionsLambda@Edge
1 か月のコンピューティング料金なし1000,000 × 0.00000625125 USD
= 6.3 USD
1 か月のリクエスト料金100 万回あたり 0.1 USD100万件あたり 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 html>
2<html lang="ja">
3<h1>Welcome</h1>
4</html>
  • management/index.html
1<!DOCTYPE html>
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への移行が必要になります。

コンピューティング使用率値:

  • 1~50 — 関数は最大許容時間を十分に下回っており、スロットリングなしで実行する必要があります。
  • 51~70 — 関数が最大許容時間に近づいています。関数コードの最適化を検討してください。
  • 71~100 — 関数が最大許容時間に非常に近いか、それを超えています。ディストリビューションに関連付けると、CloudFront でこの関数が抑制される可能性があります。
参考)https://docs.aws.amazon.com/ja_jp/AmazonCloudFront/latest/DeveloperGuide/test-function.html
  • テストを実行した後、「関数を発行」をクリックすると、ディストリビューションに関連付けの設定画面が表示されるので、作成したディストリビューションのキャッシュビヘイビア/を関連付けることができます。
  • クライアント(ユーザー)が 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
  • テストを実行した結果、ステータスが403 ForbiddenとなっていればOKです。この結果により、IPアドレス1.2.3.4からのアクセスが適切に禁止されていることが確認できます。
  • 次に、CloudFront Functionsに登録されているIPアドレスからのアクセスが許可されているかを確認するため、テスト画面にて以下の設定を行い、実行します。
    • URLパス:/management/index.html
    • IPアドレス:あなたの利用しているIPアドレス
  • テストを実行した結果、ステータスが401 UnauthorizedとなっていればOKです!401 Unauthorizedエラーは、IPアドレスからのアクセスが許可された後に、Basic認証においてエラーとなっているだけです。つまり、このテスト結果から、ご利用のIPアドレスからのアクセスが許可されていることが確認できます。
  • テストを実行した後、「関数を発行」をクリックすると、ディストリビューションに関連付けの設定画面が表示されるので、作成したディストリビューションのキャッシュビヘイビア/manager/*を関連付けることができます。
  • 関連付けたあと、アクセスするとBasic認証を求められます。
  • ユーザー名とパスワードを入力し、「Welcome manager」と表示されればOKです!

まとめ

CloudFront FunctionsとLambda@Edgeの比較と実践を行いました!
これまでLambda@Edgeを利用して実装していたものをCloudFront Functionsに移行することも検討してみると良いかもしれません。
CloudFront Functionsの導入を検討されている方々にとって、少しでも参考になれば幸いです!

AUTHOR
tiga
tiga
記事URLをコピーしました