lambda@edgeでの画像リサイズ。ハマりどころとテストまとめ

2019年2月18日

どうもくろにゃんこたんです。
lambda@edgeを触る機会があり、手元のWindows 10 Homeで嫌という程ハマったので公式ブログの手順に沿ってまとめておきます。

Lambda@edgeで画像をリサイズする

Amazon CloudFront & Lambda@Edge で画像をリサイズする | Amazon Web Services ブログ

もとの記事はこちらです。

初めて触るAWSサービスってワクワクしますが、何も分からなくて軽く絶望します。(分かる人いますかね)
知らないオプションばかりなので、恐る恐る触ってみるのですが、聞いたほうが早いなと思ってAWS Loftで一通り聞いてきました。

結論としては
「とりあえず公式のブログの通りにやろう」
ということになりました。
Cloud Formation 自体も初めてだったので、そこもいい経験になりました。

Lambda@edgeとは

公式で解説されているので不要かと思いますが、
要はCloudFrontの四隅にLambdaを実行できる「CloudFrontの追加オプション」という位置づけになります。
CloudFrontに噛ませる前に「/images/sizeA/sizeB/TypeJPG」みたいなWordpress風のパスをLambdaでパースして、
「/images/?size=A&size2=B&type=JPG」みたいなことが出来ます。
ちなみにこれは「ViewerRequest」での処理。
.htaccessのパス変換みたいなものが出来るというイメージでも良いかもですね。(ひとつの方法として)

その他、S3などから返ってきたデータをいじった上でCDNにキャッシュするということも出来ます。
なので今回の画像リサイズは格好の標的ですね。

lambda@edgeの制限

Lambda 関数の要件と制限

こちらに制限が書いてあります。
大きなところでは

  • バージニアリージョンでしか動かない
  • node.js6.0かnode.js8.0(最近追加)で書かれたLambda関数でしか動かない

というところでしょうか。
他にもメモリ制限など色々とありますが、そんな重たい処理をCDNの間に挟んだら恩恵があまりないのでスルーで大丈夫です。

Windows10Homeマシンの限界

僕の限界だったと思いますが、結論からだとWindows10HomeではDockerでコケました。
Dockerが動くところまでは行けるのですが、その上のDockerでうまく動かないという謎。
一日試行錯誤してもダメだったので、やめました。

結局EC2でAmazonLinux2のイメージでDockerを用意して実行させています。

させたかったこと

node.jsでnpm installで必要なライブラリを持ってきたかった。
ただそれだけのことです。
なので別にWindowsでもDockerをわざわざ用意する必要がなければ出来ます。
今回は公式ブログの通りにやりたかったので、Windowsでは限界と断定しました。

コケたところその2

いきなりDockerファイルに記述漏れがありました。

FROM amazonlinux

WORKDIR /tmp
#install the dependencies
RUN yum -y install gcc-c++ && yum -y install findutils

RUN touch ~/.bashrc && chmod +x ~/.bashrc

RUN curl -o- https://raw.githubusercontent.com/creationix/nvm/v0.33.5/install.sh | bash

RUN source ~/.bashrc && nvm install 6.10

WORKDIR /build

と書いてありますが、tarとgzが無いのでコケます。

なのでこうしましょう。

FROM amazonlinux

WORKDIR /tmp
#install the dependencies
RUN yum -y install gcc-c++ && yum -y install findutils

RUN yum -y install tar && yum -y install gzip

RUN touch ~/.bashrc && chmod +x ~/.bashrc

RUN curl -o- https://raw.githubusercontent.com/creationix/nvm/v0.33.5/install.sh | bash

RUN source ~/.bashrc && nvm install 6.10

WORKDIR /build

Cloud Formation

こちらは基本的にそのとおりで動きました!
YAMLがこうなります。

このままこれを実行します。

いくつか権限が追加されるのでそのままGO

CloudFrontも作るので時間がかかります。

無事に出来たらCOMPLETEと出ますので、これで動作確認を行ないます。

Lambda関数

こちらもスニペットままで動く。。はず。

ただ僕の場合やり方が悪かったのか、
Origin-Responseの方で
「${AWS::AccountId}」の記述があるのですが、置換されなかった。(多分環境変数をEC2に入れてなかったんだと)のでビルドする時にちゃんと置換されたか確認しましょう。

また、ブログに書かれていない(基本中の基本なのか)のですが、各スニペットは「index.js」というファイル名で保存します。

ですのでディレクトリ構造はこうなります。

  — dist/

  — lambda/viewer-request-function
   ーindex.js

  — lambda/origin-response-function
   ーindex.js

  — Dockerfile

当たり前なのかもしれませんが、僕は迷いました。
(これくらい書いてくれてもいいじゃんね(´ω`) )

Lambda@edgeのテスト

Lambda@edgeはCloudFrontありきなので、テストがとてもやりづらいです。
単体で完結しないのでこのあたり、サンプルコードからカスタマイズしようとするとハマります。
CloudFrontのログを見てもエラー行すら出ないので、心の目を使ってデバッグすることになります。

サンプルでテストを作ってみましたので、こちらを環境に置き換えて使ってみてください。
これでConsole.logなどでがんばりましょう。

ViewerRequest用テストJSON

{
  "Records": [
    {
      "cf": {
        "config": {
          "distributionId": "EXAMPLE"
        },
        "request": {
          "uri": "/images/sample.jpg",
          "querystring": "d=100x100",
          "method": "GET",
          "clientIp": "2001:xxxx::xxxx:xxxx",
          "headers": {
            "host": [
              {
                "key": "Host",
                "value": "d123.cf.net"
              }
            ],
            "user-agent": [
              {
                "key": "User-Agent",
                "value": "Test Agent"
              }
            ],
            "user-name": [
              {
                "key": "User-Name",
                "value": "aws-cloudfront"
              }
            ]
          }
        }
      }
    }
  ]
}

origin response用テストJSON

{
  "Records": [
    {
      "cf": {
        "config": {
          "distributionId": "EXAMPLE"
        },
        "headers": {
          "host": [
            {
              "key": "Host",
              "value": "d123.cf.net"
            }
          ],
          "user-agent": [
            {
              "key": "User-Agent",
              "value": "Test Agent"
            }
          ],
          "user-name": [
            {
              "key": "User-Name",
              "value": "aws-cloudfront"
            }
          ]
        },
        "response": {
          "status": "404",
          "statusDescription": "OK",
          "headers": {
            "x-cache": [
              {
                "key": "X-Cache",
                "value": "Hello from Cloudfront"
              }
            ]
          }
        },
        "request": {
          "uri": "/images/100x100/jpg/sample.jpg",
          "querystring": "d=100x100",
          "method": "GET",
          "clientIp": "2001:xxxx::xxxx:xxxx"
        }
      }
    }
  ]
}

コンソールのLambdaテストケースでこちらを入れてみて成功なら大丈夫です。

まとめ

自分向けに備忘録として Lambda@edge のテストなどについてまとめてみました。
Lambda@edgeはCloudFrontの機能ではあるので、CloudFrontがある程度わかっていないと使えない、中級者向けな機能ではあるなぁと思います。

使い始めると結構使い所は見いだせるので有用ではありますね(´ω`)

バージニアに限る。という制限はなんかもとのIXがバージニアにあるよーって言っているようなものですが、人気が出たら東京でも使えるようになるんですかねぇ。
そもそもここのレイテンシーが気になるようなものを作るのであれば、この構成自体は選択肢に入れるのはどうかなとは思いますが・・

ここまで読んで頂きありがとうございます!