Lamba@Edgeのハマったポイント紹介



#はじめに

Lambda@Edgeは便利なサービスですよね。
例えば、DynamoDBにも繋げたりするので保存したデータに応じた処理も書けちゃいます。
またS3に配置したhtmlファイルにBasic認証かけたいとか、動的にOGPタグ埋め込みたいとか……etc
S3＋CloudFrontにこれを加えるだけで実現できることが増えます。

プロジェクトでLambda@Edgeに触れる機会があったので、そこでハマったポイントを紹介させていただきます。


##想定読者　
Lambda@Edgeのサービス概要や基本的な開発手順について理解している方

#ハマったポイント

##①開発の取っ掛かりがつかめない
Lambda@Edgeのエントリーポイントは以下の形です。

```
exports.handler = async (event) => {
    ・・・eventに対する操作など・・・
};
```

引数のeventはHTTPのリクエストやレスポンスに相当するオブジェクトによって構成されています。
このeventの値を使ってデプロイ先のCloudFront Eventに対応したresponse,requestを返却することによってLambda@Edgeは動作します。

Lambda@Edgeを触り始めて困るのが、
eventをどう解釈して処理を書けばよいのか取っ掛かりがつかめないことでした。


公式ドキュメントやテストテンプレートを見ればある程度の構造はつかめますが、具体的な値は実際に捕まえてみない事には何とも……
そんなわけで以下のようなコードで、eventをCloudWatchLogsに一度吐き出してそれをテストイベントに登録しての流れで実施していました。

```
exports.handler = async (event) => {
    console.log(event.Records[0].cf.request);
    return event.Records[0].cf.request;
};
```
↑をCloudFrontにデプロイすると、日本からのアクセスであれば（Logの出力場所もまた別のハマりどころがあったので後述します）東京リージョンのCloudWatch Logsにログが出力されるのが確認できるかと思います。
![cloudwatchlogs.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/905754/35e68ca3-8d0b-abc3-b035-95245c73d178.png)

今はだいぶテストテンプレートは拡充されていますが、
始める前にはリアルなeventの値をつかんでおいた方が効率が上がると思います。

##②Origin responseでresponse.bodyが見えない
以下オフィシャルより借用した概念図をご確認ください。
![cloudfront-events-that-trigger-lambda-functions.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/905754/25b03a31-079c-5512-3a00-dfa1ac786e12.png)
Origin responseで処理が挟めそうですね。

であれば、ちょろっと処理を仕込めば簡単に返却されたコンテンツを書き換えられそうです。
CloudFront cacheにも乗るので余計にLamba@Edgeを実行されることもない……と費用的にもハッピーです。
よし、これとDynamoDBを組み合わせてhtmlファイルに動的にOGPタグ仕込んだるぜ！　
念のためどんなbody来るか把握しておこう……

```
exports.handler = async (event) => {
    console.log(event.Records[0]);
    console.log(event.Records[0].cf.response);
    return event.Records[0].cf.response;
};
```

これを仕込んで、CloudWatch Logsを見てーと
![cloudwatchlogs2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/905754/83ab8b2f-e16e-2233-334c-aaff1c68176a.png)


あれ、bodyないの？

大きな落とし穴がありました。
Viewer response,Origin responseでは、レスポンスされたbodyの中身が見れません。

今はAWSコンソール上でinclude bodyオプションが非活性になるので気づきますが。
![origin_response.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/905754/ebabe5f4-62e6-0938-d8c8-cac088f9c023.png)
当時はinlude bodyオプションもチェックできてしまう（けど機能しない）という状態だった覚えがあります。

いざ実装して試験するまで気づきませんでした。
処理中でS3ストレージに配置していたhtmlファイルを再び取得してレスポンスする処理を書くという何とも無駄なソースに……
（いや、これは仕方がないのです。キャッシュに乗せたいし……でもなぁ……）

冷静になって考えてみると、この仕様が妥当だと考えています。
他人が管理されているサイトを勝手に書き換えたコンテンツが発行できるって言っているようなものですし

##③環境変数が使えない
環境変数が使えないため展開先の環境によって切り替えたいというようなケースでは、
DynamoDBやパラメーターストア等を活用する必要があります。
パラメーターストアを使う場合には特定リージョンに固定して参照するようにしてください。

痛い目見ます（見ました）。

Lambda@Edgeは複数のエッジリージョンにレプリケーションされるため、
明示的に場所を指定しておかないとせっかく設定したパラメーターが取れなくなるためです。

以下の感じですね。

```
const AWS = require('aws-sdk')

AWS.config.update({
  region: 'ap-northeast-1'
})
```


##④ログを集約するのが一苦労
我々はログ収集や障害発生時の発砲にDataDogを利用していました。
CloudWatchLogsから吸い上げDataDogに集約する仕組みは構築していましたが、
レプリケーション先が全世界になるためCloudWatchLogsの監視先が複数にわたってしまいます。

とはいえ処理的の性質的（HTTPリクエストレスポンスのフローに挟まるため処理時間の制約がシビアです）
にログを吐くためだけに色々時間が掛かりそうな外部サービスは呼びたくない……
どうせ内部でHTTP Client初期化してとか何かするはずだし……
やむなく主要なエッジリージョンからログをDataDogに吸い上げる仕組みを作りましたが、
もっとスマートな選択肢があった気もします。

#まとめ
プロジェクトでの開発の中で得られたLambda@Edgeのハマりそうなポイントをいくつか紹介させていただきました。
この記事が誰かに役に立ちますように！