2021年7月23日金曜日

Lambda@Edgeで使ってブラウザによって見せるページを変えたときのメモ

やったこと

このAWSチュートリアルこの例を組合せてLambda@Edgeをやってみました。

  • ヘッダ情報から、ブラウザがデスクトップ、iOS、Androidを判定して、別のhtmlファイルにアクセスする
  • 構成は、Browser -> Lambda@Edge -> CloudFront -> S3 (static file)
  • 手順はざっくり下記のようになるが、上記リンク先のままでできる
    • S3にdesktop, ios, android用のhtmlファイルを入れる
    • Cloudfrontでパブリック静的サイトにする
    • LambdaEdge対応にする
  • TTLの設定が良く解らず、CloudFrontが更新されるまでのタイミングがうまく見極められなかったので時間が無駄になってしまった感あり

CloudFront リクエストとレスポンスを変更するタイミング

以下のようにタイミングを選べますが、今回はオリジンリクエストで、OriginのS3にデータを取りにいくときに、バケットオブジェクトを変えています。最初間違えて(ビューワーリクエストにしてしまっていた)
Lambda@Edge では、Node.js および Python の Lambda 関数を実行して CloudFront が発信するコンテンツをカスタマイズし、ビューワーに近い AWS 地域でこの関数を実行できます。この関数は、プロビジョニングや管理の必要なく、CloudFront イベントに応答を実行します。Lambda 関数を使用して、次の時点で CloudFront リクエストとレスポンスを変更できます。

CloudFront がビューワーからリクエストを受信した後 (ビューワーリクエスト)

CloudFront がリクエストをオリジンサーバーに転送する前 (オリジンリクエスト)

CloudFront がオリジンからレスポンスを受信した後 (オリジンレスポンス)

CloudFront がビューワーにレスポンスを転送する前 (ビューワーレスポンス) 
https://docs.aws.amazon.com/ja_jp/lambda/latest/dg/lambda-edge.html 

Error

エラーその1



Correct the errors below and try again. Your function's execution role must be assumable by the edgelambda.amazonaws.com service principal.

これはtrusted policy に追加するはずの、edgelambda.amazon.comが入っていなかったのが問題だった。

エラーその2

Lambda@edgeではCWのservice linked roleがいるらしい


Log group does not exist The specific log group: /aws/lambda/ does not exist in this account or region

というエラーは結局答えは、


ここ とかにあって、

AWSServiceRoleForCloudFrontLogger の中にあるAWSCloudFrontLoggerと同じポリシーをinlineで書き込むと解決。
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": [
                "logs:CreateLogGroup",
                "logs:CreateLogStream",
                "logs:PutLogEvents"
            ],
            "Resource": "arn:aws:logs:*:*:log-group:/aws/cloudfront/*"
        }
    ]
}

Lambda@EdgeのCWログ

Lambda@Edgeはue-east-1にしか置けないが、ログは実際にLambdaが動作するリージョンにある。 パーミションのエラーと合わせてけっこう嵌っってしまいました。こちらに答え。
https://dev.classmethod.jp/articles/where-is-the-lambda-edge-log/

CloudFrontのログ

CloudFrontのコンソールからEnable。S3バケットを設定する。 gzファイルになっているのでダウンロードして見ていた。面倒なのでAthenaとか使えた方がよいのかも。
LambdaExecutionError が出ているのが解った。動作として間違っていなさそうでも、Errorが出ている。Lambda自体に問題もなさそうだが。
解凍は $ gunzip XXX.gz
項目はこのような感じ。
#Version: 1.0
#Fields: date time x-edge-location sc-bytes c-ip cs-method cs(Host) cs-uri-stem sc-status cs(Referer) cs(User-Agent) cs-uri-query cs(Cookie) x-edge-result-type x-edge-request-id x-host-header cs-protocol cs-bytes time-taken x-forwarded-for ssl-protocol ssl-cipher x-edge-response-result-type cs-protocol-version fle-status fle-encrypted-fields c-port time-to-first-byte x-edge-detailed-result-type sc-content-type sc-content-len sc-range-start sc-range-end

Lambda node.jsでCloudFrontのHTTP headerを出力

User-Agent headerの中を読んでいるらしいが、それが実際どうなっているのかがわからない
https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/using-cloudfront-headers.html

この書き方が便利だった https://stackoverflow.com/questions/13147693/how-to-extract-request-http-headers-from-a-request-using-nodejs-connect
# レスポンス
    console.log("response:");
    console.log(JSON.stringify(response));

# ヘッダー
    console.log("request.headers");
    console.log(JSON.stringify(request.headers));

# NG: こうやると空白が出力される
console.log(res_headers);

# NG: こうやると objectであることが出力されるだけ
    console.log(`headers: "${res_headers}"`);
		headers: "[object Object]"

CloudFrontからのHTTP heder requestを無理やり確認するとこうなる。NodeJSのconsole.logでCW logで見ている。
exports.handler = (event, context, callback) => {

  const request = event.Records[0].cf.request;
	console.log(JSON.stringify(request));

  callback(null, request);
};

こういうのが出てくる。例えばCloudFront-Is-Mobile-Viewerでモバイルかどうかの判定ができる。

2021-07-11T03:25:31.555Z	51136a8b-d242-4354-b640-5643d8a7eee1	INFO

{
    "clientIp": "2001.xxxx...",
    "headers": {
        "cloudfront-is-mobile-viewer": [
            {
                "key": "CloudFront-Is-Mobile-Viewer",
                "value": "false"
            }
        ],
        "cloudfront-is-tablet-viewer": [
            {
                "key": "CloudFront-Is-Tablet-Viewer",
                "value": "false"
            }
        ],
        "cloudfront-is-smarttv-viewer": [
            {
                "key": "CloudFront-Is-SmartTV-Viewer",
                "value": "false"
            }
        ],
        "cloudfront-is-desktop-viewer": [
            {
                "key": "CloudFront-Is-Desktop-Viewer",
                "value": "true"
            }
        ],
        "cloudfront-is-ios-viewer": [
            {
                "key": "CloudFront-Is-IOS-Viewer",
                "value": "false"
            }
        ],
        "cloudfront-is-android-viewer": [
            {
                "key": "CloudFront-Is-Android-Viewer",
                "value": "false"
            }
        ],
        "x-forwarded-for": [
            {
                "key": "X-Forwarded-For",
                "value": "2001:xxx...."
            }
        ],
        "user-agent": [
            {
                "key": "User-Agent",
                "value": "Amazon CloudFront"
            }
        ],
        "via": [
            {
                "key": "Via",
                "value": "2.0 aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa.cloudfront.net (CloudFront)"
            }
        ],
        "accept-encoding": [
            {
                "key": "Accept-Encoding",
                "value": "gzip"
            }
        ],
        "pragma": [
            {
                "key": "Pragma",
                "value": "no-cache"
            }
        ],
        "sec-ch-ua": [
            {
                "key": "sec-ch-ua",
                "value": "\" Not A;Brand\";v=\"99\", \"Chromium\";v=\"90\", \"Google Chrome\";v=\"90\""
            }
        ],
        "sec-ch-ua-mobile": [
            {
                "key": "sec-ch-ua-mobile",
                "value": "?0"
            }
        ],
        "sec-fetch-site": [
            {
                "key": "sec-fetch-site",
                "value": "same-origin"
            }
        ],
        "sec-fetch-mode": [
            {
                "key": "sec-fetch-mode",
                "value": "no-cors"
            }
        ],
        "sec-fetch-dest": [
            {
                "key": "sec-fetch-dest",
                "value": "image"
            }
        ],
        "host": [
            {
                "key": "Host",
                "value": "name-of-lambda-edge.s3.region.amazonaws.com"
            }
        ],
        "cache-control": [
            {
                "key": "Cache-Control",
                "value": "no-cache"
            }
        ]
    },
    "method": "GET",
    "origin": {
        "s3": {
            "authMethod": "none",
            "customHeaders": {},
            "domainName": "name-of-lambda-edge.s3.region.amazonaws.com",
            "path": ""
        }
    },
    "querystring": "",
    "uri": "/favicon.ico"
}

0 件のコメント:

コメントを投稿