d.sunnyone.org
sunnyone.org

ページ

2018-03-26

AWS CloudFront Lambda@Edge の罠たち

CloudFront Lambda@Edge は、Lambda関数を実行しCloudFrontのリクエスト/レスポンスに手を加えることができるサービス。CloudFront⇔オリジン間、ビューワ⇔CloudFront間のリクエスト側/レスポンス側の4つの場所から適用する場所を選ぶことができる。

適切に使えば便利だと思うが、使ってみないとわからない罠が多いので、経験した範囲をまとめておく。

CloudFrontにはバージョンつきのLambda関数のARNを設定する必要がある

CloudFront BehaviorにLambda関数のARNを設定するのだが、名前だけのARNは設定することができず、バージョン番号入りのものを設定する必要がある。

つまり、Lambda関数の中身を書き換えたら、新しいバージョン番号を発行して、その番号をCloudFrontに設定する必要がある。

毎回手動でなんて現実的ではないので、AWS Serverless Application Model (AWS SAM)を利用してCloudFormationでLambda関数をデプロイし、AutoPublishAlias 機能によってバージョン番号を自動的に設定するのが現実的であろう。同一スタックであれば、CacheBehaviorsに以下のように設定する。
LambdaFunctionAssociations:
  - EventType: origin-response
    LambdaFunctionARN: !Ref "Function.Version"
これが何を意味するかというとLambda関数を更新する度にCloudFront Distributionを更新する、すなわちコードを書き換えるたびに数十分かかるということを意味する。

Lambda@Edgeに設定する関数はus-east-1リージョンに配置する必要がある

Lambdaはap-northeast-1でも利用可能だが、Lambda@Edgeに設定する関数はus-east-1に配置する必要がある。先のCloudFormationスタックを作るのであれば、us-east-1リージョンで作る必要があるということになる。

日本のサービスであればap-northeast-1に配置することも多いと思うが、スタック間でのパラメータの引継ぎに利用できるクロススタック参照は同一リージョンでしか利用できない。したがって、us-east-1→ap-northeast-1のスタック間でリソースの参照がある場合、自前でパラメータ引き渡しをするための仕組みが必要となる。

Lambda@Edgeで設定した関数は各リージョンにレプリカが作られて実行される

us-east-1にLambda関数を設定したからといって、us-east-1で常に実行されるかというとそうではなく、CloudFrontのエッジのリージョンに必要なタイミングでコピーされて実行される。

「オレがCloudFront エッジをサービスしたいのは日本だけ!」ということはできないので、日本を含もうとすると、多くのリージョンにレプリカが作られることになる。

レプリカはLambdaから見ると、us-east-1.というprefixがついた変更不可能な関数にしか見えない。これは、Lambda@Edgeに設定した関数のログは各リージョンのCloudWatch Logsに散らばって配置されるということを意味する。 もしログを集約したりフィルタしたりといったことをしようとすると、ほぼ全リージョンのCloudWatch Logsのリソースに対して手続きを行う必要がある。

Lambda@Edgeでは環境変数は利用できない

Lambdaで環境ごとの値を設定するのに便利な環境変数は、Lambda@Edgeでは利用できない。よって、コードに埋め込むか何らかのAPI問い合わせで値を取得する必要がある。今回 必要な値は別のリソースのARNだったので、レプリカ元(マスター)のLambda関数リソースのタグに設定し、以下の手順で取得した。
  1. context.invokedFunctionArnからレプリカ関数のARNを取得
  2. レプリカ関数のARNからリージョンを取得し、そのリージョンに対してレプリカ関数を問い合わせるlambda:GetFunctionの呼び出しを行いマスター関数のARNを取得
  3. マスター関数のARNからリージョン(us-east-1)を取得し、lambda:GetFunctionを呼び出してタグを取得
別の関数の環境変数だとか、DynamoDBだとか、状況により選択肢はあると思うが、いずれにせよ工夫が必要である。

Lambdaよりもタイムアウトなどの制限が厳しい

通常のLambda関数よりも、制限が厳しくなっている(Lambda@Edge の制限)。

例えば、Lambda単体であればタイムアウトは300秒まで設定できるが、オリジンリクエスト/レスポンスに対する関数だと30秒、ビューワーだと5秒など項目によっては厳しくなっているので注意が必要。アップロードサイズも注意。

以上のように使えそうに見えて使ってみるとどうしよう、みたいなところが多いので皆様お気をつけください。