Lambda Node.js8.10から10.xへの バージョンアップに伴うImageMagickの対応

# はじめに
今回、LambdaランタイムNode.js8.10のサポート終了に伴い、Node.js10.xへアップデートを行いました。
結構詰まった部分などが合ったので、その備忘録として残します。

## LambdaランタイムNode.js8.10のサポート終了について

以下、AWS公式より
<img width="888" alt="スクリーンショット 2020-01-24 14.17.37.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/73081/0cd844c7-b329-257d-2a53-8d5389549543.png">
https://docs.aws.amazon.com/ja_jp/lambda/latest/dg/runtime-support-policy.html

2020/2/3を持ってLambdaのランタイムNode.js8.10の更新が終了します。
これに伴い、Node.js8.10で実装しているLambdaのランタイムを10.xにバージョンアップをしました。

# Lambdaのランタイム変更による変更内容
大きな変更は、コンテナ内で起動するEC2のOSがAmazon Linuxから**Amazon Linux2**へ変わることです。
これにより、もともとデフォルトでOSにインストールされていた、**graphicsmagick** と **imagemagick**がAmazon Linux2ではインストールされていないという状態になります。

こちらが参考記事です
https://note.com/mohya/n/n48692d8e4a57#jgl1s
つまり、Node.js10以上で、imagemagickなどを使いたい場合は、インストールしなければけません。

本記事では、この点についての対応方法を紹介します。

# 手順

1. 単純にランタイムを変更する
2. Lambdaを実行させ、エラー内容を確認する
3. graphicsmagickとimagemagickのレイヤーを追加する
4. Lambdaを実行させ、成功を確認する

# 1. 単純にランタイムを変更する
まず、状況確認をするために単純にランタイムを変更します。
<img width="1296" alt="スクリーンショット 2020-01-24 15.36.06.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/73081/49d584af-32e9-71d3-88e5-e9afd843ba74.png">

# 2. Lambdaを実行させ、エラー内容を確認する

以下のようなエラーが出ました。

```
2020-01-24T02:40:09.509Z	c28c7e8b-b775-48ae-ac36-aec733c9b025	ERROR	Invoke Error	
{
    "errorType": "Error",
    "errorMessage": "Could not execute GraphicsMagick/ImageMagick: identify \"-ping\" \"-format\" \"%wx%h\" \"-\" this most likely means the gm/convert binaries can't be found",
    "stack": [
        "Error: Could not execute GraphicsMagick/ImageMagick: identify \"-ping\" \"-format\" \"%wx%h\" \"-\" this most likely means the gm/convert binaries can't be found",
        "    at ChildProcess.<anonymous> (/opt/nodejs/node_modules/gm/lib/command.js:232:12)",
        "    at ChildProcess.emit (events.js:198:13)",
        "    at ChildProcess.EventEmitter.emit (domain.js:448:20)",
        "    at Process.ChildProcess._handle.onexit (internal/child_process.js:246:12)",
        "    at onErrorNT (internal/child_process.js:415:16)",
        "    at process._tickCallback (internal/process/next_tick.js:63:19)"
    ]
}
```

ImageMagickが見つからず、実行できないと言われてしまっています。

# 3. graphicsmagickとimagemagickのレイヤーを追加する
ここがメインです。
Amazon Linux2ではImageMagickが存在しないので、インストールする必要があります。
こちらのissueが大変参考になりました。
https://github.com/ysugimoto/aws-lambda-image/issues/191

## Imagemagickレイヤーの設置
サーバーレスレポジトリにて提供されているので、こちらから作成します。
https://serverlessrepo.aws.amazon.com/applications/arn:aws:serverlessrepo:us-east-1:145266761615:applications~image-magick-lambda-layer

## Gaphicsmagickレイヤーの設置
こちらは、誰か知らない人が作ってくれているので、使っちゃいます。
https://github.com/rpidanny/gm-lambda-layer
こちらは、Dockerになっていて、あれ？？？と思いましたが
LayerもそれぞれDockerを起動してくれ、起動時にDockerfileに記載してあるこの辺りでインストールしてくれます。

```
RUN curl https://versaweb.dl.sourceforge.net/project/graphicsmagick/graphicsmagick/${GM_VERSION}/GraphicsMagick-${GM_VERSION}.tar.xz | tar -xJ && \
cd GraphicsMagick-${GM_VERSION} && \
./configure --prefix=/opt --enable-shared=no --enable-static=yes --with-gs-font-dir=/opt/share/fonts/default/Type1 && \
make && \
make install
```

このレイヤーをLambdaに登録しておけば、いいらしいですね。便利です。

## レイヤーの適応

今回はServerless Frameworkを使用しているため、ymlで記載します。

```yml
functions:
  storePhotoData:
    handler: storePhotoData.handler
    layers:
      - arn:aws:lambda:ap-northeast-1:579663348364:layer:image-magick:1
      - arn:aws:lambda:ap-northeast-1:175033217214:layer:graphicsmagick:2
      - { Ref: MyLayerLambdaLayer }
    tracing: Active
    description: create storePhotoData
    memorySize: 2048
    timeout: 10
    role: storePhotoDataLambdaFunctionRole
```

レイヤーには実行順序という概念もあるため、レイヤーの順番も気をつけなければなりません。
**MyLayerLambdaLayer** はカスタマイズして作ったレイヤーで、graphicsmagickとimagemagickがないと実行できないので、念の為後ろに持ってきました。
(あんまり意味ないかも...)

# 4. Lambdaを実行させ、成功を確認する
ログが出せないのが申し訳ないですが、これで成功しました！！


# Node.js 12.xの対応
この時どうせならと思ってランタイムをNode.js12.xにしてみたのですが、一応動きました。w
少しだけ気になる点もありましたが、恐らく大丈夫かなと思います。
一気に12へあげたい方はやってみてください。

# 追記
先日、AWSの中の人のサーバーレスのLTを聴講しました。
そこで言われたのは、`LambdaはNode.jsでわざわざやるメリットがあまりない` とのことでした。(あくまで個人的な意見として)
そもそも今回のようにサポート終了になった場合に、ランタイム変更をしなければならないし、Node.js自体がサポートの動きが激しいので、それに伴って対応しなければいけないことが多いようです。
また、Node.jsの特性上、Lambdaのようにファンクション一つだけでの処理の場合、非同期により得られるメリットもあまりないよね、みたいなことをおっしゃっていました。
(確かに、現在使っているLambdaの場合waterfallを用いて無理やり同期処理させています)

弊社アプリケーションの場合、Lambdα＠Edgeを検討していた背景からNode.jsを採択しましたが、今となってはEdgeも使わなそうなので、PythonやGoに切り替えるという選択肢もあるかなと思います。

今後、言語別の速度比較なども含めて検証したいと思います。