AWSの Lambda ファンクションを、CloudFrontのエッジサーバにデプロイして、リクエストやレスポンスをインターセプトして処理を行うことができます。Lambda@Edge と呼ばれます。
Webアプリケーションフレームワークにある「ミドルウェア」のような使用感で使えます。
例えば、S3 をオリジンに使っている時(サーバレス環境などで)、少しだけ動的に認証やリダイレクトなどのHTTPヘッダーを扱う処理をしたい時など、便利です。
ポイントとして、us-east-1 (バージニア北部) リージョンの Lambdaファンクションのみ選択できます。
1. 概要
1-1. フックポイント(トリガー)
リクエスト:
クライアント → [Viewer Request]→ CloudFront → [Origin Request] → Origin
レスポンス:
クライアント ← [Viewer Response] ← CloudFront ← [Origin Response] ← Origin
Viewer Request, Origin Request, Origin Response, Viewer Response の4箇所で処理を差し込めます。
今回は、例として Origin Response にファンクションを差し込んでみます。
1-2. Lambdaファンクションのパターン
公式ドキュメントにサンプル集があります(便利)。
今回は、Origin Response で処理をさせるため、引数にはレスポンスが入っており、処理としてはレスポンスを返す( callback(null, response) を実行する) 必要があります。
リクエストとレスポンスを取得するファンクションの定番パターンは
'use strict';
exports.handler = (event, context, callback) => {
const request = event.Records[0].cf.request;
const response = event.Records[0].cf.response;
// なにか処理...
callback(null, response);
}
このようになります。responseを加工して返すか、新しいレスポンスデータを作って返すことになるでしょう。
1-3. eventの中身をすべてJson で表示するファンクション
最初は、event の中身をすべて見れるようなファンクションを作ると動作を早く理解できます。このようになります。
'use strict';
exports.handler = (event, context, callback) => {
const response = event.Records[0].cf.response;
response.status = 200;
response.statusDescription = 'Ok';
response.headers['content-type'] = [{
"key": "Content-Type",
"value": "application/json",
}];
response.body = JSON.stringify(event, null, ' ');
callback(null, response);
}
2. 実際に作ってみる
2-1. CloudFront ディストリビューションの作成
2-1-1. CloudFront のページ から、 Create Distribution
2-1-2. Web の Get Started
2-1-3. Lambda Function Associations は、ここでは設定しない。(あとで Lambdaのページから設定する)
オリジンは適当なS3にでもしておいてください。
キャッシュ時間は、すべて0にしてキャッシュさせないようにすると検証が楽です。
Create Distribution で作成。
2-2. Lambda 関数の作成
2-2-1. us-east リージョンの Lambdaページから「関数の作成」
2-2-2. ロールは、「カスタムロールの作成」
2-2-3. ロール作成ページが表示されるので、ロール名をつけて許可
2-2-4. このロールに、EdgeLambdaの「信頼関係」(サービスプリンシパル) が必要なので、一旦 IAM のロールの「信頼関係」タブを見る
2-2-5. 信頼関係の編集 をクリック
2-2-6. Principal をリストにして、"edgelambda.amazonaws.com" を追加して、ポリシーの更新。
"Service": [
"edgelambda.amazonaws.com",
"lambda.amazonaws.com"
]
2-2-7. 信頼されたエンティティに edgelambda.amazonaws.com が追加されている。
2-2-8. Lambda のページに戻ると、「既存のロール」に先程のロールが設定されるので、「関数の作成」をクリック。
2-2-5. 関数作成のウインドウが出るので、関数を書いて、「保存」。
'use strict';
exports.handler = (event, context, callback) => {
const response = event.Records[0].cf.response;
response.status = 200;
response.statusDescription = 'Ok';
response.headers['content-type'] = [{
"key": "Content-Type",
"value": "application/json",
}];
response.body = JSON.stringify(event, null, ' ');
callback(null, response);
}
2-2-6 「テスト」ボタンをクリック
2-2-7. このテストページでは、ファンクションの第一引数を設定してテストできる。
今回のLamda関数は、第一引数 event が何であってもオウム返しするだけなのでどんなデータでもテストできるが、Amaon CloudFront Access Request In Response が実際の Origin Response の event に近いので、これを使ってテストイベントを作る。
2-2-8. 「テスト」をクリックしてテストを実行。
2-3. CloudFront にデプロイ
作ったファンクションを CloudFront にデプロイする。
2-3-1. Lambda の Designer から、CloudFront を選択
2-3-2. 「グローバル展開が保留中」と表示されるので、下にある トリガーの設定で、「Lambda@Edgeへのデプロイ」をクリック
2-3-4. デプロイページで、「オリジンレスポンス」を選択し、確認チェックボックスを入れて「デプロイ」
デプロイされる。
2-4. テストリクエスト
curlかなんかでリクエスト。eventの内容がわかる。
% curl -v 'http://xxxxxxxxxxxxxx.cloudfront.net/'
* Trying ****:****:****:****:****:****:****:****...
* TCP_NODELAY set
* Connected to xxxxxxxxxxxxxx.cloudfront.net (****:****:****:****:****:****:****:****) port 80 (#0)
> GET / HTTP/1.1
> Host: xxxxxxxxxxxxxx.cloudfront.net
> User-Agent: curl/7.54.0
> Accept: */*
>
< HTTP/1.1 200 OK
< Content-Type: application/json
< Content-Length: 2663
< Connection: keep-alive
< x-amz-bucket-region: ap-northeast-1
< Date: Sat, 19 Jan 2019 13:30:09 GMT
< Server: AmazonS3
< X-Cache: Error from cloudfront
< Via: 1.1 xxxxxxxxxxxxxx.cloudfront.net (CloudFront)
< X-Amz-Cf-Id: xxxxxxxxxxxxxx
<
{
"Records": [
{
"cf": {
"config": {
"distributionDomainName": "xxxxxxxxxxxxxx.cloudfront.net",
"distributionId": "xxxxxxxxxxxxxx",
"eventType": "origin-response"
},
"request": {
"clientIp": "****:****:****:****:****:****:****:****",
...
...
この出力は、今回の Lambda 関数の第一引数そのものなので、Lambda のテストデータとしてコピペしておくと今後のテストに便利です。
2-5. 修正後のデプロイ
アクションメニューに「Lambda@Edge へのデプロイ」項目が追加されるのでここからデプロイすると楽です。